diff --git a/plugins/other/slapd_ b/plugins/other/slapd_ new file mode 100755 index 00000000..62826478 --- /dev/null +++ b/plugins/other/slapd_ @@ -0,0 +1,281 @@ +#!/usr/bin/perl -w + +# Copyright Bjorn Ruberg +# Licenced under GPL v2 +# +# TODO: +# - Check for OpenLDAP version + +# We use one script for all monitoring. +# This script may be symlinked with several names, all +# performing different functions: +# slapd_statistics_bytes +# slapd_statistics_pdu +# slapd_statistics_other +# slapd_connections +# slapd_waiters +# slapd_operations +# slapd_operations_diff + +# Magic markers +#%# family=auto +#%# capabilities=autoconf suggest + +# use strict; +use Net::LDAP; +use Data::Dumper; + +use vars qw ( $config $param $act $scope $descr $cn $vlabel + $info $title ); + +# Change these to reflect your LDAP ACL. The given DN must have +# read access to the Monitor branch. +my $basedn = "cn=Monitor"; +my $server = "localhost"; +my $userdn = ($ENV{'binddn'} || ''); +my $userpw = ($ENV{'bindpw'} || ''); + +# Remember: bytes, pdu needs scope=base + +# The possible measurements +my %ops = + ('statistics_bytes' + => { + 'search' => "cn=Bytes,cn=Statistics", + 'desc' => "The number of bytes sent by the LDAP server.", + 'vlabel' => "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", + 'title' => "Number of PDUs sent", + 'info' => "The graph shows the number of PDUs sent", + 'scope' => "base" + }, + # Referrals + 'statistics_referrals' + => { + 'search' => "cn=Referrals,cn=Statistics", + 'attrs' => "Referrals", + 'desc' => "The number of Referrals sent by the LDAP server.", + 'vlabel' => "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", + 'attrs' => "Entries", + 'desc' => "The number of Entries sent by the LDAP server.", + 'vlabel' => "Entries", + 'title' => "Number of LDAP Entries", + 'info' => "The graph shows the number of entries sent", + 'scope' => "base" + }, + # Only read Current and Total + 'connections' + => { + 'search' => 'cn=Connections', + 'filter' => '(|(cn=Current)(cn=Total))', + 'desc' => 'The number of Connections', + 'label' => {'current' => 'Current', + 'total' => 'Total'}, + 'vlabel' => 'Connections', + 'title' => 'Number of Connections', + 'info' => 'Number of connections to the LDAP server' + }, + # 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 number of Waiters|", + 'label' => {'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", + 'title' => "Operations", + 'info' => "Number of completed LDAP operations" + }, + 'operations_diff' + => { + 'search' => "cn=Operations", + 'desc' => "Operations deviance", + 'vlabel' => "Deviance", + 'title' => "Operations deviance", + 'info' => "Deviance between Initiated and Completed ops" + } + ); + +# Config subroutine +sub config { + my $action = shift; + print <{'vlabel'} +graph_title $ops{$action}->{'title'} +graph_category OpenLDAP +graph_info $ops{$action}->{'info'} +EOF + + if ($ops{$action}->{'label'}) { + while (my ($key, $val) = each (%{$ops{$action}->{'label'}})) { + my $name = $action . "_" . $key; + print "$name.label $val\n"; + print "$name.type DERIVE\n"; + print "$name.min 0\n"; + } + } elsif ($action =~ /^operations(?:_diff)?$/) { + my $ldap = Net::LDAP->new ($server) or die "$@"; + $ldap->bind ($userdn, password => $userpw) or die "$@"; + my $searchdn = $ops{$action}->{'search'} . "," . $basedn; + my $mesg = + $ldap->search ( + base => $searchdn, + scope => 'one', + filter => '(objectclass=*)', + attrs => ['monitorOpInitiated', + 'monitorOpCompleted', + 'cn'], + ); + $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'); + $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}->{'vlabel'}\n"; + print "$action.type DERIVE\n"; + print "$action.min 0\n"; + } +} + +if ($ARGV[0]) { + if ($ARGV[0] eq 'autoconf') { + # Check for Net::LDAP + if (! eval "require Net::LDAP;") { + print "no (Net::LDAP not found)"; + exit 1; + } + # Check for LDAP version 3 + my $ldap = Net::LDAP->new ($server, version => 3) + or die "no (Needs LDAPv3)"; + $ldap->bind ($userdn, password => $userpw) + or die ("no (Can't log in, check env file)"); + } elsif ($ARGV[0] eq "config") { + if ($0 =~ /slapd_([\w\d_]+)$/) { + my $action = $1; + &config ($1); + } else { + die ("Can't run config without a symlinked name\n"); + } + } elsif ($ARGV[0] eq "suggest") { + print join ("\n", keys (%ops)), "\n"; + } + exit 0; +} + +# We won't run this without parameters. +die ("Can't run without a symlinked name\n") if $0 =~ /slapd_$/; + +# Default scope for LDAP searches. We'll change to other scopes if +# necessary. +$scope = "one"; + +# The filename is teh key +(my $action = $0) =~ s/^.*slapd_([\w\d_]+)$/$1/; + +# Net::LDAP variant +my $ldap = Net::LDAP->new ($server, version => 3) + or die "$@"; +$ldap->bind ($userdn,password => $userpw) + or die "$@"; + +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 $filter; +if ($ops{$action}->{'filter'}) { + $filter = "(&(objectclass=*)" . $ops{$action}->{'filter'} . ")"; +} else { + $filter = "(objectClass=*)"; +} + +if ($ops{$action}->{'scope'}) { + $scope = $ops{$action}->{'scope'}; +} + +my $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 =~ /_/) { + print lc ("${action}.value "); + } else { + print lc ("${action}_${cn}.value "); + } + print $entry->get_value('monitorCounter'), "\n"; + } +} +$ldap->unbind;