#! /usr/bin/perl -w
# Check DiskIO via SNMP.
# Plugin uses UCD-DISKIO MIB (1.3.6.1.4.1.2021.13.15.1).
# Used in net-snmp packages on linux.
# UCD-DISKIO on linux is only running ok starting with net-snmp version 5.2
# Releases below this version could deliver wrong figures.
#
#
# Copyright (C) 2009 by Herbert Stadler
# email: hestadler@gmx.at
# License Information:
# 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 3 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, see .
#
#
############################################################################
use POSIX;
use strict;
use Getopt::Long;
use lib ".";
use lib "/usr/lib/nagios/plugins";
use lib "/usr/lib64/nagios/plugins";
use lib "/usr/local/nagios/libexec";
use utils qw(%ERRORS);
use Net::SNMP qw(oid_lex_sort oid_base_match);
my ($opt_version,$opt_help,$opt_verbose);
my ($opt_timeout,$opt_license,$opt_device);
my ($opt_hostname,$opt_community,$opt_port,$opt_snmpvers);
my ($opt_username,$opt_authpasswd,$opt_authproto);
my ($opt_privpasswd,$opt_privproto);
my ($opt_warn,$opt_crit);
my ($PROGNAME,$REVISION);
my ($state,$msg);
use constant DEFAULT_TIMEOUT =>30;
use constant DEFAULT_PORT =>161;
use constant DEFAULT_COMMUNITY =>"public";
use constant DEFAULT_SNMPVERS =>"2";
use constant DEFAULT_PRIVPROTO =>"DES";
use constant DEFAULT_AUTHPROTO =>"MD5";
use constant WAIT_BETWEEN_MEASUREMENT =>5;
use constant COUNTER_MAXVAL =>4294967295;
# UCD-DISKIO MIB
my $dskIOTable ="1.3.6.1.4.1.2021.13.15.1";
my $dskIOEntry ="1.3.6.1.4.1.2021.13.15.1.1";
my $dskIOIndex ="1.3.6.1.4.1.2021.13.15.1.1.1";
my $dskIODevice ="1.3.6.1.4.1.2021.13.15.1.1.2";
my $dskIONRead ="1.3.6.1.4.1.2021.13.15.1.1.3";
my $dskIONWritten ="1.3.6.1.4.1.2021.13.15.1.1.4";
my $dskIOReads ="1.3.6.1.4.1.2021.13.15.1.1.5";
my $dskIOWrites ="1.3.6.1.4.1.2021.13.15.1.1.6";
$ENV{'PATH'}='';
$ENV{'BASH_ENV'}='';
$ENV{'ENV'}='';
$PROGNAME = "check_diskio_ucd";
$REVISION = "1.2";
# checking commandline arguments
my $arg_status = check_args();
if ($arg_status){
print "ERROR: some arguments wrong\n";
exit $ERRORS{"UNKNOWN"};
}
if ( $opt_verbose ) {
printf("Net::SNMP Version: %s\n",Net::SNMP->VERSION);
}
# set alarmhandler for timeout handling
$SIG{'ALRM'} = sub {
print ("ERROR: plugin timed out after $opt_timeout seconds \n");
exit $ERRORS{"UNKNOWN"};
};
alarm($opt_timeout);
# let's see if the server wants to speak with us
my ($snmp_session,$snmp_error)=open_snmp_session($opt_hostname);
if ( ! defined ($snmp_session)) {
print "ERROR: Could not open connection: $snmp_error \n";
exit $ERRORS{'UNKNOWN'};
}
$snmp_session->translate(['-endofmibview'=>0,'-nosuchobject'=>0,'-nosuchinstance'=>0]);
if ( $opt_verbose ) {
printf("1st Measurement of DiskIO-Load\n");
}
# get diskIOTable 1st Measurement
my $hdskIOTable_1st=get_table($dskIOTable);
if ( $opt_verbose ) {
printf("Waiting %d seconds\n",WAIT_BETWEEN_MEASUREMENT);
}
# now we are waiting some seconds
sleep (WAIT_BETWEEN_MEASUREMENT);
if ( $opt_verbose ) {
printf("2nd Measurement of DiskIO-Load\n");
}
# get diskIOTable 2nd Measurement
my $hdskIOTable_2nd=get_table($dskIOTable);
$snmp_session->close;
if ( $opt_verbose ) {
print_hdskIOTable ("1st Measurement",$hdskIOTable_1st);
print_hdskIOTable ("2nd Measurement",$hdskIOTable_2nd);
}
my $perfdata="";
my $hdskIOTableDiff=Build_Diff_Values();
my $s_DiskFound=0;
foreach my $l_key (oid_lex_sort(keys(%{$hdskIOTableDiff}))){
next if (!(oid_base_match($dskIOIndex,$l_key)));
my $l_val=$hdskIOTableDiff->{$l_key};
next if ( $hdskIOTableDiff->{$dskIODevice.".".$l_val} ne $opt_device );
$s_DiskFound=1;
my $dskIOBytesTotal = $hdskIOTableDiff->{$dskIONRead.".".$l_val} + $hdskIOTableDiff->{$dskIONWritten.".".$l_val};
my $dskIOTotal = $hdskIOTableDiff->{$dskIOReads.".".$l_val} + $hdskIOTableDiff->{$dskIOWrites.".".$l_val};
my $IONReadMB =$hdskIOTableDiff->{$dskIONRead.".".$l_val} /1024/1024;
my $IONWrittenMB=$hdskIOTableDiff->{$dskIONWritten.".".$l_val} /1024/1024;
my $IONReadMBsec =$IONReadMB / WAIT_BETWEEN_MEASUREMENT;
my $IONWrittenMBsec=$IONWrittenMB / WAIT_BETWEEN_MEASUREMENT;
my $IOReadssec = $hdskIOTableDiff->{$dskIOReads.".".$l_val} / WAIT_BETWEEN_MEASUREMENT;
my $IOWritessec = $hdskIOTableDiff->{$dskIOWrites.".".$l_val} / WAIT_BETWEEN_MEASUREMENT;
# Build performance data line
$perfdata=sprintf("RMBsec=%.2f,WMBsec=%.2f,RIOsec=%d,WIOsec=%d",$IONReadMBsec,$IONWrittenMBsec,$IOReadssec,$IOWritessec);
if ( $opt_verbose ) {
printf ("Disk found in UCD-DISKIO Table\n");
printf ("IndexNr : %d\n",$l_val);
printf ("Device : %s\n",$hdskIOTableDiff->{$dskIODevice.".".$l_val});
printf ("Bytes Read : %d (%.2f MB)(%.2f MB/sec)\n",$hdskIOTableDiff->{$dskIONRead.".".$l_val},$IONReadMB,$IONReadMBsec);
printf ("Bytes Written : %d (%.2f MB)(%.2f MB/sec)\n",$hdskIOTableDiff->{$dskIONWritten.".".$l_val},$IONWrittenMB,$IONWrittenMBsec);
printf ("I/O Read : %d (%.2f IO/sec)\n",$hdskIOTableDiff->{$dskIOReads.".".$l_val},$IOReadssec);
printf ("I/O Writes : %d (%.2f IO/sec)\n",$hdskIOTableDiff->{$dskIOWrites.".".$l_val},$IOWritessec);
}
# in case we use some other calculated fields for checking
# $IONReadMBsec, $IOReadssec, $IOWritessec
my $checkval=$IONWrittenMBsec;
if ( $checkval < $opt_warn ) {
$msg = sprintf("DISKIO OK - No Problems found (Write %d MB/s)",$checkval);
$state = $ERRORS{'OK'};
}elsif ( $checkval < $opt_crit ) {
$msg = sprintf("DISKIO WARN - Write %d MB/s",$checkval);
$state = $ERRORS{'WARNING'};
}else{
$msg = sprintf("DISKIO CRIT - Write %d MB/s",$checkval);
$state = $ERRORS{'CRITICAL'};
}
last;
}
if ( $s_DiskFound == 1 ) {
# attach performance data line
$msg.="|".$perfdata;
}else{
$msg=sprintf("DISKIO WARN - Disk %s not found",$opt_device);
$state = $ERRORS{'WARNING'};
}
# and now "over and out"
print "$msg\n";
exit $state;
#--------------------------------------------------------------------------#
# S U B R O U T I N E S #
#--------------------------------------------------------------------------#
#--------------------------------------------------------------------------
sub open_snmp_session {
#--------------------------------------------------------------------------
my ($l_host)=@_;
my ($snmp_session,$snmp_error);
# open SNMP Session to Server
if ( $opt_snmpvers eq "3" ) {
if ( defined ($opt_authpasswd)) {
if ( defined ($opt_privpasswd)) {
($snmp_session,$snmp_error)=Net::SNMP->session(
-hostname => $l_host,
-port => $opt_port,
-timeout => 2,
-retries => 2,
-maxmsgsize => 16384,
-version => $opt_snmpvers,
-username => $opt_username,
-authpassword => $opt_authpasswd,
-authprotocol => $opt_authproto,
-privpassword => $opt_privpasswd,
-privprotocol => $opt_privproto,
);
} else {
($snmp_session,$snmp_error)=Net::SNMP->session(
-hostname => $l_host,
-port => $opt_port,
-timeout => 2,
-retries => 2,
-maxmsgsize => 16384,
-version => $opt_snmpvers,
-username => $opt_username,
-authpassword => $opt_authpasswd,
-authprotocol => $opt_authproto,
);
}
} else {
($snmp_session,$snmp_error)=Net::SNMP->session(
-hostname => $l_host,
-port => $opt_port,
-timeout => 2,
-retries => 2,
-maxmsgsize => 16384,
-version => $opt_snmpvers,
-username => $opt_username,
);
}
} else {
($snmp_session,$snmp_error)=Net::SNMP->session(
-hostname => $l_host,
-community => $opt_community,
-port => $opt_port,
-timeout => 2,
-retries => 2,
-maxmsgsize => 16384,
-version => $opt_snmpvers,
);
}
return ($snmp_session,$snmp_error);
}
#--------------------------------------------------------------------------
sub create_msg {
#--------------------------------------------------------------------------
my ($l_txt,$l_msg)=@_;
if (! defined $l_txt) {return};
if (defined $$l_msg) {
$$l_msg.=", ";
}
$$l_msg.=$l_txt;
}
#--------------------------------------------------------------------------
sub get_table {
#--------------------------------------------------------------------------
my ($l_oid)=@_;
my $l_snmp_result=$snmp_session->get_table(
-baseoid => $l_oid
);
if ($snmp_session->error_status != 0) {
printf("ERROR %d: get_table: %s",$snmp_session->error_status,$snmp_session->error,"\n");
$snmp_session->close;
exit $ERRORS{'UNKNOWN'};
}
return $l_snmp_result;
}
#--------------------------------------------------------------------------
sub check_args {
#--------------------------------------------------------------------------
Getopt::Long::Configure('bundling');
GetOptions
("V" => \$opt_version,
"version" => \$opt_version,
"L" => \$opt_license,
"license" => \$opt_license,
"v" => \$opt_verbose,
"verbose" => \$opt_verbose,
"h|?" => \$opt_help,
"help" => \$opt_help,
"t=i" => \$opt_timeout,
"timeout=i" => \$opt_timeout,
"H=s" => \$opt_hostname,
"hostname=s" => \$opt_hostname,
"d=s" => \$opt_device,
"device=s" => \$opt_device,
"C=s" => \$opt_community,
"community=s" => \$opt_community,
"p=i" => \$opt_port,
"port=i" => \$opt_port,
"s=s" => \$opt_snmpvers,
"snmpvers=s" => \$opt_snmpvers,
"u=s" => \$opt_username,
"username=s" => \$opt_username,
"o=s" => \$opt_authpasswd,
"authpass=s" => \$opt_authpasswd,
"r=s" => \$opt_authproto,
"authprot=s" => \$opt_authproto,
"O=s" => \$opt_privpasswd,
"privpass=s" => \$opt_privpasswd,
"R=s" => \$opt_privproto,
"privprot=s" => \$opt_privproto,
"w=s" => \$opt_warn,
"warn=s" => \$opt_warn,
"c=s" => \$opt_crit,
"crit=s" => \$opt_crit,
);
if ($opt_license) {
print_gpl($PROGNAME,$REVISION);
exit $ERRORS{'OK'};
}
if ($opt_version) {
print_revision($PROGNAME,$REVISION);
exit $ERRORS{'OK'};
}
if ($opt_help) {
print_help();
exit $ERRORS{'OK'};
}
if ( ! defined($opt_hostname)){
print "\nERROR: Hostname not defined\n\n";
print_usage();
exit $ERRORS{'UNKNOWN'};
}
if ( ! defined($opt_device)){
print "\nERROR: Device not defined\n\n";
print_usage();
exit $ERRORS{'UNKNOWN'};
}
unless (defined $opt_snmpvers) {
$opt_snmpvers = DEFAULT_SNMPVERS;
}
if (($opt_snmpvers ne "1") && ($opt_snmpvers ne "2") && ($opt_snmpvers ne "3")) {
printf ("\nERROR: SNMP Version %s unknown\n",$opt_snmpvers);
print_usage();
exit $ERRORS{'UNKNOWN'};
}
unless (defined $opt_warn) {
print "\nERROR: parameter -w not defined\n\n";
print_usage();
exit ($ERRORS{'UNKNOWN'});
}
unless (defined $opt_crit) {
print "\nERROR: parameter -c not defined\n\n";
print_usage();
exit ($ERRORS{'UNKNOWN'});
}
if ( $opt_warn > $opt_crit) {
print "\nERROR: parameter -w greater than parameter -c\n\n";
print_usage();
exit ($ERRORS{'UNKNOWN'});
}
unless (defined $opt_timeout) {
$opt_timeout = DEFAULT_TIMEOUT;
}
unless (defined $opt_port) {
$opt_port = DEFAULT_PORT;
}
unless (defined $opt_community) {
$opt_community = DEFAULT_COMMUNITY;
}
if (defined $opt_privpasswd) {
unless (defined $opt_privproto) {
$opt_privproto = DEFAULT_PRIVPROTO;
}
}
if (defined $opt_authpasswd) {
unless (defined $opt_authproto) {
$opt_authproto = DEFAULT_AUTHPROTO;
}
}
if ($opt_snmpvers eq 3) {
unless (defined $opt_username) {
printf ("\nERROR: SNMP Version %s: please define username\n",$opt_snmpvers);
print_usage();
exit $ERRORS{'UNKNOWN'};
}
}
return $ERRORS{'OK'};
}
#--------------------------------------------------------------------------
sub print_usage {
#--------------------------------------------------------------------------
print "Usage: $PROGNAME [-h] [-L] [-t timeout] [-v] [-V] [-C community] [-p port] [-s 1|2|3] -H hostname -d diskdevice -w -c \n\n";
print "SNMP version 3 specific: [-u username] [-o authpass] [-r authprot] [-O privpass] [-R privprot]\n";
}
#--------------------------------------------------------------------------
sub print_help {
#--------------------------------------------------------------------------
print_revision($PROGNAME,$REVISION);
printf("\n");
print_usage();
printf("\n");
printf(" Check DiskIO via UCD-DISKIO SNMP MIB\n");
printf(" e.g: used on linux in net-snmp agent.\n\n");
printf("-t (--timeout) Timeout in seconds (default=%d)\n",DEFAULT_TIMEOUT);
printf("-H (--hostname) Host to monitor\n");
printf("-d (--device) Disk to monitor\n");
printf("-s (--snmpvers) SNMP Version [1|2|3] (default=%d)\n",DEFAULT_SNMPVERS);
printf("-C (--community) SNMP Community (default=%s)\n",DEFAULT_COMMUNITY);
printf("-p (--port) SNMP Port (default=%d)\n",DEFAULT_PORT);
printf("-w (--warn) Parameter warning for MB Write/sec\n");
printf("-c (--crit) Parameter critical for MB Write/sec\n");
printf("-h (--help) Help\n");
printf("-V (--version) Programm version\n");
printf("-v (--verbose) Print some useful information\n");
printf("-L (--license) Print license information\n");
printf("\nSNMP version 3 specific arguments:\n");
printf("-u (--username) Security Name\n");
printf("-o (--authpassword) Authentication password\n");
printf("-r (--authprotocol) Authentication protocol [md5|sha]\n");
printf("-O (--privpassword) Privacy password\n");
printf("-R (--privprotocol) Privacy protocol [des|aes|3des]\n");
printf("\n");
}
#--------------------------------------------------------------------------
sub print_hdskIOTable {
#--------------------------------------------------------------------------
my ($l_description,$hdskIOTable)=@_;
printtable (sprintf("UCD-DISKIO Table - %s",$l_description));
print ("======================================\n");
foreach my $l_key (oid_lex_sort(keys(%{$hdskIOTable}))){
next if (!(oid_base_match($dskIOIndex,$l_key)));
my $l_val=$hdskIOTable->{$l_key};
printtabular("Index", $l_val);
printtabular("IO Device", $hdskIOTable->{$dskIODevice.".".$l_val});
printtabular("Bytes Read", $hdskIOTable->{$dskIONRead.".".$l_val});
printtabular("Bytes Written", $hdskIOTable->{$dskIONWritten.".".$l_val});
printtabular("I/O Read", $hdskIOTable->{$dskIOReads.".".$l_val});
printtabular("I/O Write", $hdskIOTable->{$dskIOWrites.".".$l_val});
printf("\n");
}
}
#--------------------------------------------------------------------------
sub printhead {
#--------------------------------------------------------------------------
my ($l_head)=@_;
printf ("\n%-40s\n",$l_head);
}
#--------------------------------------------------------------------------
sub printtable {
#--------------------------------------------------------------------------
my ($l_head)=@_;
printf ("%-40s\n",$l_head);
}
#--------------------------------------------------------------------------
sub printscalar {
#--------------------------------------------------------------------------
my ($l_arg,$l_oid)=@_;
printf ("%-35s: %-30s\n",$l_arg,$l_oid);
}
#--------------------------------------------------------------------------
sub printtabular {
#--------------------------------------------------------------------------
my ($l_arg,$l_oid)=@_;
printf ("%-35s: %-30s\n",$l_arg,$l_oid);
}
#--------------------------------------------------------------------------
sub Build_Diff_Values {
#--------------------------------------------------------------------------
my $hDiffTable={};
foreach my $l_key (oid_lex_sort(keys(%{$hdskIOTable_2nd}))){
next if (!(oid_base_match($dskIOIndex,$l_key)));
my $index_2nd=$hdskIOTable_2nd->{$l_key};
my $l_device=$hdskIOTable_2nd->{$dskIODevice.".".$index_2nd};
next if ( $l_device ne $opt_device );
my $index_1st=Get_Index_Nr_1st_Table($l_device);
my $IONRead =$hdskIOTable_2nd->{$dskIONRead.".".$index_2nd} - $hdskIOTable_1st->{$dskIONRead.".".$index_1st};
my $IONWritten=$hdskIOTable_2nd->{$dskIONWritten.".".$index_2nd} - $hdskIOTable_1st->{$dskIONWritten.".".$index_1st};
my $IOReads =$hdskIOTable_2nd->{$dskIOReads.".".$index_2nd} - $hdskIOTable_1st->{$dskIOReads.".".$index_1st};
my $IOWrites =$hdskIOTable_2nd->{$dskIOWrites.".".$index_2nd} - $hdskIOTable_1st->{$dskIOWrites.".".$index_1st};
if ( $IONRead < 0 ) {
$IONRead+=COUNTER_MAXVAL;
}
if ( $IONWritten < 0 ) {
$IONWritten+=COUNTER_MAXVAL;
}
if ( $IOReads < 0 ) {
$IOReads+=COUNTER_MAXVAL;
}
if ( $IOWrites < 0 ) {
$IOWrites+=COUNTER_MAXVAL;
}
$hDiffTable->{$l_key} = $index_2nd;
$hDiffTable->{$dskIODevice.".".$index_2nd} = $l_device;
$hDiffTable->{$dskIONRead.".".$index_2nd} = $IONRead;
$hDiffTable->{$dskIONWritten.".".$index_2nd} = $IONWritten;
$hDiffTable->{$dskIOReads.".".$index_2nd} = $IOReads;
$hDiffTable->{$dskIOWrites.".".$index_2nd} = $IOWrites;
}
return($hDiffTable);
}
#--------------------------------------------------------------------------
sub Get_Index_Nr_1st_Table {
#--------------------------------------------------------------------------
my ($l_device)=@_;
foreach my $l_key (keys(%{$hdskIOTable_1st})){
next if (!(oid_base_match($dskIOIndex,$l_key)));
my $l_val=$hdskIOTable_1st->{$l_key};
my $l_device_1st=$hdskIOTable_1st->{$dskIODevice.".".$l_val};
next if ( $l_device_1st ne $l_device );
return($l_val);
}
return(undef);
}
#--------------------------------------------------------------------------
sub print_gpl {
#--------------------------------------------------------------------------
print <.
EOD
}
#--------------------------------------------------------------------------
sub print_revision {
#--------------------------------------------------------------------------
my ($l_prog,$l_revision)=@_;
print < -w -c -d
for more information concerning this plugin call:
check_diskio_ucd -h
perldoc check_diskio_ucd
more information concerning the configuration of the UCD SNMP Package:
man snmpd.conf
=head1 AUTHOR
Herbert Stadler, Austria (hestadler@gmx.at)
June 2009
This plugin is a contribution to the nagios community.
=head1 REQUIRED SOFTWARE
from search.cpan.org
Net::SNMP Package e.g: Net-SNMP-5.2.0.tar.gz
UCD-DISKIO on linux is only running ok starting with net-snmp version 5.2
Releases below this version could deliver wrong figures.
Example: net-snmp-5.3.1-24.el5_2.1
To check your installed version enter:
rpm -qa | grep snmp
Please check also:
http://rhn.redhat.com/errata/RHBA-2007-0738.html
=head1 HOW TO CHECK THE SERVER FUNCTIONALITY
Example:
snmpwalk -On -c public -v 1 1.3.6.1.4.1.2021.13.15.1
should return some lines
=head1 CONFIGURATION IN NAGIOS
Copy this plugin to the nagios plugin installation directory
e.g.: /usr/lib(64)/nagios/plugin
COMMAND DEFINITION:
# "check_diskio_ucd" command definition
define command{
command_name check_diskio_ucd
command_line $USER1$/check_diskio_ucd -H $HOSTADDRESS$ ...
}
=head1 PLUGIN HISTORY
Version 1.0 - 2009-06-22 first release
Version 1.1 - 2010-03-24 check error_status of snmp call
Version 1.2 - 2010-05-04 Checking Counter32 overrun
=head1 COPYRIGHT AND DISCLAIMER
Copyright (C) 2009 by Herbert Stadler
email: hestadler@gmx.at
License Information:
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 3 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, see .
=cut