#!/usr/bin/perl
#
# check_swap_rate -- Nagios plugin to check swap rate of system.
#
# Written by Russ Allbery <rra@stanford.edu>
# Based on work by Adam Lewenberg <adamhl@stanford.edu>
# Copyright 2011, 2012, 2013
#     The Board of Trustees of the Leland Stanford Junior University
#
# This program is free software; you may redistribute it and/or modify it
# under the same terms as Perl itself.

##############################################################################
# Modules and declarations
##############################################################################

use 5.006;
use strict;
use warnings;

use Getopt::Long qw(GetOptions);

# Time period for the sar command.
our $PERIOD = 5;

# Thresholds for warning and critical in swaps per second.
our $WARNING  =  30;
our $CRITICAL = 100;

##############################################################################
# Implementation
##############################################################################

# Report a syntax error and exit.  We do this via stdout in order to satisfy
# the Nagios plugin output requirements, but also report a more conventional
# error via stderr in case people are calling this outside of Nagios.
sub syntax {
    print "SWAP-RATE UNKNOWN - ", join ('', @_), "\n";
    warn "$0: ", join ('', @_), "\n";
    exit 3;
}

# Parse command line options.
my ($help, $host, $version);
Getopt::Long::config ('bundling', 'no_ignore_case');
GetOptions ('c|critical=i' => \$CRITICAL,
            'h|help'       => \$help,
            'p|period=i'   => \$PERIOD,
            'w|warning=i'  => \$WARNING)
    or syntax ("invalid option");
if ($help) {
    print "Feeding myself to perldoc, please wait....\n";
    exec ('perldoc', '-t', $0) or die "Cannot fork: $!\n";
}
syntax ("extra arguments on command line") if @ARGV;

# Set up the alarm.
$SIG{ALRM} = sub {
    print "SWAP-RATE CRITICAL - command timeout after $PERIOD seconds\n";
    exit 2;
};
alarm ($PERIOD + 2);

# Run the command and capture the output.
my @command = ('sar', '-W', $PERIOD, '1');
unless (open (OUT, '-|', @command)) {
    print "SWAP-RATE CRITICAL - Cannot run @command: $!\n";
    warn "$0: cannot run @command: $!\n";
    exit 2;
}
local $/;
my $output = <OUT>;
close OUT;
if ($? != 0) {
    print "SWAP-RATE CRITICAL - @command failed\n";
    warn "$0: @command failed\n";
    exit 2;
}

# Extract the swap rate information from the sar output.
if ($output =~ /Average:\s+(\d+\.\d+)\s+(\d+\.\d+)/) {
    my ($from, $to) = ($1, $2);
    my $max = ($from > $to) ? $from : $to;
    my ($state, $status) = ('OK', 0);
    if ($max > $CRITICAL) {
        ($state, $status) = ('CRITICAL', 2);
    } elsif ($max > $WARNING) {
        ($state, $status) = ('WARNING', 1);
    }
    print "SWAP-RATE $state - $max swaps/sec\n";
    exit $status;
} else {
    print "SWAP-RATE CRITICAL - Cannot parse @command output\n";
    warn "$0: cannot parse @command output\n";
    exit 2;
}

##############################################################################
# Documentation
##############################################################################

=for stopwords
Lewenberg Nagios sar util Allbery

=head1 NAME

check_swap_rate - Nagios plugin to check swap rate of system

=head1 SYNOPSIS

B<check_swap_rate> [B<-h>] [B<-c> I<threshold>] [B<-w> I<threshold>]
    [B<-p> I<period>]

=head1 DESCRIPTION

B<check_swap_rate> is a Nagios plugin that checks the current swap rate of
the system using an external command (normally B<sar>) against provided
thresholds and returns critical, warning, or okay status depending on
whether that swap rate is below various thresholds.  The default
thresholds are okay below 30 swaps per second, warning below 100 swaps per
second, and critical above that level.  The maximum of the swaps in and
the swaps out is taken as the swap rate.

B<check_swap_rate> will always produce a single line of output starting
with C<SWAP-RATE>.  The next word will be the status, chosen from
C<CRITICAL>, C<WARNING>, or C<OK>, or C<UNKNOWN> for a syntax error.
Following that will be a dash surrounded by spaces and then the swap rate,
or an error if the command failed.

The default command to run will be C<sar -W 5 1>, and the average results
will be used.  This averages the results over five seconds.  The period
over which to average can be changed with the B<-p> option.  This plugin
parses the output from B<sar> and will break if that output format
changes.

=head1 OPTIONS

=over 4

=item B<-c> I<threshold>, B<--critical>=I<threshold>

Change the critical threshold to I<threshold>, which should be an integer
number of swaps per second.  The default is 100.

=item B<-h>, B<--help>

Print out this documentation (which is done simply by feeding the script
to C<perldoc -t>).

=item B<-p> I<period>, B<--period>=I<period>

Change the period over which the B<sar> command averages.  The default is
five seconds.  This is also how long the command will take to run.

=item B<-w> I<threshold>, B<--warning>=I<threshold>

Change the warning threshold to I<threshold>, which should be an integer
number of swaps per second.  The default is 30.

=back

=head1 EXIT STATUS

B<check_swap_rate> follows the standard Nagios exit status requirements.
This means that it will exit with status 0 if the swap rate is below all
thresholds, status 2 if it is above the critical threshold, or status 1 if
it is between the warning and critical threshold.  For other errors, such
as invalid syntax, B<check_swap_rate> will exit with status 3.

=head1 BUGS

The standard B<-v> verbose Nagios plugin option is not supported, nor is
the B<-V> version option.

The usage message for invalid options and for the B<-h> option doesn't
conform to Nagios standards.

=head1 CAVEATS

This script does not use the Nagios util library or any of the defaults
that it provides, which makes it somewhat deficient as a Nagios plugin.
This is intentional, though, since this script can be used with other
monitoring systems as well.  It's not clear what a good solution to this
would be.

=head1 SEE ALSO

sar(1)

=head1 AUTHORS

Written by Russ Allbery based on work by Adam Lewenberg.

=head1 COPYRIGHT AND LICENSE

Copyright 2012 The Board of Trustees of the Leland Stanford Junior
University.

This program is free software; you may redistribute it and/or modify it
under the same terms as Perl itself.

=cut
