Index: trunk/server/common/oursrc/scripts-munin-plugins/389ds
===================================================================
--- trunk/server/common/oursrc/scripts-munin-plugins/389ds	(revision 2361)
+++ trunk/server/common/oursrc/scripts-munin-plugins/389ds	(revision 2361)
@@ -0,0 +1,295 @@
+#!/usr/bin/perl -w
+# -*- perl -*-
+# vim: ft=perl
+
+# Copyright Quentin Smith <quentin@mit.edu>
+# and Bjorn Ruberg <bjorn@ruberg.no>
+# Licenced under GPL v2
+#
+
+# We use one script for all monitoring.
+# This script may be symlinked with several names, all
+# performing different functions:
+# 389ds_statistics_bytes
+# 389ds_statistics_pdu
+# 389ds_statistics_referrals
+# 389ds_statistics_entries
+# 389ds_connections
+# 389ds_waiters
+# 389ds_operations
+# 389ds_operations_diff
+
+# Magic markers
+#%# family=auto
+#%# capabilities=autoconf suggest
+
+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
+
+# http://www.icir.org/fenner/mibs/extracted/DIRECTORY-SERVER-MIB-rfc2605.txt
+
+# The possible measurements
+my %ops =
+    (
+     # Only read Total
+     'connections' 
+     => {
+         'search' => 'cn=monitor',
+	 'searchattr' => 'totalconnections',
+         'desc'   => 'The number of connections',
+         'label'  => 'connections',
+         'vlabel' => 'connections/${graph_period}',
+         'title'  => 'Connection rate',
+         'info'   => 'Rate of connections to the LDAP server',
+         'scope'  => "base"
+         },
+     'connections_active' 
+     => {
+         'search' => 'cn=monitor',
+	 'searchattr' => 'currentconnections',
+         'desc'   => 'The number of connections',
+         'label'  => 'connections',
+         'vlabel' => 'connections',
+	 'type'   => 'GAUGE',
+         'title'  => 'Active connections',
+         'info'   => 'Number of connections to the LDAP server',
+         'scope'  => "base"
+         },
+     'binds'
+     => {
+	 'search' => 'cn=snmp,cn=monitor',
+         'label2' => {
+	     'anonymousbinds' => 'Anonymous',
+	     'unauthbinds'  => 'Unauthenticated',
+	     'simpleauthbinds' => 'Simple authentication',
+	     'strongauthbinds' => 'Strong authentication',
+	     'bindsecurityerrors' => 'Errors',
+	 },
+	 'desc'   => 'The number of binds',
+	 'vlabel' => 'binds/${graph_period}',
+	 'type'   => 'DERIVE',
+	 'title'  => 'Binds',
+	 'info'   => 'Number of binds to the LDAP server',
+	 'scope'  => "base"
+         },	 
+     'statistics_bytes'
+     => {
+         'search' => "cn=monitor",
+	 'searchattr' => 'bytessent',
+         'desc'   => "The number of bytes sent by the LDAP server.",
+         'vlabel' => 'bytes/${graph_period}',
+         'label'  => 'bytes',
+         'title'  => "Number of bytes sent",
+         'info'   => "The graph shows the number of bytes sent",
+	 'scope'  => "base"
+         },
+     # Entries
+     'statistics_entries'
+     => {
+         'search' => "cn=monitor",
+	 'searchattr' => 'entriessent',
+         'desc'   => "The number of entries sent by the LDAP server.",
+         'vlabel' => 'entries/${graph_period}',
+         'label'  => 'entries',
+         'title'  => "Number of LDAP Entries",
+         'info'   => "The graph shows the number of entries sent",
+	 'scope'  => "base"
+         },
+     'operations'
+     => {
+	 'search' => 'cn=snmp,cn=monitor',
+         'label2' => {
+	     readops        => 'Read',
+	     compareops     => 'Compare',
+	     addentryops    => 'Add entry',
+	     removeentryops => 'Remove entry',
+	     modifyentryops => 'Modify entry',
+	     modifyrdnops   => 'Modify RDN',
+	     listops        => 'List',
+	     searchops      => 'Search',
+	     onelevelsearchops => 'One-level search',
+	     wholesubtreesearchops => 'Subtree search',
+	     errors         => 'Error',
+	     securityerrors => 'Security error',
+	 },
+	 'desc'   => 'The number of operations',
+	 'vlabel' => 'ops/${graph_period}',
+	 'type'   => 'DERIVE',
+	 'title'  => 'Operations',
+	 'info'   => 'Number of completed LDAP operations',
+	 'scope'  => "base"
+         },
+     );
+
+# Config subroutine
+sub config {
+    my $action = shift;
+    if(!exists $ops{$action}) {
+	die "Unknown action specified: $action";
+    }
+    print <<EOF;
+graph_args --base 1000 -l 0
+graph_vlabel $ops{$action}->{'vlabel'}
+graph_title $ops{$action}->{'title'}
+graph_category 389-ds
+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 ",$ops{$action}->{'type'}||"DERIVE","\n";
+        }
+    } else {
+        print "$action.label $ops{$action}->{'label'}\n";
+        print "$action.type ",$ops{$action}->{'type'}||"DERIVE","\n";
+        print "$action.min 0\n";
+    }
+}
+
+sub 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;
+}
+
+# Determine action based on filename first
+
+if ($ARGV[0]) {
+    if ($ARGV[0] eq 'autoconf') {
+	autoconf();
+    } elsif ($ARGV[0] eq "suggest") {
+        print "$0\n";
+    } elsif ($ARGV[0] eq "config") {
+	foreach my $action (keys %ops) {
+	    print "multigraph 389ds_", $action, "\n";
+	    &config ($action);
+	}
+    }
+    exit 0;
+}
+
+# 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;
+}
+
+foreach my $action (keys %ops) {
+    print "multigraph 389ds_", $action, "\n";
+
+    # Default scope for LDAP searches. We'll change to other scopes if
+    # necessary.
+    $scope = "one";
+
+    my $searchdn = $ops{$action}->{'search'};
+    my $searchattrs;
+
+    if ($ops{$action}->{'label2'}) {
+        $searchattrs = [keys %{$ops{$action}->{'label2'}}];
+    } else {
+        $searchattrs = [$ops{$action}->{'searchattr'} || '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 @search = (
+                       base   => $searchdn,
+                       scope  => $scope,
+                       filter => $filter,
+                       attrs  => $searchattrs,
+        );
+
+    #use Data::Dumper; print Dumper({@search});
+
+    $mesg =
+        $ldap->search (@search);
+
+    $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 ($ops{$action}->{'label2'}) {
+    	foreach my $attr (keys %{$ops{$action}->{'label2'}}) {
+    	    print lc ("${action}_${attr}.value ");
+    	    print $entry->get_value($attr), "\n";
+    	}
+        } else {
+    	print lc ("${action}.value ");
+    	print $entry->get_value($ops{$action}->{'searchattr'} || 'monitorCounter'), "\n";
+        }
+    }
+}
+$ldap->unbind;
