#!/usr/bin/perl
# $Id: check_voip_opal,v 1.11 2007/07/05 10:51:41 root Exp $
# nagios plugin to check H323 calls using simpleopal
#
# Copyright (C) 2007 George Fergadis
#
# 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 .
#
# email: rts@grnet.gr
use strict;
use warnings;
use Getopt::Std;
use Expect;
use Sys::Hostname;
use Socket;
use lib "/usr/local/nagios/libexec";
use utils qw(%ERRORS &print_revision &support &usage);
my $start_time = time;
my $debug = 0;
my $status;
$Getopt::Std::STANDARD_HELP_VERSION = 1;
my %options;
getopts('hn:g:u:t:p:', \%options) or HELP_MESSAGE();
$options{h} and HELP_MESSAGE();
$options{n} or HELP_MESSAGE();
$options{g} or HELP_MESSAGE();
$options{t} ||= 90;
srand($options{n} ^ $$); # try a better seed
$options{p} ||= int(rand(30000)+30000);
$options{u} ||= "$options{n}_$options{p}";
my $address = inet_ntoa( scalar gethostbyname(hostname() || 'localhost') );
my $command = '/usr/local/bin/simpleopal';
my $trace_file = "/var/log/nagios/$options{n}.log";
my @params = qw(
--disableui
--no-sip
--no-iax2
--no-ivr
--no-quicknet
--h245tunneldisable
--disable-grq
--no-rx-video
--no-tx-video
-ttt
);
push @params, "--output", '/dev/stdout';
push @params, "--gatekeeper", $options{g};
push @params, "--user", $options{u};
push @params, "--h323-listen", "$address:$options{p}";
push @params, "h323:$options{n}";
my $duration = -time;
my $exp = Expect->spawn($command, @params)
or die "Cannot spawn $command: $!\n";
$exp->log_stdout(0);
$exp->log_file($trace_file, "w");
my %callend = (
Unknown => { status => 'UNKNOWN', msg => 'Unknown Reason' },
Timeout => { status => 'UNKNOWN', msg => 'Timeout' },
AddressInUse => { status => 'UNKNOWN', msg => 'Address already in use' },
EndedByLocalUser => { status => 'OK', msg => 'Local endpoint application cleared call' },
EndedByNoAccept => { status => 'CRITICAL', msg => 'Local endpoint did not accept call' },
EndedByAnswerDenied => { status => 'CRITICAL', msg => 'Local endpoint declined to answer call' },
EndedByRemoteUser => { status => 'OK', msg => 'Remote endpoint application cleared call' },
EndedByRefusal => { status => 'CRITICAL', msg => 'Remote endpoint refused call' },
EndedByNoAnswer => { status => 'CRITICAL', msg => 'Remote endpoint did not answer in required time' },
EndedByCallerAbort => { status => 'CRITICAL', msg => 'Remote endpoint stopped calling' },
EndedByTransportFail => { status => 'CRITICAL', msg => 'Transport error cleared call' },
EndedByConnectFail => { status => 'CRITICAL', msg => 'Transport connection failed to establish call' },
EndedByGatekeeper => { status => 'CRITICAL', msg => 'Gatekeeper has cleared call' },
EndedByNoUser => { status => 'CRITICAL', msg => 'Call failed as could not find user (in GK)' },
EndedByNoBandwidth => { status => 'CRITICAL', msg => 'Call failed as could not get enough bandwidth' },
EndedByCapabilityExchange => { status => 'CRITICAL', msg => 'Could not find common capabilities' },
EndedByCallForwarded => { status => 'CRITICAL', msg => 'Call was forwarded using FACILITY message' },
EndedBySecurityDenial => { status => 'CRITICAL', msg => 'Call failed a security check and was ended' },
EndedByLocalBusy => { status => 'CRITICAL', msg => 'Local endpoint busy' },
EndedByLocalCongestion => { status => 'CRITICAL', msg => 'Local endpoint congested' },
EndedByRemoteBusy => { status => 'OK', msg => 'Remote endpoint busy' },
EndedByRemoteCongestion => { status => 'CRITICAL', msg => 'Remote endpoint congested' },
EndedByUnreachable => { status => 'CRITICAL', msg => 'Could not reach the remote party' },
EndedByNoEndPoint => { status => 'CRITICAL', msg => 'The remote party is not running an endpoint' },
EndedByOffline => { status => 'CRITICAL', msg => 'The remote party is off line' },
EndedByTemporaryFailure => { status => 'CRITICAL', msg => 'The remote failed temporarily app may retry' },
EndedByQ931Cause => { status => 'CRITICAL', msg => 'The remote ended the call with unmapped Q.931 cause code' },
EndedByDurationLimit => { status => 'CRITICAL', msg => 'Call cleared due to an enforced duration limit' },
EndedByInvalidConferenceID => { status => 'CRITICAL', msg => 'Call cleared due to invalid conference ID' },
);
my $reason = 'Unknown';
my $ARQ='';
my $OpalTCP='';
$exp->expect($options{t},
[ timeout => sub { $reason = 'Timeout'; } ],
[ qr/EndedBy\w+/ => sub { $reason = shift->match(); exp_continue; } ],
[ qr/OpalTCP\t.*/ => sub {
$OpalTCP = shift->match();
chop $OpalTCP;
$OpalTCP =~ s/OpalTCP\t.*to (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*$/ at $1/;
exp_continue; }
],
[ qr/Address already in use/ => sub { $reason = 'AddressInUse'; exp_continue; } ],
[ qr/OpalMan\tDeleted manager/ => sub { shift->send("x\n"); } ],
);
$duration += time;
unlink $trace_file if $callend{$reason}{status} eq 'OK';
print "VoIP $callend{$reason}{status}: [$options{n}$OpalTCP $reason in ${duration}s] ($options{p}) $callend{$reason}{msg}";
exit $ERRORS{$callend{$reason}{status}};
### Getopt functions ###
sub HELP_MESSAGE {
print << "EOF";
Usage: $0 -n -g [-u username] [-t timeout] [-p listenport]
EOF
exit $ERRORS{'OK'};
}
sub VERSION_MESSAGE { print 'Version: $Id: check_voip_opal,v 1.11 2007/07/05 10:51:41 root Exp $', "\n"; }
# vim: ts=4 et sw=4