- Repository
- Munin (master)
- Last change
- 2018-08-17
- Graph Categories
- Family
- auto
- Capabilities
- Language
- Perl
- License
- GPL-2.0-only
- Authors
slapd_
Name
slapd_ - Plugin for monitoring LDAP servers
Applicable Systems
Any system that can communicate with a slapd service.
Configuration
Create a symlink to this script and place it below /etc/munin/plugins.
The name of the symlink determines the detail to be monitored:
- slapd_statistics_bytes - slapd_statistics_pdu - slapd_statistics_referrals - slapd_statistics_entries - slapd_connections - slapd_waiters - slapd_operations - slapd_operations_diff
Author
Copyright Bjørn Ruberg bjorn@ruberg.no
License
Licensed under GPL v2
Todo
- Check for OpenLDAP version
Magic Markers
# Magic markers #%# family=auto #%# capabilities=autoconf suggest
#!/usr/bin/perl -w
=head1 NAME
slapd_ - Plugin for monitoring LDAP servers
=head1 APPLICABLE SYSTEMS
Any system that can communicate with a slapd service.
=head1 CONFIGURATION
Create a symlink to this script and place it below /etc/munin/plugins.
The name of the symlink determines the detail to be monitored:
- slapd_statistics_bytes
- slapd_statistics_pdu
- slapd_statistics_referrals
- slapd_statistics_entries
- slapd_connections
- slapd_waiters
- slapd_operations
- slapd_operations_diff
=head1 AUTHOR
Copyright Bjørn Ruberg <bjorn@ruberg.no>
=head1 LICENSE
Licensed under GPL v2
=head1 TODO
- Check for OpenLDAP version
=head1 MAGIC MARKERS
# Magic markers
#%# family=auto
#%# capabilities=autoconf suggest
=cut
use strict;
my $ret = '';
if (! eval "require Net::LDAP;") {
$ret = "Net::LDAP not found";
}
use vars qw ( $config $param $act $scope $descr $cn $vlabel
$info $title $label);
# Change these to reflect your LDAP ACL. The given DN must have
# read access to the Monitor branch.
my $basedn = "cn=Monitor";
my $server = ($ENV{'server'} || 'localhost');
my $userdn = ($ENV{'binddn'} || '');
my $userpw = ($ENV{'bindpw'} || '');
# Remember: connections, 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 pr ${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 pr ${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 pr ${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 pr ${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 pr ${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 pr ${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"
}
);
# Config subroutine
sub config {
my $action = shift;
print <<EOF;
graph_args --base 1000 -l 0
graph_vlabel $ops{$action}->{'vlabel'}
graph_title $ops{$action}->{'title'}
graph_category auth
graph_info $ops{$action}->{'info'}
EOF
if ($ops{$action}->{'label2'}) {
while (my ($key, $val) = each (%{$ops{$action}->{'label2'}})) {
my $name = $action . "_" . $key;
print "$name.label $val\n";
print "$name.type GAUGE\n";
}
} elsif ($action =~ /^operations(?:_diff)?$/) {
my $ldap = Net::LDAP->new ($server)
or die "Failed to connect to server $server: $@";
my $mesg;
if ($userdn ne '') {
$mesg = $ldap->bind ($userdn, password => $userpw)
or die "Failed to bind with $userdn: $@";
} else {
$mesg = $ldap->bind
or die "Failed to bind anonymously: $@";
}
if ($mesg->code) {
die "Failed to bind: " . $mesg->error;
}
my $searchdn = $ops{$action}->{'search'} . "," . $basedn;
$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');
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";
}
}
$ldap->unbind;
} else {
print "$action.label $ops{$action}->{'label'}\n";
print "$action.type DERIVE\n";
print "$action.min 0\n";
}
}
# Determine action based on filename first
(my $action = $0) =~ s/^.*slapd_([\w\d_]+)$/$1/;
if ($ARGV[0]) {
if ($ARGV[0] eq 'autoconf') {
# Check for Net::LDAP
if ($ret) {
print "no ($ret)\n";
exit 0;
}
# Check for LDAP version 3
my $ldap = Net::LDAP->new ($server, version => 3)
or do { print "no ($@)\n"; exit 0; };
my $mesg;
if ($userdn ne '') {
$mesg = $ldap->bind ($userdn, password => $userpw)
or do { print "no ($@)\n"; exit 0; };
} else {
$mesg = $ldap->bind
or do { print "no ($@)\n"; exit 0; };
}
if ($mesg->code) {
print "no (" . $mesg->error . ")\n";
exit 0;
}
$mesg =
$ldap->search (
base => $basedn,
scope => 'one',
filter => '(objectClass=monitorServer)',
attrs => [
'cn',
],
);
if ($mesg->code) {
print "no (" . $mesg->error . ")\n";
exit 0;
}
print "yes\n";
exit 0;
} elsif ($ARGV[0] eq "config") {
if(!exists $ops{$action}) {
die "Unknown action specified: $action";
}
&config ($action);
} elsif ($ARGV[0] eq "suggest") {
print join ("\n", keys (%ops)), "\n";
}
exit 0;
}
# If $action isn't in %ops, we quit
if(!exists $ops{$action}) {
die "Unknown action specified: $action";
}
# Default scope for LDAP searches. We'll change to other scopes if
# necessary.
$scope = "one";
# Net::LDAP variant
my $ldap = Net::LDAP->new ($server, version => 3)
or die "Failed to connect to server $server: $@";
my $mesg;
if ($userdn ne '') {
$mesg = $ldap->bind ($userdn, password => $userpw)
or die "Failed to bind with $userdn: $@";
} else {
$mesg = $ldap->bind
or die "Failed to bind anonymously: $@";
}
if ($mesg->code) {
die "Failed to bind: " . $mesg->error;
}
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'};
}
$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;