#!/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