#! /usr/bin/perl -w #========================================================================== # This script parses files generated by slurpd (OpenLDAP replication server) # displays if errors occured and if master and slaves are up2date # Script designed for Nagios/NRPE ( http://www.nagios.org ) # # Usage : # You have to set the replica's name and port (as written in slapd.conf) # ./slurpd_status -w warning_level -c critical_level [-h hostname] [-p hostport] [-v] # The level must be given with 3 integers, for exemple 100,5,30 # The first integer are the entries in transition (not picked up by slurpd) # The second integer are the rejected entries (not accepted by the slave) # The third integer are the waiting entries (picked up by slurpd but not sent to the slave) # If only one integer is given, the level will be the max of all entries (in transition + rejected + waiting) # If no port is declared, 0 will be used # If no host is declared, localhost will be used # Option -v displays a lot of message, use for debug only # # 2004 (c) Clement OUDOT # # 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. # # GPL Licence : http://www.gnu.org/licenses/gpl.txt # # More contributions on http://www.linagora.org #========================================================================== #========================================================================== # Modules #========================================================================== use strict ; use Getopt::Std ; use List::Util qw(max) ; #========================================================================== # Internal Parameters #========================================================================== # Directory where slurpd stores his files my $slurpd_tempdir = "/var/openldap-slurp/replica" ; # Replog file beetween slapd and slurpd, as written in slapd.conf my $slapd_replog_file = "/var/replog" ; # Nagios Codes my %code = ( 'Ok' => 0, 'Warning' => 1, 'Critical' => 2, 'Unknown' => 3) ; #========================================================================== # Command Line Parameters #========================================================================== my %opts ; getopt( 'wchpv' , \%opts ) ; my $debug = 1 if ( exists ( $opts{"v"} ) ) ; &usage unless ( exists ( $opts{"w"} ) ) ; &usage unless ( exists ( $opts{"c"} ) ) ; $opts{"h"} = "localhost" unless ( exists ( $opts{"h"} ) ) ; $opts{"p"} = 0 unless ( exists ( $opts{"p"} ) ) ; my $slave_name = $opts{"h"} ; my $slave_port = $opts{"p"} ; my $warning_level = $opts{"w"} ; my $critical_level = $opts{"c"} ; # See if warning_level and critical_level are given with 3 integers or just one my @warning_level ; if ( $warning_level =~ /^(\d*)$/ ) { # This is just one integer $warning_level = $1 ; if ( $debug ) { print "Warning level set to $warning_level entries\n" ; } } elsif ( $warning_level =~ /^(\d*),(\d*),(\d*)$/ ) { # Three integers, comma separated @warning_level = ( $1, $2 , $3 ) ; if ( $debug ) { print "Warning level set to $1 entries in transition, $2 rejected entries and $3 waiting entries\n" ; } $warning_level = 0 ; } else { # Bad usage &usage ; } my @critical_level ; if ( $critical_level =~ /^(\d*)$/ ) { # This is just one integer $critical_level = $1 ; if ( $debug ) { print "Critical level set to $critical_level entries\n" ; } } elsif ( $critical_level =~ /^(\d*),(\d*),(\d*)$/ ) { # Three integers, comma separated @critical_level = ( $1, $2 , $3 ) ; if ( $debug ) { print "Critical level set to $1 entries in transition, $2 rejected entries and $3 waiting entries\n" ; } $critical_level = 0 ; } else { # Bad usage &usage ; } #========================================================================== # Usage function #========================================================================== sub usage { if ( $debug ) { print "Usage: $0 -w warning_level -c critical_level [-h hostname] [-p hostport] [-v]\n\n" ; print "The level (warning or critical) can be given as 3 comma separated integers, for example 100,5,30\n" ; print "The first integer is the number of entries in transition (not picked up by slurpd)\n" ; print "The second integer is the number of rejected entries (not accepted by the slave)\n" ; print "The third integer is the number of waiting entries (picked up by slurpd but not sent to the slave)\n" ; print "If only one integer is given, the level will be the max (in transition, rejected and waiting)\n\n" ; print "hostname and hostport must fit what is written in slapd.conf\n" ; print "If no hostport is declared, 0 will be used\n" ; print "If no hostname is declared, localhost will be used\n\n" ; print "Option -v displays a lot of message, use for debug only !\n" ; } else { print "Bad script usage. Use -v for more inforations.\n" ; } exit $code{'Unknown'} ; } #========================================================================== # Slurpd Files #========================================================================== my $slurpd_replog_file = $slurpd_tempdir."/slurpd.replog" ; my $status_file = $slurpd_tempdir."/slurpd.status" ; my $rej_file = $slurpd_tempdir."/".$slave_name.":".$slave_port.".rej" ; #========================================================================== # Welcome #========================================================================== if ( $debug ) { print "Determining replication status for $slave_name at port $slave_port\n\n" ; } #========================================================================== # Preliminary Tests #========================================================================== # If one of the 4 files exists but we can't read it, it's a problem foreach ( $slapd_replog_file , $slurpd_replog_file , $status_file , $rej_file ) { if ( ( -e $_ ) && ( ! -r $_ ) ) { if ( $debug ) { print "File $_ exists but is not readable for the user running this script\n" ; } else { print "Unable to read $_\n" ; } exit $code{'Unknown'} ; } } # If slapd replog file is not present, it can meen that : # - the internal script config is bad # - the config of OpenLDAP is bad # - no modifications were done to slapd, so replog file was not created # - slapd is not running well... # => an Unknown status is returned unless ( -e $slapd_replog_file ) { if ( $debug ) { print "File $slapd_replog_file does not exist\n" ; } else { print "No slapd replog file\n" ; } exit $code{'Unknown'} ; } # If slurpd status file is not present, it can meen that : # - the internal script config is bad # - slurpd was launched with an alternate tempdir (option -t) # - slurpd was never launched # - slurpd is not running well... # => a Critical status is returned unless ( -e $status_file ) { if ( $debug ) { print "File $status_file does not exist\n" ; } else { print "No slurpd status file\n" ; } exit $code{'Critical'} ; } # If slurpd replog file is not present, it can meen that : # - the internal script config is bad # - slurpd is not running well... # => a Critical status is returned unless ( -e $slurpd_replog_file ) { if ( $debug ) { print "File $slurpd_replog_file does not exists\n" ; } else { print "No slurpd replog file\n" ; } exit $code{'Critical'} ; } # If slurpd status file is empty, it can meen that : # - it was manually cleared # - slurpd is not running well... # => a Critical status is returned if ( -z $status_file ) { if ( $debug ) { print "File $status_file is empty\n" ; } else { print "No data in slurpd status file\n" ; } exit $code{'Critical'} ; } # If the slave is not present in status file, it can meen that : # - the command line parameters are bad # - the config of OpenLDAP is bad # - the config of OpenLDAP is good but slurpd was not restarted # => a Critical status is returned # If it is present : # Get the time and the range in status file in order to count how many # entries are waiting in slurpd replog file # Variables for further comparaison my ( $status_time, $status_range ) ; # Open status file for reading if ( open ( STATUS , $status_file ) ) { # Parse the file # lines in status file are like : # servername:serverport:time:range while ( ) { if ( /^$slave_name:$slave_port:(\d*):(\d*)$/ ) { ( $status_time , $status_range ) = ( $1 , $2 ) ; } } # Close file close ( STATUS ) ; # Test if something was found if ( defined $status_time ) { # Just print a message for debug if ( $debug ) { print "$slave_name (port $slave_port) is listed in $status_file\n\n" ; print "Time found in status file : $status_time\n\n" ; print "Range found in status file : $status_range\n\n" ; } } else { if ( $debug ) { print "$slave_name (port $slave_port) is not listed in $status_file\n"; print "Maybe slurpd needs to be restarted to read OpenLDAP configuration\n" ; } else { print "No status for $slave_name:$slave_port\n" ; } exit $code{'Critical'} ; } } # Error while opening the file # => an Unknown status is returned else { if ( $debug ) { print "Unable to open $status_file for reading\n" ; } else { print "Error while reading status file\n" ; } exit $code{'Unknown'} ; } #========================================================================== # Waiting entries in Slapd Replog File #========================================================================== # If there are waiting entries in slapd replog file, it meens that slurpd # has not yet get these entries ou that slurpd is disturbed. my $waiting_entries = 0 ; if ( open ( SLAPDREPLOG , $slapd_replog_file ) ) { # Count entries that must be added to slurpd replog file while ( ) { if ( /^replica:/ ) { if ( $slave_port ) { # The port is <> 0, so it is written after the name $waiting_entries++ if /^replica: $slave_name:$slave_port$/ ; } else { # The port is = 0, so only the name is written (or the port after the name) $waiting_entries++ if /^replica: $slave_name(:$slave_port)?$/ ; } next ; } } # Close file close ( SLAPDREPLOG ) ; # Print debug message if ( $debug ) { print "$waiting_entries entries in slapd replog file\n\n" ; } } else { if ( $debug ) { print "Unable to read $slapd_replog_file\n" ; } } #========================================================================== # Rejection File #========================================================================== # Rejection file exists only if errors occured. # Keep time and range for comparaison with slurpd replog file my $rejected_entries = 0 ; if ( -e $rej_file ) { # The rejection file exists if ( open ( REJ, $rej_file ) ) { # Count entries in error while ( ) { $rejected_entries++ if ( /^time: / ) ; if ( ( $debug ) && (/^dn: (.*)$/ ) ) { print "Modification on $1 was rejected\n" ; } } # Close file close ( REJ ) ; # Print debug message if ( $debug ) { print "$rejected_entries entries rejected\n\n" ; } } else { # Error while opening the file if ( $debug ) { print "Unable to read $rej_file\n" ; } } } else { # No rejection file if ( $debug ) { print "There is no rejection file\n\n" ; } } #========================================================================== # Slurpd Replog File #========================================================================== # Find all entries younger than time and range found in status file # This entries must concern our slave # => $slave_is_concerned is set to 1 if replica: $slave_name:$slave_port is found # => Go to the next lines # => Get the time and set $slave_is_concerned to 0, and continue the loop my $slave_is_concerned = 0 ; my $younger_entries = 0 ; if ( open ( REPLOG , $slurpd_replog_file ) ) { # Parse the file while ( ) { # If it is a "replica:" line if ( /^replica:/ ) { if ( $slave_port ) { # The port is <> 0, so it is written after the name $slave_is_concerned = 1 if /^replica: $slave_name:$slave_port$/ ; } else { # The port is = 0, so only the name is written (or the port after the name) $slave_is_concerned = 1 if /^replica: $slave_name(:$slave_port)?$/ ; } # Go to the next line next ; } if ( ( $slave_is_concerned ) && ( /^time: (\d*)\.?(\d*)?$/ ) ) { # Reset $slave_is_concerned $slave_is_concerned = 0 ; # Next if replog entry is older than status time next if ( $1 < $status_time ) ; # Count the entry if younger and go to next line if ( $1 > $status_time ) { $younger_entries++ ; if ( $debug ) { print "Entry with time $1 is young\n" } next ; } # Check the range if times are equal if ( $1 == $status_time ) { my $replog_range = $2 ? $2 : 0 ; # If replog range > status range, entry is younger if ( $replog_range > $status_range ) { $younger_entries++ ; if ( $debug ) { print "Entry with time $1 and range $replog_range is young\n" ; } next ; } # Else it is older, nothing to do } } } # Close file close ( REPLOG ) ; if ( $debug ) { print "$younger_entries entries waiting in slurpd replog file\n\n" ; } } # Error while opening the file else { if ( $debug ) { print "Unable to read $slurpd_replog_file\n" ; } } #========================================================================== # Results and exit with Nagios codes #========================================================================== # All errors my $all_errors = $waiting_entries + $rejected_entries + $younger_entries ; my $max_errors = max ( $waiting_entries , $rejected_entries , $younger_entries ) ; if ( $debug ) { print "A total of $all_errors errors was found :\n" ; print "$waiting_entries entries are in transition\n" ; print "$rejected_entries entries are rejected\n" ; print "$younger_entries entries are waiting\n" ; } # Message to sent print "$waiting_entries entries in transition, $rejected_entries entries rejected, $younger_entries entries waiting\n" ; # exit code if ( ( $critical_level && $max_errors >= $critical_level ) || ( @critical_level && ( $critical_level[0] <= $waiting_entries || $critical_level[1] <= $rejected_entries || $critical_level[2] <= $younger_entries ) ) ) { exit $code{'Critical'} ; } if ( ( $warning_level && $max_errors >= $warning_level ) || ( @warning_level && ( $warning_level[0] <= $waiting_entries || $warning_level[1] <= $rejected_entries || $warning_level[2] <= $younger_entries ) ) ) { exit $code{'Warning'} ; } exit $code{'Ok'} ;