#!/usr/bin/perl -w # Munin plugin for monitoring slapd. Based on Bjorn Ruberg's slapd_ # plugin. The main difference is that in our case munin SASL-binds # against the LDAP directory, and a single connection is used to collect # all statistics. # Copyright Bjorn Ruberg # Copyright © 2015 Guilhem Moulin # # 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, version 2 of the License. # # 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 . use warnings; use strict; use Net::LDAP (); use Net::LDAP::Util (); use Authen::SASL (); # The possible measurements my %OPS = ('statistics_bytes' => { 'search' => "cn=Bytes,cn=Statistics", 'desc' => "The number of bytes sent by the LDAP server.", 'vlabel' => 'Bytes per ${graph_period}', 'label' => 'Bytes', 'title' => "Number of bytes sent", 'info' => "The graph shows the number of bytes sent", 'scope' => "base" }, 'statistics_pdu' => { 'search' => "cn=PDU,cn=Statistics", 'desc' => "The number of PDUs sent by the LDAP server.", 'vlabel' => 'PDUs per ${graph_period}', 'label' => 'PDUs', 'title' => "Number of PDUs sent", 'info' => "The graph shows the number of PDUs sent", 'scope' => "base" }, # Referrals 'statistics_referrals' => { 'search' => "cn=Referrals,cn=Statistics", 'desc' => "The number of Referrals sent by the LDAP server.", 'vlabel' => 'Referrals per ${graph_period}', 'label' => 'Referrals', 'title' => "Number of LDAP Referrals", 'info' => "The graph shows the number of referrals sent", 'scope' => "base" }, # Entries 'statistics_entries' => { 'search' => "cn=Entries,cn=Statistics", 'desc' => "The number of Entries sent by the LDAP server.", 'vlabel' => 'Entries per ${graph_period}', 'label' => 'Entries', 'title' => "Number of LDAP Entries", 'info' => "The graph shows the number of entries sent", 'scope' => "base" }, # Only read Total 'connections' => { 'search' => 'cn=Total,cn=Connections', 'desc' => 'The number of connections', 'label' => 'Connections', 'vlabel' => 'Connections per ${graph_period}', 'title' => 'Number of Connections', 'info' => 'Number of connections to the LDAP server', 'scope' => "base" }, # dn: cn=Write,cn=Waiters,cn=Monitor # dn: cn=Read,cn=Waiters,cn=Monitor 'waiters' => { 'search' => 'cn=Waiters', 'filter' => '(|(cn=Write)(cn=Read))', 'desc' => "The current number of Waiters", 'label2' => {'write' => 'Write', 'read' => 'Read'}, 'vlabel' => "Waiters", 'title' => "Number of Waiters", 'info' => "The graph shows the number of Waiters" }, 'operations' => { 'search' => "cn=Operations", 'desc' => "Operations", 'vlabel' => 'Operations per ${graph_period}', 'label' => 'Operations', 'title' => "Operations", 'info' => "Number of completed LDAP operations" }, 'operations_diff' => { 'search' => "cn=Operations", 'desc' => "Operations deviance", 'vlabel' => 'Deviance', 'label' => 'Deviance', 'title' => "Operations deviance", 'info' => "Deviance between Initiated and Completed ops" } ); my $ldap = Net::LDAP::->new( 'ldapi://' ); my $sasl = Authen::SASL::->new( mechanism => 'EXTERNAL' ); my $mesg = $ldap->bind( undef, sasl => $sasl ); die "LDAP error code $mesg->code: $mesg->error\n", Net::LDAP::Util::ldap_error_text($mesg), "\n" if $mesg->code; my $basedn = 'cn=Monitor'; if (@ARGV and $ARGV[0] eq 'config') { foreach my $action (keys %OPS) { print "multigraph slapd2_$action\n"; print "graph_title $OPS{$action}->{title}\n"; print "graph_info $OPS{$action}->{info}\n"; print "graph_vlabel $OPS{$action}->{vlabel}\n"; print "graph_args --base 1000 -l 0\n"; print "graph_scale no\n"; print "graph_category OpenLDAP\n"; if ($OPS{$action}->{label2}) { foreach my $key (keys %{$OPS{$action}->{label2}}) { my $name = $action . "_" . $key; print "$name.label $OPS{$action}->{label2}->{$key}\n"; print "$name.type GAUGE\n"; } } elsif ($action =~ /^operations(?:_diff)?$/) { my $mesg = $ldap->search ( base => "$OPS{$action}->{search},$basedn" , scope => 'one' , deref => 'never' , filter => '(objectclass=*)' , attrs => [ 'monitorOpInitiated' , 'monitorOpCompleted' , 'cn' ] ); die "LDAP error code $mesg->code: $mesg->error\n", Net::LDAP::Util::ldap_error_text($mesg), "\n" if $mesg->code; while (my $e = $mesg->pop_entry) { my $cn = $e->get_value('cn'); my $name = $action .'_'. lc $cn; print "$name.label $cn\n"; print "$name.type DERIVE\n"; print "$name.min 0\n"; if ($action eq "operations") { print "$name.info The number of $cn operations\n"; } else { print "$name.info The difference between Initiated "; print "and Completed operations (should be 0)\n"; print "$name.warning 1\n"; } } } else { print "$action.label $OPS{$action}->{label}\n"; print "$action.type DERIVE\n"; print "$action.min 0\n"; } } } else { foreach my $action (keys %OPS) { my $searchdn = "$OPS{$action}->{search},$basedn"; my @searchattrs; if ($action =~ /^operations(_diff)?$/) { # We look for different parameters in Operations branch @searchattrs = ('monitorOpInitiated', 'monitorOpCompleted', 'cn'); } else { @searchattrs = ('monitorCounter', 'cn'); } my $mesg = $ldap->search ( base => "$OPS{$action}->{search},$basedn" , scope => $OPS{$action}->{scope} // 'one' , deref => 'never' , filter => $OPS{$action}->{filter} // '(objectclass=*)' , attrs => \@searchattrs ); die "LDAP error code $mesg->code: $mesg->error\n", Net::LDAP::Util::ldap_error_text($mesg), "\n" if $mesg->code; print "multigraph slapd2_$action\n"; while (my $e = $mesg->pop_entry) { my $cn = $e->get_value('cn'); if ($action =~ /operations(_diff)?$/) { if ($1) { my $opsInit = $e->get_value('monitorOpInitiated'); my $opsComp = $e->get_value('monitorOpCompleted'); printf "operations_diff_%s.value %d\n", lc $cn, ($opsInit - $opsComp); } else { printf "operations_%s.value %d\n", lc $cn, $e->get_value('monitorOpCompleted'); } } else { # Hotfix, must do for now printf "%s.value %d\n", (($action =~ /_/ or $action eq 'connections') ? lc $action : lc "${action}_${cn}"), $e->get_value('monitorCounter'); } } } } #$mesg = $ldap->search( base => 'cn=Monitor' # , scope => 'base' # , deref => 'never' # , attrs => $ldap->unbind(); #$mesg = # $ldap->search ( # base => $searchdn, # scope => $scope, # filter => $filter, # attrs => $searchattrs, # ); # #$mesg->code && die $mesg->error; # #my $max = $mesg->count; # #for (my $i = 0 ; $i < $max ; $i++) { # my $entry = $mesg->entry ($i); # my $cn = $entry->get_value('cn'); # if ($action =~ /operations(_diff)?$/) { # if ($1) { # my $opsInit = # $entry->get_value('monitorOpInitiated'); # my $opsComp = # $entry->get_value('monitorOpCompleted'); # print lc ("operations_diff_${cn}.value "); # print ($opsInit - $opsComp); # print "\n"; # } else { # print lc ("operations_${cn}.value "); # print $entry->get_value('monitorOpCompleted'), # "\n"; # } # } else { # # Hotfix, must do for now # if ($action =~ /_/ || $action eq 'connections') { # print lc ("${action}.value "); # } else { # print lc ("${action}_${cn}.value "); # } # print $entry->get_value('monitorCounter'), "\n"; # } #} #$ldap->unbind;