Browse Source

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 [email protected] (Thank you, Rich!!)
Noriko Hosoi 11 years ago
parent
commit
639be4567e
4 changed files with 283 additions and 0 deletions
  1. 3 0
      Makefile.am
  2. 3 0
      Makefile.in
  3. 221 0
      ldap/admin/src/scripts/dbmon.sh
  4. 56 0
      man/man8/dbmon.sh.8

+ 3 - 0
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 \

+ 3 - 0
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 \

+ 221 - 0
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

+ 56 - 0
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.