#!/usr/bin/perl # Buchan Milne 20051213 # Script to monitor OpenLDAP performance and sync-replication status via # Hobbit (may also still work with BigBrother). # # 1)Install Net::LDAP (perl-ldap) and Date::Manip (perl-DateManip) # 2)Run as task from hobbitserver.cfg (or BBEXT in bbdef.sh for BigBrother) # 3)Use ncv in hobbit to collect data: # -add "ol=ncv" to TEST2RRD, and "ncv" to GRAPHS in hobbitserver.cfg # -add NCV_ol="ReadWaiters:GAUGE,WriteWaiters:GAUGE,CurrentConnections:GAUGE" # to hobbitserver.cfg # 4)Add something like the contents of hobbitgraph-ol.cfg to hobbitgraph.cfg # 5)Ensure monitor backend in OpenLDAP 2.1 or later is configured for # anonymous access from the display server # 6)Add "ol" to hosts in bb-hosts to monitor with this script use strict; use Net::LDAP; use Date::Manip; my ($slave,$bbhost,$ldap_message,$ldap_entry,$monitor,$tmp,$content,$status,$base); my (@slaves,@values); my %ldap; my (%ldap_perf,%performance,%master); my $service = "ol"; my @slave_states = ("in sync", "not a sync-repl slave", "could not check master", "out of sync"); my @state_colours = ("OK", "NONE", "NONE", "WARN","ERR"); my @state_names = ("green", "clear", "clear", "yellow","red"); if ( $ENV{'BBHOME'} ) { # Use bbhostgrep if available (hobbit), otherwise use a hardcoded list # of servers: if ( -f "$ENV{'BBHOME'}/bin/bbhostgrep" ) { print "DEBUG Found bbhostgrep\n" if $ENV{DEBUG}; open (BBHOSTGREP, $ENV{BBHOME} . "/bin/bbhostgrep " . $service . " |") or die $*; while () { my ($junk,$host); ($junk,$host) = split ' '; print "DEBUG Adding $host to server list\n" if $ENV{DEBUG}; push @slaves, $host; } close BBHOSTGREP; } else { print "DEBUG Couldn't find bbhostgrep\n" if $ENV{DEBUG}; #@slaves = ('master.domain.com', 'ldap1.domain.com', 'ldap2.domain.com'); @slaves = ('localhost'); } @state_colours = ("&green", "&clear", "&clear", "&yellow","&red"); } else { @slaves = @ARGV; } foreach $slave (@slaves) { %master = (); $status = 0; $content = ""; print "DEBUG Using $slave\n" if $ENV{DEBUG}; $bbhost = $slave; $bbhost =~ s/\./\,/g; if ($ldap{$slave} ne undef or $ldap{$slave} = Net::LDAP->new( $slave, async=> 1 ) ) { print "DEBUG Binding to $slave\n" if $ENV{DEBUG}; $ldap_message = $ldap{$slave}->bind; # Get monitor context $ldap_message = $ldap{$slave}->search( base => '', scope => 'base', filter => '(objectclass=*)', attrs => ['monitorContext'] ); $ldap_message->code && die $ldap_message->error; $ldap_entry = $ldap_message->entry(0); $monitor = $ldap_entry->get_value('monitorContext'); #$ldap_message = $ldap->root_dse(); #$ldap_message->code && die $ldap_message->error; #$monitor = $ldap_message->get_value('monitorContext'); print "DEBUG Monitor context for $slave: $monitor\n" if $ENV{DEBUG}; %performance = &get_perf($slave,$monitor,$ldap{$slave}); $content = "\n"; foreach ( sort keys %performance ) { $content .= sprintf "%s : %s\n",$_,$performance{$_}; } # Get databases that have a master $ldap_message = $ldap{$slave}->search( base => $monitor, scope => 'sub', filter => '(&(namingContexts=*)(MonitorUpdateRef=*))', attrs => [ 'monitorupdateref', 'namingContexts' ] ); if ( $ldap_message->count == 0 ) { $content .= "$state_colours[0] Not a syncrepl slave\n"; } else { $content .= "Replication status of slave databases\n"; } for $ldap_entry ( $ldap_message->all_entries ) { $master{$ldap_entry->get_value('namingContexts')} = $ldap_entry->get_value('monitorupdateref'); } for ( keys ( %master )) { my ($slave_status,$msg) = &check_slave($slave,$master{$_},$_,\%ldap); $content .= sprintf("%s %s %s %s\n", $state_colours[$slave_status],$_,$slave_states[$slave_status],$msg); $status = ($status lt $slave_status) ? $slave_status : $status; } } else { $status = 4; $content = "$@\n"; } if ( $ENV{BBHOME} eq "" ) { print $state_colours[$status] . " $slave " . `date`; print $content; } else { my $line = "status " . $bbhost . "." . $service . " " . $state_names[$status] . " " . `date` . "\n"; $line .= $content; if ( $ENV{BB} ) { system ("$ENV{BB}", "$ENV{BBDISP}", "$line"); } else { print "$line"; } } } # clean up ldap connections foreach (keys %ldap) { next if not defined ($ldap{$_}); print "DEBUG Unbinding from $_\n" if $ENV{DEBUG}; $ldap{$_}->unbind; } sub get_contextcsn { my ($c_ldapserver,$c_base,$c_ldap) = @_; my $c_message; my $c_entry; my $bound = 'TRUE'; $c_message = $c_ldap->search( base => $c_base, scope => 'base', filter => '(objectclass=*)', attrs => ['contextCSN'] ); $c_message->code && return $c_message->error; $c_entry = $c_message->entry(0); return $c_entry->get_value('contextCSN'); } sub get_perf { my ($ldapserver,$base,$ldapconn) = @_; my ($message,$entry,$dn,$value,$monitortype,$monitorfilter,$firstattrib); my $bound =' TRUE'; my %perf; my @monitorattribs; if ( $ldapconn == undef ) { $bound = 'FALSE'; printf "DEBUG Binding to $ldapserver\n" if $ENV{DEBUG}; $ldapconn = Net::LDAP->new( $ldapserver, async => 1) or die "$@" unless defined ( $ldapconn); $message = $ldapconn->bind; } # search on monitorcontext for structuralobjectclass # OL 2.1 = structuralObjectClass: monitor # OL 2.2 and later = structuralObjectClass: monitorServer $message = $ldapconn->search( base => $base, filter => '(objectclass=*)', scope => 'base', attrs => ['structuralObjectClass'] ); $message->code && die $message->error; $monitortype = $message->entry(0)->get_value('structuralObjectClass'); print "DEBUG monitor is " . $monitortype . "\n" if $ENV{DEBUG}; if ( $monitortype eq "monitorServer" ) { $monitorfilter = '(& (| (objectclass=monitorCounterObject) (objectclass=monitorOperation) (objectclass=monitorContainer) ) (| (monitorcounter=*) (monitorOpInitiated=*) ) )'; @monitorattribs = ['monitorCounter', 'monitorOpInitiated', 'monitorOpCompleted', 'dn']; $firstattrib = 'monitorCounter'; } elsif ( $monitortype eq 'monitor' ) { $monitorfilter = '(objectclass=monitor)'; @monitorattribs = ['description', 'dn']; $firstattrib = 'description'; } # counters: print "DEBUG Searching under $base with filter $monitorfilter on $ldapserver\n" if $ENV{DEBUG}; $message = $ldapconn->search( base => $base, scope => 'sub', filter => $monitorfilter, attrs => @monitorattribs ); $message->code && die $message->error; foreach $entry ( $message->all_entries ) { $dn = $entry->dn; next if ($dn =~ m/(cn=(Connections|Backends|Databases|Listeners|Time|TLS|SASL|Log)|(^cn=Monitor))/); print "DEBUG got $dn\n" if $ENV{DEBUG}; $dn =~ s/,$base$//; $dn =~ s/cn=//g; $dn =~ s/\,/ /g; $dn =~ s/ Operations//g; $dn =~ s/Operations/Operation/g; $dn =~ s/Statistics//g; print "DEBUG getting $firstattrib for $dn\n" if $ENV{DEBUG}; $value = $entry->get_value($firstattrib); if ( $value eq "" ) { $value = $dn . " Initiated"; $perf{$value} = $entry->get_value('monitorOpInitiated'); $value = $dn . " Completed"; $perf{$value} = $entry->get_value('monitorOpCompleted'); } else { $perf{$dn} = $value; } print "DEBUG $dn:\t $value, $perf{$dn}\n" if $ENV{DEBUG}; } print "DEBUG Finished search results\n" if $ENV{DEBUG}; return %perf; } sub check_slave { my ($slave,$master,$db,$ldap) = @_; if ($ldap{$master{$_}} eq undef) { print "DEBUG Binding to master $master{$_}\n" if $ENV{DEBUG}; $ldap{$master{$_}} = Net::LDAP->new( $master{$_}, async=> 1) or return (2,""); $ldap{$master{$_}}->bind or return (2,""); } my ($mastercsn,$slavecsn); print "DEBUG Checking $master for $db\n" if $ENV{DEBUG}; $slavecsn = &get_contextcsn($slave,$db,$$ldap{$slave}) or return (1,""); $mastercsn = &get_contextcsn($master,$db,$$ldap{$master}) or return (2,""); if ($slavecsn == $mastercsn){ return (0,""); } else { my $slavetime = $slavecsn; my $mastertime = $mastercsn; my $time; $slavetime =~ s/#.*//g; $mastertime =~ s/#.*//g; $time = DateCalc($mastertime,$slavetime); $time =~ s/^(.)0:0:/$1/; $time =~ s/^\+(.*)$/$1 ahead/g; $time =~ s/^\-(.*)$/$1 behind/g; foreach ("weeks","days") { $time =~s/:/ $_ /;} $time =~ s/:/-/g; return (3,$time); } }