Makefile.am | 3
Makefile.in | 3
ldap/admin/src/scripts/dbmon.sh | 221 ++++++++++++++++++++++++++++++++++++++++
man/man8/dbmon.sh.8 | 56 ++++++++++
4 files changed, 283 insertions(+)
New commits:
commit 639be4567ee5c3b9cc3b632f60e7db3da14cc28c
Author: Noriko Hosoi <nhosoi(a)redhat.com>
Date: Fri Jul 25 13:15:48 2014 -0700
Ticket #47579 - add dbmon.sh
Description: Porting dbmon.sh from
https://github.com/richm/scripts/wiki/dbmon.sh
to the 389-ds-base package.
https://fedorahosted.org/389/ticket/47579
Porting was reviewed by the author rmeggins(a)redhat.com (Thank you, Rich!!)
diff --git a/Makefile.am b/Makefile.am
index 9c75c3a..cae8ec7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -135,6 +135,7 @@ CLEANFILES = dberrstrs.h ns-slapd.properties \
ldap/admin/src/scripts/schema-reload.pl ldap/admin/src/scripts/syntax-validate.pl \
ldap/admin/src/scripts/usn-tombstone-cleanup.pl ldap/admin/src/scripts/verify-db.pl \
ldap/admin/src/scripts/dbverify \
+ ldap/admin/src/scripts/dbmon.sh \
$(POSIX_WINSYNC_PLUGIN_LDIF)
clean-local:
@@ -370,6 +371,7 @@ sbin_SCRIPTS = ldap/admin/src/scripts/setup-ds.pl \
ldap/admin/src/scripts/verify-db.pl \
ldap/admin/src/scripts/dbverify \
ldap/admin/src/scripts/upgradedb \
+ ldap/admin/src/scripts/dbmon.sh \
wrappers/ldap-agent
bin_SCRIPTS = ldap/servers/slapd/tools/rsearch/scripts/dbgen.pl \
@@ -511,6 +513,7 @@ dist_man_MANS = man/man1/dbscan.1 \
man/man8/db2index.8 man/man8/db2index.pl.8 \
man/man8/ldif2db.8 man/man8/ldif2db.pl.8 \
man/man8/dbverify.8 man/man8/verify-db.pl.8 \
+ man/man8/dbmon.sh.8 \
man/man8/dn2rdn.8 man/man8/ldif2ldap.8 \
man/man8/restoreconfig.8 man/man8/saveconfig.8 \
man/man8/suffix2instance.8 man/man8/monitor.8 \
diff --git a/Makefile.in b/Makefile.in
index b2a97e4..2165d3e 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1609,6 +1609,7 @@ CLEANFILES = dberrstrs.h ns-slapd.properties \
ldap/admin/src/scripts/schema-reload.pl ldap/admin/src/scripts/syntax-validate.pl \
ldap/admin/src/scripts/usn-tombstone-cleanup.pl ldap/admin/src/scripts/verify-db.pl \
ldap/admin/src/scripts/dbverify \
+ ldap/admin/src/scripts/dbmon.sh \
$(POSIX_WINSYNC_PLUGIN_LDIF)
taskdir = $(datadir)@scripttemplatedir@
@@ -1780,6 +1781,7 @@ sbin_SCRIPTS = ldap/admin/src/scripts/setup-ds.pl \
ldap/admin/src/scripts/verify-db.pl \
ldap/admin/src/scripts/dbverify \
ldap/admin/src/scripts/upgradedb \
+ ldap/admin/src/scripts/dbmon.sh \
wrappers/ldap-agent
bin_SCRIPTS = ldap/servers/slapd/tools/rsearch/scripts/dbgen.pl \
@@ -1918,6 +1920,7 @@ dist_man_MANS = man/man1/dbscan.1 \
man/man8/db2index.8 man/man8/db2index.pl.8 \
man/man8/ldif2db.8 man/man8/ldif2db.pl.8 \
man/man8/dbverify.8 man/man8/verify-db.pl.8 \
+ man/man8/dbmon.sh.8 \
man/man8/dn2rdn.8 man/man8/ldif2ldap.8 \
man/man8/restoreconfig.8 man/man8/saveconfig.8 \
man/man8/suffix2instance.8 man/man8/monitor.8 \
diff --git a/ldap/admin/src/scripts/dbmon.sh b/ldap/admin/src/scripts/dbmon.sh
new file mode 100755
index 0000000..2371f4a
--- /dev/null
+++ b/ldap/admin/src/scripts/dbmon.sh
@@ -0,0 +1,221 @@
+#!/bin/sh
+# BEGIN COPYRIGHT BLOCK
+# 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, write to the Free Software Foundation, Inc., 59 Temple
+# Place, Suite 330, Boston, MA 02111-1307 USA.
+#
+# In addition, as a special exception, Red Hat, Inc. gives You the additional
+# right to link the code of this Program with code not covered under the GNU
+# General Public License ("Non-GPL Code") and to distribute linked
combinations
+# including the two, subject to the limitations in this paragraph. Non-GPL Code
+# permitted under this exception must only link to the code of this Program
+# through those well defined interfaces identified in the file named EXCEPTION
+# found in the source code files (the "Approved Interfaces"). The files of
+# Non-GPL Code may instantiate templates or use macros or inline functions from
+# the Approved Interfaces without causing the resulting work to be covered by
+# the GNU General Public License. Only Red Hat, Inc. may make changes or
+# additions to the list of Approved Interfaces. You must obey the GNU General
+# Public License in all respects for all of the Program code and other code used
+# in conjunction with the Program except the Non-GPL Code covered by this
+# exception. If you modify this file, you may extend this exception to your
+# version of the file, but you are not obligated to do so. If you do not wish to
+# provide this exception without modification, you must delete this exception
+# statement from your version and license this file solely under the GPL without
+# exception.
+#
+#
+# Copyright (C) 2014 Red Hat, Inc.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+DURATION=${DURATION:-0}
+INCR=${INCR:-1}
+HOST=${HOST:-localhost}
+PORT=${PORT:-389}
+BINDDN=${BINDDN:-"cn=directory manager"}
+BINDPW=${BINDPW:-"secret"}
+DBLIST=${DBLIST:-all}
+ldbmdn="cn=ldbm database,cn=plugins,cn=config"
+VERBOSE=${VERBOSE:-0}
+
+parseldif() {
+ awk -v dblist="$DBLIST" -v verbose=$VERBOSE -v
indexlist="$INDEXLIST" -F '[:,= ]+' '
+ function printary(ary) {
+ for (ii in ary) { print ii, "=", ary[ii] }
+ }
+ BEGIN {
+ pagesize=8192 ; CONVFMT="%.3f" ; OFMT=CONVFMT ;
SUBSEP=","
+ alldb=0
+ if (dblist == "all") {
+ alldb=1
+ } else {
+ split(dblist, dbnames)
+ for (key in dbnames) { val=dbnames[key] ; dbnames[tolower(val)]=val;
delete dbnames[key] }
+ }
+ allindex=0
+ if (indexlist == "all") {
+ allindex=1
+ } else {
+ split(indexlist, idxnames)
+ for (key in idxnames) { val=idxnames[key] ; idxnames[tolower(val)]=val;
delete idxnames[key] }
+ }
+ fn="entcur entmax entcnt dncur dnmax dncnt"
+ split(fn, fields)
+ havednstats=0
+ maxdbnamelen=0
+ }
+ /^[^ ]|^$/ {origline = $0; $0 = unwrapline; unwrapline = origline}
+ /^ / {sub(/^ /, ""); unwrapline = unwrapline $0; next}
+ /^nsslapd-dbcachesize/ { dbcachesize=$2 }
+ /^nsslapd-db-page-size/ { pagesize=$2 }
+ /^dbcachehitratio/ { dbhitratio=$2 }
+ /^dbcachepagein/ { dbcachepagein=$2 }
+ /^dbcachepageout/ { dbcachepageout=$2 }
+ /^nsslapd-db-page-ro-evict-rate/ { dbroevict=$2 }
+ /^nsslapd-db-pages-in-use/ { dbpages=$2 }
+ /^dn: cn=monitor, *cn=[a-zA-Z0-9][a-zA-Z0-9_\.\-]*, *cn=ldbm database,
*cn=plugins, *cn=config/ {
+ idxnum=-1
+ idxname=""
+ dbname=tolower($5)
+ if ((dbname in dbnames) || alldb) {
+ len=length(dbname) ; if (len > maxdbnamelen) { maxdbnamelen=len }
+ if (!(dbname in dbnames)) { dbnames[dbname] = dbname }
+ }
+ }
+ /^currententrycachesize/ { stats[dbname,"entcur"]=$2 }
+ /^maxentrycachesize/ { stats[dbname,"entmax"]=$2 }
+ /^currententrycachecount/ { stats[dbname,"entcnt"]=$2 }
+ /^currentdncachesize/ { stats[dbname,"dncur"]=$2 ; havednstats=1 }
+ /^maxdncachesize/ { stats[dbname,"dnmax"]=$2 }
+ /^currentdncachecount/ { stats[dbname,"dncnt"]=$2 }
+ /^dbfilename-/ {
+ #rhds
+ #dbfilename-3: userRoot/id2entry.db4
+ #sunds
+ #dbfilename-id2entry: /full/path/to/db/dbname/dbname_id2entry.dbX
+ if (dbname in dbnames) {
+ split($0, idxline, /[ :/.-]+/)
+ idxname=tolower(idxline[4])
+ dbn=tolower(idxline[3])
+ ilen=length(idxline)
+ sundbn=tolower(idxline[ilen-2])
+ sunidxname=tolower(idxline[2])
+ if ((dbn == dbname) && (allindex || (idxname in idxnames))) {
+ idxnum=idxline[2]
+ if (!(idxname in idxnames)) { idxnames[idxname] = idxname }
+ len = length(idxname)
+ if (len > idxmaxlen[dbn]) { idxmaxlen[dbn] = len }
+ } else if ((sundbn == dbname) && (allindex || (sunidxname in
idxnames))) {
+ idxname=sunidxname
+ idxnum=1 # no index number just index name
+ if (!(idxname in idxnames)) { idxnames[idxname] = idxname }
+ len = length(idxname)
+ if (len > idxmaxlen[sundbn]) { idxmaxlen[sundbn] = len }
+ } else {
+ # print "index", idxline[4], "not in idxnames"
+ }
+ } else {
+ # print "dbname", dbname, "not in dbnames"
+ }
+ }
+ /^dbfilepagein-/ { if (idxnum >= 0) {
idxstats[dbname,idxname,"pagein"] = $2 } }
+ /^dbfilepageout-/ { if (idxnum >= 0) {
idxstats[dbname,idxname,"pageout"] = $2 } }
+ END {
+ free=(dbcachesize-(pagesize*dbpages))
+ freeratio=free/dbcachesize
+ if (verbose > 1) {
+ print "# dbcachefree - free bytes in dbcache"
+ print "# free% - percent free in dbcache"
+ print "# roevicts - number of read-only pages dropped from cache to
make room for other pages"
+ print "# if this is non-zero, it means the dbcache is
maxed out and there is page churn"
+ print "# hit% - percent of requests that are served by cache"
+ print "# pagein - number of pages read into the cache"
+ print "# pageout - number of pages dropped from the cache"
+ }
+ print "dbcachefree", free, "free%", (freeratio*100),
"roevicts", dbroevict, "hit%", dbhitratio, "pagein",
dbcachepagein, "pageout", dbcachepageout
+ if (verbose > 1) {
+ print "# dbname - name of database instance - the row shows the
entry cache stats"
+ print "# count - number of entries in cache"
+ print "# free - number of free bytes in cache"
+ print "# free% - percent free in cache"
+ print "# size - average size of date in cache in bytes (current
size/count)"
+ if (havednstats) {
+ print "# DNcache - the line below the entry cache stats are the
DN cache stats"
+ print "# count - number of dns in dn cache"
+ print "# free - number of free bytes in dn cache"
+ print "# free% - percent free in dn cache"
+ print "# size - average size of dn in dn cache in bytes
(currentdncachesize/currentdncachecount)"
+ print "# under each db are the list of selected indexes
specified with INDEXLIST"
+ }
+ }
+ if (havednstats) { # make sure there is enough room for dbname:ent and
dbname:dn
+ maxdbnamelen += 4 # :ent
+ dbentext = ":ent"
+ dbdnext = ":dn "
+ } else {
+ dbentext = ""
+ dbdnext = ""
+ }
+ if (maxdbnamelen < 6) { # len of "dbname"
+ maxdbnamelen = 6
+ }
+
+ if (verbose > 0) {
+ fmtstr = sprintf("%%%d.%ds %%10.10s %%13.13s %%6.6s %%7.7s\n",
maxdbnamelen, maxdbnamelen)
+ printf fmtstr, "dbname", "count", "free",
"free%", "size"
+ }
+ for (dbn in dbnames) {
+ cur=stats[dbn,"entcur"]
+ max=stats[dbn,"entmax"]
+ cnt=stats[dbn,"entcnt"]
+ free=max-cur
+ freep=free/max*100
+ size=(cnt == 0) ? 0 : cur/cnt
+ fmtstr = sprintf("%%%d.%ds %%10d %%13d %%6.1f %%7.1f\n",
maxdbnamelen, maxdbnamelen)
+ printf fmtstr, dbnames[dbn] dbentext, cnt, free, freep, size
+ if (havednstats) {
+ dcur=stats[dbn,"dncur"]
+ dmax=stats[dbn,"dnmax"]
+ dcnt=stats[dbn,"dncnt"]
+ dfree=dmax-dcur
+ dfreep=dfree/dmax*100
+ dsize=(dcnt == 0) ? 0 : dcur/dcnt
+ printf fmtstr, dbnames[dbn] dbdnext, dcnt, dfree, dfreep, dsize
+ }
+ if (indexlist) {
+ len = idxmaxlen[dbn]
+ fmtstr = sprintf("%%%d.%ds %%%d.%ds pagein %%8d pageout
%%8d\n", maxdbnamelen, maxdbnamelen, len, len)
+ for (idx in idxnames) {
+ ipi = idxstats[dbn,idx,"pagein"]
+ ipo = idxstats[dbn,idx,"pageout"]
+ # not every db will have every index
+ if (ipi != "" && ipo != "") {
+ printf fmtstr, "+", idxnames[idx], ipi, ipo
+ }
+ }
+ }
+ }
+ }
+ '
+}
+
+dodbmon() {
+ while [ 1 ] ; do
+ date
+ ldapsearch -xLLL -h $HOST -p $PORT -D "$BINDDN" -w "$BINDPW"
-b "$ldbmdn" '(|(cn=config)(cn=database)(cn=monitor))' \
+ | parseldif
+ echo ""
+ sleep $INCR
+ done
+}
+
+dodbmon
diff --git a/man/man8/dbmon.sh.8 b/man/man8/dbmon.sh.8
new file mode 100644
index 0000000..b3e2e87
--- /dev/null
+++ b/man/man8/dbmon.sh.8
@@ -0,0 +1,56 @@
+.\" Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH DBMON.SH 8 "Jul 25, 2014"
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh disable hyphenation
+.\" .hy enable hyphenation
+.\" .ad l left justify
+.\" .ad b justify to both left and right margins
+.\" .nf disable filling
+.\" .fi enable filling
+.\" .br insert line break
+.\" .sp <n> insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+dbmon.sh - Directory Server script for monitoring database and entry cache usage
+.SH SYNOPSIS
+[INCR=num] [HOST=hostname] [PORT=num] [BINDDN=binddn] [BINDPW=password]
[DBLIST=databases] [INDEXLIST=indexes] [VERBOSE=num] dbmon.sh
+.SH DESCRIPTION
+dbmon.sh is a tool used to monitor database and entry cache usage. It is especially
useful for database cache and entry/dn cache tuning - how much space is left, is the cache
full, how much space on average do I need per entry/dn.
+.SH OPTIONS
+It doesn't take any command line arguments, so all options must be passed in as
environment variables.
+
+dbmon.sh will loop repeatedly showing the db information until it is killed or Ctrl-C
+
+All arguments are optional, but you will most likely have to provide BINDPW
+
+.TP
+.B \fBINCR\fR - show results every INCR seconds - default is 1 second
+.TP
+.B \fBHOST\fR - name of host or IP address - default is "localhost"
+.TP
+.B \fBPORT\fR - port number (LDAP not LDAPS) - default is 389
+.TP
+.B \fBBINDDN\fR - DN to use to bind - must have permission to read everything under
cn=config - default is cn=Directory Manager
+.TP
+.B \fBBINDPW\fR - password for BINDDN - default is secret
+.TP
+.B \fBDBLIST\fR - a list of databases you want to check - default is "all"; for
more than one, delimit with spaces e.g. DBLIST="one two three"
+.TP
+.B \fBINDEXLIST\fR - a list of indexes to show for each named database - default is none;
specify "all" for all indexes, or named e.g. INDEXLIST="id2entry
entryrdn"
+.TP
+.B \fBVERBOSE\fR - output level - 0 == suitable for parsing by a script - 1 == has column
headings - 2 == provides detailed descriptions of the data - default is 0
+
+.SH EXAMPLE
+INCR=1
HOST=ldap.example.com BINDDN="cn=directory manager"
BINDPW="secret" VERBOSE=2 dbmon.sh
+
+.SH AUTHOR
+dbmon.sh was written by the 389 Project.
+.SH "REPORTING BUGS"
+Report bugs to
http://bugzilla.redhat.com.
+.SH COPYRIGHT
+Copyright \(co 2014 Red Hat, Inc.