From: Nadezhda Ivanova Date: Wed, 29 Jan 2025 14:26:10 +0000 (+0200) Subject: ITS#9186 Implement result code counters X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=086296d5fa52cd193085d1d5db5204236e540f3d;p=thirdparty%2Fopenldap.git ITS#9186 Implement result code counters --- diff --git a/contrib/ConfigOIDs b/contrib/ConfigOIDs index ce90eff750..e6ee90a90e 100644 --- a/contrib/ConfigOIDs +++ b/contrib/ConfigOIDs @@ -11,3 +11,4 @@ OLcfgCt{Oc|At}:8 datamorph OLcfgCt{Oc|At}:9 variant OLcfgCt{Oc|At}:10 alias OLcfgCt{Oc|At}:11 dsaschema +OLcfgCt{Oc|At}:12 resultstats diff --git a/contrib/slapd-modules/resultstats/.gitignore b/contrib/slapd-modules/resultstats/.gitignore new file mode 100644 index 0000000000..8ba213962e --- /dev/null +++ b/contrib/slapd-modules/resultstats/.gitignore @@ -0,0 +1,2 @@ +servers +clients diff --git a/contrib/slapd-modules/resultstats/Makefile b/contrib/slapd-modules/resultstats/Makefile new file mode 100644 index 0000000000..e6e6506038 --- /dev/null +++ b/contrib/slapd-modules/resultstats/Makefile @@ -0,0 +1,80 @@ +# $OpenLDAP$ +# This work is part of OpenLDAP Software . +# +# Copyright 1998-2024 The OpenLDAP Foundation. +# Copyright 2024 Symas Corp. All Rights Reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted only as authorized by the OpenLDAP +# Public License. +# +# A copy of this license is available in the file LICENSE in the +# top-level directory of the distribution or, alternatively, at +# . + +LDAP_SRC = ../../.. +LDAP_BUILD = $(LDAP_SRC) +LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd +LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \ + $(LDAP_BUILD)/libraries/liblber/liblber.la + +SRCDIR = ./ + +PLAT = UNIX +NT_LIB = -L$(LDAP_BUILD)/servers/slapd -lslapd +NT_LDFLAGS = -no-undefined -avoid-version +UNIX_LDFLAGS = -version-info $(LTVER) + +LIBTOOL = $(LDAP_BUILD)/libtool +INSTALL = /usr/bin/install +CC = gcc +OPT = -g -O0 +DEFS = -DSLAPD_OVER_RESULTSTATS=SLAPD_MOD_DYNAMIC +INCS = $(LDAP_INC) +LIBS = $($(PLAT)_LIB) $(LDAP_LIB) +LD_FLAGS = $(LDFLAGS) $($(PLAT)_LDFLAGS) -rpath $(moduledir) -module + +PROGRAMS = resultstats.la +MANPAGES = slapo_resultstats.5 +LTVER = 0:0:0 +CLEAN = *.o *.lo *.la .libs +prefix=/usr/local +exec_prefix=$(prefix) +ldap_subdir=/openldap + +libdir=$(exec_prefix)/lib +libexecdir=$(exec_prefix)/libexec +moduledir = $(libexecdir)$(ldap_subdir) +mandir = $(exec_prefix)/share/man +man5dir = $(mandir)/man5 + +.SUFFIXES: .c .o .lo + +.c.lo: + $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $< + +all: $(PROGRAMS) + +d := +sp := +dir := tests +include $(dir)/Rules.mk + + +resultstats.la: resultstats.lo + $(LIBTOOL) --mode=link $(CC) $(LD_FLAGS) -o $@ $? $(LIBS) + +clean: + rm -rf $(CLEAN) + +install: install-lib install-man + +install-lib: $(PROGRAMS) + mkdir -p $(DESTDIR)$(moduledir) + for p in $(PROGRAMS) ; do \ + $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \ + done + +install-man: $(MANPAGES) + mkdir -p $(DESTDIR)$(man5dir) + $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir) diff --git a/contrib/slapd-modules/resultstats/resultstats.c b/contrib/slapd-modules/resultstats/resultstats.c new file mode 100644 index 0000000000..d24102ffe4 --- /dev/null +++ b/contrib/slapd-modules/resultstats/resultstats.c @@ -0,0 +1,515 @@ +/* resultstats.c - gather result code statistics per operation */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2005-2025 The OpenLDAP Foundation. + * Copyright 2025 Symas Corp. All Rights Reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by Nadezhda Ivanova for Symas Corp. for inclusion + * in OpenLDAP Software. + */ + +#include "portable.h" + +#ifdef SLAPD_OVER_RESULTSTATS + +#include + +#include +#include +#include +#include + +#include "slap.h" +#include "slap-config.h" +#include "lutil.h" +#include "back-monitor/back-monitor.h" + +static slap_overinst resultstats; + +#define STATS_SIZE LDAP_OTHER+2 + +static struct resultstats_ops_t { + struct berval rdn; + struct berval nrdn; +} resultstats_op[] = { + { BER_BVC( "cn=Bind" ), BER_BVC( "cn=bind" ), }, + { BER_BVC( "cn=Unbind" ), BER_BVC( "cn=unbind" ), }, + { BER_BVC( "cn=Search" ), BER_BVC( "cn=search" ), }, + { BER_BVC( "cn=Compare" ), BER_BVC( "cn=compare" ), }, + { BER_BVC( "cn=Modify" ), BER_BVC( "cn=modify" ), }, + { BER_BVC( "cn=Modrdn" ), BER_BVC( "cn=modrdn" ), }, + { BER_BVC( "cn=Add" ), BER_BVC( "cn=add" ), }, + { BER_BVC( "cn=Delete" ), BER_BVC( "cn=delete" ), }, + { BER_BVC( "cn=Abandon" ), BER_BVC( "cn=abandon" ), }, + { BER_BVC( "cn=Extended" ), BER_BVC( "cn=extended" ), }, + { BER_BVNULL, BER_BVNULL } +}; + +typedef struct resultstats_t { + uintptr_t stats[ SLAP_OP_LAST ][ STATS_SIZE ]; + struct berval monitor_ndn; + struct berval rslt_rdn; + struct berval mss_ndn; + monitor_subsys_t *mss; +} resultstats_t; + + +static int resultstats_monitor_db_init( void ); +static int resultstats_monitor_db_open( BackendDB *be ); +static int resultstats_monitor_db_close( BackendDB *be ); +//static int resultstats_monitor_db_destroy( BackendDB *be ); + +static AttributeDescription *ad_olmResultCodeStat; +static ObjectClass *oc_olmResultStatOperation; +static ObjectClass *oc_monitorContainer; + + +static struct { + char *desc; + ObjectClass **ocp; +} s_oc[] = { + { "( OLcfgCtOc:12.1 " + "NAME ( 'olmResultStatOperation' ) " + "SUP monitoredObject " + "MAY ( " + "olmResultCodeStat" + " ) )", + &oc_olmResultStatOperation }, + + { NULL } +}; + +static struct { + char *desc; + AttributeDescription **adp; +} s_ad[] = { + { "( OLcfgCtAt:12.1 " + "NAME 'olmResultCodeStat' " + "DESC 'Number of times an LDAP code result has been sent for this operation type' " + "EQUALITY integerMatch " + "ORDERING integerOrderingMatch " + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 )", + &ad_olmResultCodeStat }, + + { NULL } +}; + +static AttributeDescription *ads[ STATS_SIZE ]; + +static int +resultstats_monitor_ops_update( + Operation *op, + SlapReply *rs, + Entry *e, + void *priv ) +{ + uintptr_t *stats = (uintptr_t *)priv; + int i, rc; + for ( i = 0; i < STATS_SIZE; i++ ) { + Attribute *a; + AttributeDescription *ad = NULL; + char name_buf[ BUFSIZ ]; + char val_buf[ BUFSIZ ]; + const char *text; + struct berval bv; + unsigned long int value = __atomic_load_n( &stats[i], __ATOMIC_RELAXED ); + if ( value == 0 ) { + continue; + } + + /* TODO This should be optimised by maintaining the attributes in the entry sorted by code, + and avoid searching through the full list every time */ + ad = ads[i]; + if ( ad == NULL ) { + if ( i <= LDAP_OTHER ) + snprintf( name_buf, sizeof( name_buf ), "olmResultCodeStat;x-resultcode-%d", i ); + else + snprintf( name_buf, sizeof( name_buf ), "olmResultCodeStat;x-resultcode-more"); + + rc = slap_str2ad( name_buf, &ad, &text ); + if ( rc != 0 ) { + Debug( LDAP_DEBUG_ANY, "resultstats_monitor_ops_update: " + "unable to find attribute description %s \n ", name_buf ); + return 0; + } + } + ads[i] = ad; + bv.bv_val = val_buf; + bv.bv_len = snprintf( val_buf, sizeof( val_buf ), "%lu", value ); + a = attr_find( e->e_attrs, ad ); + if ( a != NULL ) { + ber_bvreplace( &a->a_vals[ 0 ], &bv ); + } else { + attr_merge_one( e, ad, &bv, NULL ); + } + } + return SLAP_CB_CONTINUE; +} + +static int +resultstats_monitor_initialize( void ) +{ + static int resultstats_monitor_initialized = 0; + int code, i; + + if ( backend_info( "monitor" ) == NULL ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_initialize: resultstats overlay requires cn=monitor"); + return -1; + } + + if ( resultstats_monitor_initialized++ ) { + return 0; + } + + ad_define_option( "x-resultcode-", __FILE__, __LINE__ ); + for ( i = 0; s_ad[i].desc != NULL; i++ ) { + code = register_at( s_ad[i].desc, s_ad[i].adp, 0 ); + if ( code ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_initialize: register_at #%d failed\n", i ); + return code; + } + } + + for ( i = 0; s_oc[i].desc != NULL; i++ ) { + code = register_oc( s_oc[i].desc, s_oc[i].ocp, 0 ); + if ( code ) { + Debug( LDAP_DEBUG_ANY, + "resultstat_monitor_initialize: register_oc #%d failed\n", i ); + return code; + } + } + + oc_monitorContainer = oc_find( "monitorContainer" ); + if ( !oc_monitorContainer ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_initialize: failed to find objectClass (monitorContainer)\n" ); + return 5; + } + return 0; +} + +static int +resultstats_monitor_register_entries( monitor_extra_t *mbe, + resultstats_t *rslt, + monitor_subsys_t *ms, + Entry *parent ) +{ + int i, rc; + Entry *e; + for ( i = 0; i < SLAP_OP_LAST; i++ ) { + monitor_callback_t *cb; + + e = mbe->entry_stub( &ms->mss_dn, &ms->mss_ndn, + &resultstats_op[i].rdn, + oc_olmResultStatOperation, NULL, NULL ); + if ( e == NULL ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_register_entries: " + "unable to create entry \"%s,%s\"\n", + resultstats_op[i].rdn.bv_val, + rslt->monitor_ndn.bv_val ); + return( -1 ); + } + cb = ch_calloc( sizeof( monitor_callback_t ), 1 ); + cb->mc_update = resultstats_monitor_ops_update; + cb->mc_private = (void *)rslt->stats[i]; + + rc = mbe->register_entry( e, cb, ms, 0 ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_register_entries: " + "unable to register entry \"%s\" for monitoring\n", + e->e_name.bv_val ); + ch_free( cb ); + entry_free( e ); + return rc; + } + entry_free( e ); + } + return 0; +} + +static int +resultstats_monitor_db_init( void ) +{ + return resultstats_monitor_initialize(); +} + +static int +resultstats_monitor_mss_init( + BackendDB *be, + monitor_subsys_t *ms ) +{ + slap_overinst *on = (slap_overinst *)ms->mss_private; + resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private; + monitor_extra_t *mbe; + + Entry *parent; + int rc; + + assert( be != NULL ); + mbe = (monitor_extra_t *) be->bd_info->bi_extra; + + parent = mbe->entry_stub( &rslt->monitor_ndn, &rslt->monitor_ndn, + &rslt->rslt_rdn, oc_monitorContainer, NULL, NULL ); + if ( parent == NULL ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_mss_init: " + "unable to create entry \"%s,%s\"\n", + rslt->rslt_rdn.bv_val, + rslt->monitor_ndn.bv_val ); + return( -1 ); + } + + ber_dupbv( &ms->mss_dn, &parent->e_name ); + ber_dupbv( &ms->mss_ndn, &parent->e_nname ); + ber_dupbv( &ms->mss_rdn, &rslt->rslt_rdn ); + + rc = mbe->register_entry( parent, NULL, ms, MONITOR_F_PERSISTENT_CH ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_mss_init: " + "unable to register entry \"%s,%s\"\n", + ms->mss_rdn.bv_val, + ms->mss_ndn.bv_val ); + entry_free( parent ); + return ( -1 ); + } + + rc = resultstats_monitor_register_entries( mbe, rslt, ms, parent ); + + entry_free( parent ); + + return rc; +} + +static int +resultstats_monitor_mss_destroy ( + BackendDB *be, + monitor_subsys_t *ms ) +{ + if ( ms->mss_ndn.bv_len > 0 ) { + ch_free( ms->mss_ndn.bv_val ); + } + if ( ms->mss_dn.bv_len > 0 ) { + ch_free( ms->mss_dn.bv_val ); + } + return 0; +} + +static int +resultstats_monitor_db_open( BackendDB *be ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private; + BackendInfo *bi; + int rc = 0; + monitor_extra_t *mbe; + Entry * parent; + + /* check if monitor is configured and usable */ + bi = backend_info( "monitor" ); + if ( !bi || !bi->bi_extra ) { + return -1; + } + mbe = bi->bi_extra; + + /* don't bother if monitor is not configured */ + if ( !mbe->is_configured() ) { + Debug( LDAP_DEBUG_CONFIG, "resultstats_monitor_db_open: " + "monitoring disabled; " + "configure monitor database to enable\n" ); + return -1; + } + + BER_BVZERO( &rslt->monitor_ndn ); + if ( ( rc = mbe->register_overlay( be, on, &rslt->monitor_ndn ) ) != 0 ) { + return rc; + } + parent = mbe->entry_stub( &rslt->monitor_ndn, &rslt->monitor_ndn, + &rslt->rslt_rdn, oc_monitorContainer, NULL, NULL ); + if ( parent == NULL ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_mss_init: " + "unable to create entry \"%s,%s\"\n", + rslt->rslt_rdn.bv_val, + rslt->monitor_ndn.bv_val ); + return( -1 ); + } + + ber_dupbv( &rslt->mss_ndn, &parent->e_nname ); + /* Check if the subsystem already exsists. This can happen if the overlay + has previously added and removed. For now it is safe to assume + that the dn will be unique, as databases cannot be removed. + This should be re-done when we enable database removal and fix monitor + so that subsystems can be unregistered */ + rslt->mss = monitor_back_get_subsys_by_dn( &rslt->mss_ndn, 0 ); + if ( rslt->mss == NULL ) { + /* this will leak at monitor_db_destroy, but it can't be helped */ + rslt->mss = (monitor_subsys_t *)ch_calloc( 1, sizeof( monitor_subsys_t ) ); + rslt->mss->mss_name = "Result code statistics"; + rslt->mss->mss_flags = MONITOR_F_PERSISTENT_CH; + rslt->mss->mss_open = resultstats_monitor_mss_init; + rslt->mss->mss_destroy = resultstats_monitor_mss_destroy; + rslt->mss->mss_private = on; + + if ( mbe->register_subsys_late( rslt->mss ) ) { + Debug( LDAP_DEBUG_ANY, + "resultsats_monitor_db_open: " + "failed to register result statistics subsystem" ); + return -1; + } + } else { + rslt->mss->mss_private = on; + rc = mbe->register_entry( parent, NULL, rslt->mss, MONITOR_F_PERSISTENT_CH ); + if ( rc != LDAP_SUCCESS ) { + Debug( LDAP_DEBUG_ANY, + "resultstats_monitor_db_open: " + "unable to register entry \"%s,%s\"\n", + rslt->mss->mss_rdn.bv_val, + rslt->mss->mss_ndn.bv_val ); + entry_free( parent ); + return ( -1 ); + } + rc = resultstats_monitor_register_entries( mbe, rslt, rslt->mss, parent ); + } + entry_free( parent ); + + return rc; +} + +static int +resultstats_monitor_db_close( BackendDB *be ) +{ + + slap_overinst *on = (slap_overinst *)be->bd_info; + resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private; + monitor_extra_t *mbe; + BackendInfo *mi = backend_info( "monitor" ); + + if ( mi && mi->bi_extra ) { + int i; + mbe = mi->bi_extra; + for ( i = 0; i < SLAP_OP_LAST; i++ ) { + struct berval bv; + char buf[ BACKMONITOR_BUFSIZE ]; + bv.bv_len = snprintf( buf, sizeof( buf ), "%s,%s", + resultstats_op[i].nrdn.bv_val, + rslt->mss_ndn.bv_val ); + bv.bv_val = buf; + mbe->unregister_entry( &bv ); + + } + mbe->unregister_entry( &rslt->mss_ndn ); + } + if ( !BER_BVISNULL(&rslt->mss_ndn) ) { + ch_free( rslt->mss_ndn.bv_val ); + BER_BVZERO( &rslt->mss_ndn ); + } + /* Make sure this does not point to a non-existent overlay instance */ + rslt->mss->mss_private = NULL; + return 0; +} + +static int +resultstats_response( Operation *op, SlapReply *rs ) +{ + slap_overinst *on = (slap_overinst *)op->o_bd->bd_info; + resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private; + int code, opidx; + + /* skip internal ops */ + if ( rs->sr_type != REP_RESULT || op->o_do_not_cache ) { + return SLAP_CB_CONTINUE; + } + + code = slap_map_api2result( rs ); + if ( code > LDAP_OTHER ) + code = LDAP_OTHER+1; + + opidx = slap_req2op( op->o_tag ); + __atomic_fetch_add( &(rslt->stats[opidx][code]), 1, __ATOMIC_RELAXED ); + + return SLAP_CB_CONTINUE; +} + +static int +resultstats_db_init( BackendDB *be, ConfigReply *cr ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + resultstats_t *rslt; + + rslt = (resultstats_t *)ch_calloc( 1, sizeof( resultstats_t ) ); + + on->on_bi.bi_private = (void *)rslt; + ber_str2bv( "cn=Result Stats", 0, 1, + &rslt->rslt_rdn ); + return resultstats_monitor_db_init(); +} + +static int +resultstats_db_open( BackendDB *be, ConfigReply *cr ) +{ + return resultstats_monitor_db_open( be ); +} + +static int +resultstats_db_destroy( BackendDB *be, ConfigReply *cr ) +{ + slap_overinst *on = (slap_overinst *)be->bd_info; + resultstats_t *rslt = (resultstats_t *)on->on_bi.bi_private; + if ( rslt->rslt_rdn.bv_len > 0 ) { + ch_free( rslt->rslt_rdn.bv_val ); + } + ch_free( rslt ); + return 0; +} + +static int +resultstats_db_close( BackendDB *be, ConfigReply *cr ) +{ + return resultstats_monitor_db_close( be ); +} + +int +resultstats_initialize( void ) +{ + int code; + + resultstats.on_bi.bi_type = "resultstats"; + resultstats.on_bi.bi_db_init = resultstats_db_init; + resultstats.on_bi.bi_db_open = resultstats_db_open; + resultstats.on_bi.bi_db_destroy = resultstats_db_destroy; + resultstats.on_bi.bi_db_close = resultstats_db_close; + resultstats.on_bi.bi_flags = SLAPO_BFLAG_SINGLE; + resultstats.on_response = resultstats_response; + + code = resultstats_monitor_initialize(); + if ( code != 0) + return code; + + return overlay_register( &resultstats ); +} + +#if SLAPD_OVER_RESULTSTATS == SLAPD_MOD_DYNAMIC +int +init_module( int argc, char *argv[] ) +{ + return resultstats_initialize(); +} +#endif /* SLAPD_OVER_RESULTSTATS == SLAPD_MOD_DYNAMIC */ + +#endif /* SLAPD_OVER_RESULTSTATS */ diff --git a/contrib/slapd-modules/resultstats/slapo-resultstats.5 b/contrib/slapd-modules/resultstats/slapo-resultstats.5 new file mode 100644 index 0000000000..fabb91793b --- /dev/null +++ b/contrib/slapd-modules/resultstats/slapo-resultstats.5 @@ -0,0 +1,96 @@ +.TH SLAPO-RESULTSTATS 5 "RELEASEDATE" "OpenLDAP LDVERSION" +.\" Copyright 2005-2025 The OpenLDAP Foundation All Rights Reserved. +.\" Copying restrictions apply. See COPYRIGHT/LICENSE. +.\" $OpenLDAP$ +.SH NAME +slapo\-resultstats \- Operation result statistics for slapd +.SH SYNOPSIS +ETCDIR/slapd.conf +.SH DESCRIPTION +The +.B resultstats +overlay adds per-operation counters for LDAP operation results and +exposes them via +.B cn=monitor. +Result codes from +.B LdapSuccess(0) +to +.B LdapOther(80) +are counted separately as individual values. Codes higher than that are counted +together. +.P +The overlay can be configured on a particular database, or on the front-end, +or both. When configured on the front-end, it will count operations +processed by all databases. When configured on a particular databases, +only operations processed by that database will be counted. Monitoring +must be enabled for the database (mdb databases have monitoring on by default), +and +.B cn=monitor +bust be configured. +.P +.B Note: +Bind request responses will be counted for the database where +the identity used is located. For example, binding with the rootdn of +the database will be counted for that database, but anonymous binds +will be counted only at the front-end level. Failed schema checks +(such as undefinedAttributeType) cannot be counted at all, since they +happen before a database is chosen. +.P +When enabled, the resultstats overlay will add a +.B cn=Result Stats +container under the database entry in cn=monitor, and under it entries +for all types of LDAP operations. The statistic is exposed via the +.B olmResultCodeStat +attribute with the +.B x-resultcode- +tag. Errors higher than 80 will be exposed under the +.B x-resultcode-more +tag. Only result codes with counters higher than 0 +will be displayed. + +.SH EXAPLES +This is an example entry for search requests, where we have 1 successful operation and one that returned +.B NoSuchObject(32) +.P +.nf +dn: cn=Search,cn=Result Stats,cn=database 2,cn=databases,cn=monitor +objectClass: olmResultStatOperation +olmResultCodeStat;x-resultcode-0: 1 +olmResultCodeStat;x-resultcode-32: 1 +entryDN: cn=Search,cn=Result Stats,cn=database 2,cn=databases,cn=monitor + +.SH CONFIGURATION +This overlay does not have any configuration directives. +.SH Configuration Example +.nf +moduleload resultstats.la + +database config +rootdn cn=config +rootpw password + +#resultstats configured on the front-end +#entries will appear under cn=Result Stats,cn=database 0,cn=databases,cn=monitor +overlay resultstats + +database monitor + +database mdb + +suffix dc=example,dc=com +rootdn cn=admin,dc=example,dc=com +rootpw password + +#resultstats configured on this database +#entries will appear under cn=Result Stats,cn=database 2,cn=databases,cn=monitor +overlay resultstats + +.SH FILES +.TP +ETCDIR/slapd.conf +default slapd configuration file +.SH SEE ALSO +.BR slapd.conf (5), +.BR slapd\-monitor (5), +.SH AUTHOR +Nadezhda Ivanova, on behalf of Symas Corp. diff --git a/contrib/slapd-modules/resultstats/tests/.gitignore b/contrib/slapd-modules/resultstats/tests/.gitignore new file mode 100644 index 0000000000..92538319be --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/.gitignore @@ -0,0 +1,4 @@ +progs +schema +testdata +testrun diff --git a/contrib/slapd-modules/resultstats/tests/Rules.mk b/contrib/slapd-modules/resultstats/tests/Rules.mk new file mode 100644 index 0000000000..9884b7de3c --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/Rules.mk @@ -0,0 +1,23 @@ +sp := $(sp).x +dirstack_$(sp) := $(d) +d := $(dir) + +.PHONY: test + +CLEAN += clients servers tests/progs tests/schema tests/testdata tests/testrun + +test: all clients servers tests/progs + +test: + cd tests; \ + SRCDIR=$(abspath $(LDAP_SRC)) \ + LDAP_BUILD=$(abspath $(LDAP_BUILD)) \ + TOPDIR=$(abspath $(SRCDIR)) \ + LIBTOOL=$(abspath $(LIBTOOL)) \ + $(abspath $(SRCDIR))/tests/run test001-resultstats + +servers clients tests/progs: + ln -s $(abspath $(LDAP_BUILD))/$@ $@ + +d := $(dirstack_$(sp)) +sp := $(basename $(sp)) diff --git a/contrib/slapd-modules/resultstats/tests/data/slapd.conf b/contrib/slapd-modules/resultstats/tests/data/slapd.conf new file mode 100644 index 0000000000..0df804c9ca --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/data/slapd.conf @@ -0,0 +1,33 @@ +# provider slapd config -- for testing +# $OpenLDAP$ + +include @SCHEMADIR@/core.schema +include @SCHEMADIR@/cosine.schema +include @SCHEMADIR@/inetorgperson.schema + +pidfile @TESTDIR@/slapd.m.pid + +argsfile @TESTDIR@/slapd.m.args + +#mod#modulepath ../servers/slapd/back-@BACKEND@/ +#mod#moduleload back_@BACKEND@.la + +moduleload ../resultstats.la + +pidfile @TESTDIR@/slapd.1.pid + +####################################################################### +# database definitions +####################################################################### +database @BACKEND@ +suffix "dc=example,dc=com" + +directory @TESTDIR@/db.1.a +# root or superuser +rootdn "cn=Manager,dc=example,dc=com" +rootpw secret + +database config +include @TESTDIR@/configpw.conf + +database monitor diff --git a/contrib/slapd-modules/resultstats/tests/data/test001-add.ldif b/contrib/slapd-modules/resultstats/tests/data/test001-add.ldif new file mode 100644 index 0000000000..6d9f0de769 --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/data/test001-add.ldif @@ -0,0 +1,99 @@ +dn: dc=example,dc=com +objectClass: top +objectClass: organization +objectClass: domainRelatedObject +objectClass: dcObject +dc: example +l: Anytown, Michigan +st: Michigan +o: Example, Inc. +o: EX +o: Ex. +description: The Example, Inc. at Anytown +postalAddress: Example, Inc. $ 535 W. William St. $ Anytown, MI 48109 $ US +telephoneNumber: +1 313 555 1817 +associatedDomain: example.com + +dn: ou=people,dc=example,dc=com +objectClass: organizationalUnit +ou: people +description: All domain members + +dn: cn=user01,ou=people,dc=example,dc=com +cn: user01 +objectClass: inetOrgPerson +userPassword:: UEBzc3cwcmQ= +roomNumber: 101 +carLicense: 1234ha +sn: user01 +mobile: 12345678 +mobile: 987654321 +ou: people +preferredLanguage: English +description: This is user user01 + +dn: cn=user02,ou=people,dc=example,dc=com +cn: user02 +objectClass: inetOrgPerson +userPassword:: UEBzc3cwcmQ= +roomNumber: 102 +carLicense: 1234hb +sn: user02 +mobile: 12345678 +mobile: 987654321 +ou: people +preferredLanguage: English +description: This is user user02 + +dn: cn=user03,ou=people,dc=example,dc=com +cn: user03 +objectClass: inetOrgPerson +userPassword:: UEBzc3cwcmQ= +roomNumber: 103 +carLicense: 1234hc +sn: user03 +mobile: 12345678 +mobile: 987654321 +ou: people +preferredLanguage: English +description: This is user user03 + +dn: cn=user04,ou=people,dc=example,dc=com +cn: user04 +objectClass: inetOrgPerson +userPassword:: UEBzc3cwcmQ= +roomNumber: 104 +carLicense: 1234ha +sn: user04 +mobile: 12345678 +mobile: 987654321 +ou: people +preferredLanguage: English +description: This is user user04 + +dn: cn=user05,ou=people,dc=example,dc=com +cn: user05 +objectClass: inetOrgPerson +userPassword:: UEBzc3cwcmQ= +roomNumber: 105 +carLicense: 1234hb +sn: user05 +mobile: 12345678 +mobile: 987654321 +ou: people +preferredLanguage: English +description: This is user user05 + +dn: cn=user06,ou=people,dc=example,dc=com +cn: user06 +objectClass: inetOrgPerson +userPassword:: UEBzc3cwcmQ= +roomNumber: 106 +carLicense: 1234hc +sn: user06 +mobile: 12345678 +mobile: 987654321 +ou: people +preferredLanguage: English +description: This is user user06 + diff --git a/contrib/slapd-modules/resultstats/tests/data/test001-bind.ldif b/contrib/slapd-modules/resultstats/tests/data/test001-bind.ldif new file mode 100644 index 0000000000..e751812e20 --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/data/test001-bind.ldif @@ -0,0 +1,6 @@ +dn: cn=Bind,cn=Result Stats,cn=frontend,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Bind +olmResultCodeStat;x-resultcode-0: 28 +olmResultCodeStat;x-resultcode-49: 5 + diff --git a/contrib/slapd-modules/resultstats/tests/data/test001-modify.ldif b/contrib/slapd-modules/resultstats/tests/data/test001-modify.ldif new file mode 100644 index 0000000000..5195394581 --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/data/test001-modify.ldif @@ -0,0 +1,6 @@ +dn: cn=Modify,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Modify +olmResultCodeStat;x-resultcode-0: 5 +olmResultCodeStat;x-resultcode-32: 5 + diff --git a/contrib/slapd-modules/resultstats/tests/data/test001-reset.ldif b/contrib/slapd-modules/resultstats/tests/data/test001-reset.ldif new file mode 100644 index 0000000000..06678bb5aa --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/data/test001-reset.ldif @@ -0,0 +1,45 @@ +# base="cn=Result Stats,cn=database 1,cn=databases,cn=monitor"... +dn: cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: monitorContainer +cn: Result Stats + +dn: cn=Bind,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Bind + +dn: cn=Unbind,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Unbind + +dn: cn=Search,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Search + +dn: cn=Compare,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Compare + +dn: cn=Modify,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Modify + +dn: cn=Modrdn,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Modrdn + +dn: cn=Add,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Add + +dn: cn=Delete,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Delete + +dn: cn=Abandon,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Abandon + +dn: cn=Extended,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Extended + diff --git a/contrib/slapd-modules/resultstats/tests/data/test001-search.ldif b/contrib/slapd-modules/resultstats/tests/data/test001-search.ldif new file mode 100644 index 0000000000..0dee906843 --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/data/test001-search.ldif @@ -0,0 +1,6 @@ +dn: cn=Search,cn=Result Stats,cn=database 1,cn=databases,cn=monitor +objectClass: olmResultStatOperation +cn: Search +olmResultCodeStat;x-resultcode-0: 5 +olmResultCodeStat;x-resultcode-32: 5 + diff --git a/contrib/slapd-modules/resultstats/tests/run b/contrib/slapd-modules/resultstats/tests/run new file mode 100755 index 0000000000..9f2406392f --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/run @@ -0,0 +1,17 @@ +#!/bin/sh +## $OpenLDAP$ +## This work is part of OpenLDAP Software . +## +## Copyright 1998-2024 The OpenLDAP Foundation. +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## . + +TOPSRCDIR="$SRCDIR" OBJDIR="${LDAP_BUILD}" SRCDIR="${SRCDIR}/tests" DEFSDIR="${SRCDIR}/scripts" SCRIPTDIR="${TOPDIR}/tests/scripts" "${LDAP_BUILD}/tests/run" $* + diff --git a/contrib/slapd-modules/resultstats/tests/scripts/test001-resultstats b/contrib/slapd-modules/resultstats/tests/scripts/test001-resultstats new file mode 100755 index 0000000000..080cddf628 --- /dev/null +++ b/contrib/slapd-modules/resultstats/tests/scripts/test001-resultstats @@ -0,0 +1,372 @@ +#! /bin/sh +# $OpenLDAP$ +## This work is part of OpenLDAP Software . +## +## Copyright 1998-2025 The OpenLDAP Foundation. +## Copyright 2025 Symas Corp. +## +## All rights reserved. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted only as authorized by the OpenLDAP +## Public License. +## +## A copy of this license is available in the file LICENSE in the +## top-level directory of the distribution or, alternatively, at +## . +## +## ACKNOWLEDGEMENTS: +## This work was developed in 2025 by Nadezhda Ivanova for Symas Corp. +echo "running defines.sh" +. $SRCDIR/scripts/defines.sh + +echo "" + +rm -rf $TESTDIR + +mkdir -p $TESTDIR $DBDIR1 + +$SLAPPASSWD -g -n >$CONFIGPWF +echo "rootpw `$SLAPPASSWD -T $CONFIGPWF`" >$TESTDIR/configpw.conf + +echo "Starting slapd on TCP/IP port $PORT1..." +. $CONFFILTER $BACKEND < data/slapd.conf > $CONF1 +$SLAPD -f $CONF1 -h $URI1 -d $LVL > $LOG1 2>&1 & +PID=$! +if test $WAIT != 0 ; then + echo PID $PID + read foo +fi +KILLPIDS="$PID" + +sleep 1 + +echo "Using ldapsearch to check that slapd is running..." +for i in 0 1 2 3 4 5; do + $LDAPSEARCH -s base -b "$MONITOR" -H $URI1 \ + 'objectclass=*' > /dev/null 2>&1 + RC=$? + if test $RC = 0 ; then + break + fi + echo "Waiting 5 seconds for slapd to start..." + sleep 5 +done +if test $RC != 0 ; then + echo "ldapsearch failed ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +echo "Using ldapadd to populate the database..." +$LDAPADD -D "$MANAGERDN" -H $URI1 -w $PASSWD < \ + data/test001-add.ldif > $TESTOUT 2>&1 +RC=$? +if test $RC != 0 ; then + echo "ldapadd failed ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +cat /dev/null > $SEARCHOUT + +echo "Searching base=\"$BASEDN\"..." +echo "# searching base=\"$BASEDN\"..." >> $SEARCHOUT +$LDAPSEARCH -S "" -H $URI1 -b "$BASEDN" >> $SEARCHOUT 2>&1 +RC=$? + +if test $RC != 0 ; then + echo "Search failed ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi +#enable the overlay +echo "Modifying cn=config, enabling resultstats overlay on $BACKEND" +$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF \ + >> $TESTOUT 2>&1 << EOMODS +dn: olcOverlay=resultstats,olcDatabase={1}$BACKEND,cn=config +objectClass: olcOverlayConfig +EOMODS +RC=$? +if test $RC != 0 ; then + echo "ldapadd failed ($RC)! Unable to start resultstats overlay" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi +echo "Modifying cn=config, enabling resultstats overlay on frontend" +$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF \ + >> $TESTOUT 2>&1 << EOMODS +dn: olcOverlay=resultstats,olcDatabase={-1}frontend,cn=config +objectClass: olcOverlayConfig +EOMODS +RC=$? +if test $RC != 0 ; then + echo "ldapadd failed ($RC)! Unable to start resultstats overlay" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi +#run successful searches +echo "Performing successful search..." +for i in 1 2 3 4 5; do + $LDAPSEARCH -S "" -H $URI1 -b "cn=user0$i,ou=people,$BASEDN" "(objectClass=*)" '*' \ + > /dev/null 2>&1 + RC=$? + if test $RC != 0 ; then + echo "Expected successful search ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC + fi +done +#verify + +RC=$? +if test $RC != 0 ; then + echo "Expected successful search ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +#run noSuchObject searches +echo "Performing incorrect search (noSuchObject).." +for i in 1 2 3 4 5; do + $LDAPSEARCH -S "" -H $URI1 -b "cn=user0$i,$BASEDN" "(objectClass=*)" '*' \ + > /dev/null 2>&1 + RC=$? + if test $RC != 32 ; then + echo "Expected noSuchObject ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC + fi +done +#verify +SEARCHDN="cn=Search,cn=Result Stats,cn=database 1,cn=databases,cn=monitor" +echo "Verifying search statistics..." +cat /dev/null > $SEARCHOUT + +echo " base=\"$SEARCHDN\"..." +echo "# base=\"$SEARCHDN\"..." >> $SEARCHOUT +$LDAPSEARCH -H $URI1 \ + -b "$SEARCHDN" \ + "(objectClass=*)" '*' >> $SEARCHOUT 2>&1 +RC=$? +if test $RC != 0 ; then + echo "Unable to read monitor stats ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi +echo "Filtering ldapsearch results..." +$LDIFFILTER < $SEARCHOUT > $SEARCHFLT +$LDIFFILTER < data/test001-search.ldif > $LDIFFLT +echo "Comparing filter output..." +$CMP $SEARCHFLT $LDIFFLT > $CMPOUT + +if test $? != 0 ; then + echo "comparison failed - search statistics incorrect" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit 1 +fi +#run successful mods +echo "Performing successful modify..." +for i in 1 2 3 4 5; do + $LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \ + >> $TESTOUT 2>&1 << EOMODS +dn: cn=user0$i,ou=people,$BASEDN +changetype: modify +replace: roomNumber +roomNumber: 200 +EOMODS + RC=$? + if test $RC != 0 ; then + echo "Expected successful modify ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC + fi +done + +#run incorrect mods +#this will not be counted, as it happens before database choice +echo "Performing incorrect modify (undefinedAttributeType)..." +for i in 1 2 3 4 5; do + $LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \ + >> $TESTOUT 2>&1 << EOMODS +dn: cn=user0$i,ou=people,$BASEDN +changetype: modify +replace: IsBusy +IsBusy: TRUE +EOMODS + RC=$? + if test $RC != 17 ; then + echo "Expected undefinedAttributeType ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC + fi +done +echo "Performing incorrect modify (noSuchObject)..." +for i in 1 2 3 4 5; do + $LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w $PASSWD \ + >> $TESTOUT 2>&1 << EOMODS +dn: cn=user0$i,$BASEDN +changetype: modify +replace: roomNumber +roomNumber: 200 +EOMODS + RC=$? + if test $RC != 32 ; then + echo "Expected noSuchObject ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC + fi +done +echo "Performing incorrect modify (invalidCredentials)..." +#this will only be counted on the frontend +for i in 1 2 3 4 5; do + $LDAPMODIFY -D "$MANAGERDN" -H $URI1 -w "wrongpassword" \ + >> $TESTOUT 2>&1 << EOMODS +dn: cn=user0$i,ou=people,$BASEDN +changetype: modify +replace: roomNumber +roomNumber: 200 +EOMODS + RC=$? + if test $RC != 49 ; then + echo "Expected invalidCredentials ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC + fi +done +#verify +echo "Verifying modify statistics..." +cat /dev/null > $SEARCHOUT +MODIFYDN="cn=Modify,cn=Result Stats,cn=database 1,cn=databases,cn=monitor" +echo " base=\"$MODIFYDN\"..." +echo "# base=\"$MODIFYDN\"..." >> $SEARCHOUT +$LDAPSEARCH -H $URI1 \ + -b "$MODIFYDN" \ + "(objectClass=*)" '*' >> $SEARCHOUT 2>&1 + +RC=$? +if test $RC != 0 ; then + echo "Unable to read monitor stats ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi +echo "Filtering ldapsearch results..." +$LDIFFILTER < $SEARCHOUT > $SEARCHFLT +$LDIFFILTER < data/test001-modify.ldif > $LDIFFLT +echo "Comparing filter output..." +$CMP $SEARCHFLT $LDIFFLT > $CMPOUT +if test $? != 0 ; then + echo "comparison failed - modify statistics incorrect" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit 1 +fi + +echo "Verifying bind statistics..." +cat /dev/null > $SEARCHOUT +BINDDN="cn=Bind,cn=Result Stats,cn=frontend,cn=databases,cn=monitor" +echo " base=\"$BINDDN\"..." +echo "# base=\"$BINDDN\"..." >> $SEARCHOUT +$LDAPSEARCH -H $URI1 \ + -b "$BINDDN" \ + "(objectClass=*)" '*' >> $SEARCHOUT 2>&1 +RC=$? +if test $RC != 0 ; then + echo "Unable to read monitor stats ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi +echo "Filtering ldapsearch results..." +$LDIFFILTER < $SEARCHOUT > $SEARCHFLT +$LDIFFILTER < data/test001-bind.ldif > $LDIFFLT +echo "Comparing filter output..." +$CMP $SEARCHFLT $LDIFFLT > $CMPOUT + +if test $? != 0 ; then + echo "comparison failed - bind statistics incorrect" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit 1 +fi +cat /dev/null > $SEARCHOUT + +OVERLAYDN="olcOverlay={0}resultstats,olcDatabase={1}$BACKEND,cn=config" +echo "Modifying cn=config, deleting resultstats overlay on $BACKEND" +$LDAPDELETE -D cn=config -H $URI1 -y $CONFIGPWF $OVERLAYDN\ + >> $TESTOUT 2>&1 +RC=$? +if test $RC != 0 ; then + echo "ldapdelete failed ($RC)! Unable to delete resultstats overlay" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +echo "Modifying cn=config, enabling resultstats overlay on $BACKEND" +$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF \ + >> $TESTOUT 2>&1 << EOMODS +dn: olcOverlay=resultstats,olcDatabase={1}$BACKEND,cn=config +objectClass: olcOverlayConfig +EOMODS +RC=$? +if test $RC != 0 ; then + echo "ldapadd failed ($RC)! Unable to start resultstats overlay" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +echo "Verifying statistics are reset..." +cat /dev/null > $SEARCHOUT +RESDN="cn=Result Stats,cn=database 1,cn=databases,cn=monitor" +echo " base=\"$RESDN\"..." +echo "# base=\"$RESDN\"..." >> $SEARCHOUT +$LDAPSEARCH -H $URI1 \ + -b "$RESDN" \ + "(objectClass=*)" '*' >> $SEARCHOUT 2>&1 + +RC=$? +if test $RC != 0 ; then + echo "Unable to read monitor stats ($RC)!" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +echo "Filtering ldapsearch results..." +$LDIFFILTER < $SEARCHOUT > $SEARCHFLT +$LDIFFILTER < data/test001-reset.ldif > $LDIFFLT +echo "Comparing filter output..." +$CMP $SEARCHFLT $LDIFFLT > $CMPOUT +if test $? != 0 ; then + echo "comparison failed - reset statistics incorrect" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit 1 +fi + +OVERLAYDN="olcOverlay={0}resultstats,olcDatabase={1}$BACKEND,cn=config" +echo "Modifying cn=config, deleting resultstats overlay on $BACKEND" +$LDAPDELETE -D cn=config -H $URI1 -y $CONFIGPWF $OVERLAYDN\ + >> $TESTOUT 2>&1 +RC=$? +if test $RC != 0 ; then + echo "ldapdelete failed ($RC)! Unable to delete resultstats overlay" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +echo "Modifying cn=config, enabling resultstats overlay on $BACKEND" +$LDAPADD -D cn=config -H $URI1 -y $CONFIGPWF \ + >> $TESTOUT 2>&1 << EOMODS +dn: olcOverlay=resultstats,olcDatabase={1}$BACKEND,cn=config +objectClass: olcOverlayConfig +EOMODS +RC=$? +if test $RC != 0 ; then + echo "ldapadd failed ($RC)! Unable to start resultstats overlay" + test $KILLSERVERS != no && kill -HUP $KILLPIDS + exit $RC +fi + +test $KILLSERVERS != no && kill -HUP $KILLPIDS + +echo ">>>>> Test succeeded" + +test $KILLSERVERS != no && wait + +exit 0