diff options
Diffstat (limited to 'src/lib/database')
37 files changed, 8637 insertions, 0 deletions
diff --git a/src/lib/database/Makefile.am b/src/lib/database/Makefile.am new file mode 100644 index 0000000..5127b52 --- /dev/null +++ b/src/lib/database/Makefile.am @@ -0,0 +1,79 @@ +SUBDIRS = . testutils tests + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Ensure that the message file is included in the distribution +EXTRA_DIST = database.dox db_messages.mes + +CLEANFILES = *.gcno *.gcda + +lib_LTLIBRARIES = libkea-database.la +libkea_database_la_SOURCES = audit_entry.cc audit_entry.h +libkea_database_la_SOURCES += backend_selector.cc backend_selector.h +libkea_database_la_SOURCES += database_connection.cc database_connection.h +libkea_database_la_SOURCES += dbaccess_parser.h dbaccess_parser.cc +libkea_database_la_SOURCES += db_exceptions.h +libkea_database_la_SOURCES += db_log.cc db_log.h +libkea_database_la_SOURCES += db_messages.cc db_messages.h +libkea_database_la_SOURCES += server.cc server.h +libkea_database_la_SOURCES += server_collection.cc server_collection.h +libkea_database_la_SOURCES += server_selector.cc server_selector.h + +libkea_database_la_LIBADD = $(top_builddir)/src/lib/cc/libkea-cc.la +libkea_database_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libkea_database_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libkea_database_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la +libkea_database_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libkea_database_la_LIBADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) + +libkea_database_la_LDFLAGS = -no-undefined -version-info 48:0:0 + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f db_messages.h db_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +if GENERATE_MESSAGES + +# Define rule to build logging source files from message file +messages: db_messages.h db_messages.cc + @echo Message files regenerated + +db_messages.h db_messages.cc: db_messages.mes + $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/database/db_messages.mes + +else + +messages db_messages.h db_messages.cc: + @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +endif + +# Specify the headers for copying into the installation directory tree. +libkea_database_includedir = $(pkgincludedir)/database +libkea_database_include_HEADERS = \ + audit_entry.h \ + backend_selector.h \ + database_connection.h \ + dbaccess_parser.h \ + db_exceptions.h \ + db_log.h \ + db_messages.h \ + server.h \ + server_collection.h \ + server_selector.h diff --git a/src/lib/database/Makefile.in b/src/lib/database/Makefile.in new file mode 100644 index 0000000..ae4d5ca --- /dev/null +++ b/src/lib/database/Makefile.in @@ -0,0 +1,1027 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/lib/database +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_cpp20.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_netconf.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(libkea_database_include_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(libkea_database_includedir)" +LTLIBRARIES = $(lib_LTLIBRARIES) +am__DEPENDENCIES_1 = +libkea_database_la_DEPENDENCIES = \ + $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_libkea_database_la_OBJECTS = audit_entry.lo backend_selector.lo \ + database_connection.lo dbaccess_parser.lo db_log.lo \ + db_messages.lo server.lo server_collection.lo \ + server_selector.lo +libkea_database_la_OBJECTS = $(am_libkea_database_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libkea_database_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libkea_database_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/audit_entry.Plo \ + ./$(DEPDIR)/backend_selector.Plo \ + ./$(DEPDIR)/database_connection.Plo ./$(DEPDIR)/db_log.Plo \ + ./$(DEPDIR)/db_messages.Plo ./$(DEPDIR)/dbaccess_parser.Plo \ + ./$(DEPDIR)/server.Plo ./$(DEPDIR)/server_collection.Plo \ + ./$(DEPDIR)/server_selector.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libkea_database_la_SOURCES) +DIST_SOURCES = $(libkea_database_la_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(libkea_database_include_HEADERS) +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@ +DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@ +DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_NETCONF = @HAVE_NETCONF@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@ +LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@ +LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@ +LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@ +LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@ +LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@ +LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@ +LIBYANG_LIBS = @LIBYANG_LIBS@ +LIBYANG_PREFIX = @LIBYANG_PREFIX@ +LIBYANG_VERSION = @LIBYANG_VERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_PLUGINS_PATH = @SR_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@ +SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@ +SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@ +SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_PREFIX = @SYSREPO_PREFIX@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . testutils tests +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \ + $(BOOST_INCLUDES) +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +# Ensure that the message file is included in the distribution +EXTRA_DIST = database.dox db_messages.mes +CLEANFILES = *.gcno *.gcda +lib_LTLIBRARIES = libkea-database.la +libkea_database_la_SOURCES = audit_entry.cc audit_entry.h \ + backend_selector.cc backend_selector.h database_connection.cc \ + database_connection.h dbaccess_parser.h dbaccess_parser.cc \ + db_exceptions.h db_log.cc db_log.h db_messages.cc \ + db_messages.h server.cc server.h server_collection.cc \ + server_collection.h server_selector.cc server_selector.h +libkea_database_la_LIBADD = $(top_builddir)/src/lib/cc/libkea-cc.la \ + $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ + $(top_builddir)/src/lib/log/libkea-log.la \ + $(top_builddir)/src/lib/util/libkea-util.la \ + $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ + $(LOG4CPLUS_LIBS) $(BOOST_LIBS) +libkea_database_la_LDFLAGS = -no-undefined -version-info 48:0:0 + +# Specify the headers for copying into the installation directory tree. +libkea_database_includedir = $(pkgincludedir)/database +libkea_database_include_HEADERS = \ + audit_entry.h \ + backend_selector.h \ + database_connection.h \ + dbaccess_parser.h \ + db_exceptions.h \ + db_log.h \ + db_messages.h \ + server.h \ + server_collection.h \ + server_selector.h + +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/database/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/database/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libkea-database.la: $(libkea_database_la_OBJECTS) $(libkea_database_la_DEPENDENCIES) $(EXTRA_libkea_database_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libkea_database_la_LINK) -rpath $(libdir) $(libkea_database_la_OBJECTS) $(libkea_database_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/audit_entry.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/backend_selector.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/database_connection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db_log.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db_messages.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dbaccess_parser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_collection.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_selector.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-libkea_database_includeHEADERS: $(libkea_database_include_HEADERS) + @$(NORMAL_INSTALL) + @list='$(libkea_database_include_HEADERS)'; test -n "$(libkea_database_includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libkea_database_includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libkea_database_includedir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(libkea_database_includedir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(libkea_database_includedir)" || exit $$?; \ + done + +uninstall-libkea_database_includeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(libkea_database_include_HEADERS)'; test -n "$(libkea_database_includedir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libkea_database_includedir)'; $(am__uninstall_files_from_dir) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libkea_database_includedir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/audit_entry.Plo + -rm -f ./$(DEPDIR)/backend_selector.Plo + -rm -f ./$(DEPDIR)/database_connection.Plo + -rm -f ./$(DEPDIR)/db_log.Plo + -rm -f ./$(DEPDIR)/db_messages.Plo + -rm -f ./$(DEPDIR)/dbaccess_parser.Plo + -rm -f ./$(DEPDIR)/server.Plo + -rm -f ./$(DEPDIR)/server_collection.Plo + -rm -f ./$(DEPDIR)/server_selector.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-libkea_database_includeHEADERS + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-libLTLIBRARIES + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/audit_entry.Plo + -rm -f ./$(DEPDIR)/backend_selector.Plo + -rm -f ./$(DEPDIR)/database_connection.Plo + -rm -f ./$(DEPDIR)/db_log.Plo + -rm -f ./$(DEPDIR)/db_messages.Plo + -rm -f ./$(DEPDIR)/dbaccess_parser.Plo + -rm -f ./$(DEPDIR)/server.Plo + -rm -f ./$(DEPDIR)/server_collection.Plo + -rm -f ./$(DEPDIR)/server_selector.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic \ + maintainer-clean-local + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-libLTLIBRARIES \ + uninstall-libkea_database_includeHEADERS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libLTLIBRARIES \ + install-libkea_database_includeHEADERS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic \ + maintainer-clean-local mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES \ + uninstall-libkea_database_includeHEADERS + +.PRECIOUS: Makefile + + +# If we want to get rid of all generated messages files, we need to use +# make maintainer-clean. The proper way to introduce custom commands for +# that operation is to define maintainer-clean-local target. However, +# make maintainer-clean also removes Makefile, so running configure script +# is required. To make it easy to rebuild messages without going through +# reconfigure, a new target messages-clean has been added. +maintainer-clean-local: + rm -f db_messages.h db_messages.cc + +# To regenerate messages files, one can do: +# +# make messages-clean +# make messages +# +# This is needed only when a .mes file is modified. +messages-clean: maintainer-clean-local + +# Define rule to build logging source files from message file +@GENERATE_MESSAGES_TRUE@messages: db_messages.h db_messages.cc +@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated + +@GENERATE_MESSAGES_TRUE@db_messages.h db_messages.cc: db_messages.mes +@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/lib/database/db_messages.mes + +@GENERATE_MESSAGES_FALSE@messages db_messages.h db_messages.cc: +@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it. + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/database/audit_entry.cc b/src/lib/database/audit_entry.cc new file mode 100644 index 0000000..92cb887 --- /dev/null +++ b/src/lib/database/audit_entry.cc @@ -0,0 +1,88 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/audit_entry.h> +#include <exceptions/exceptions.h> +#include <boost/make_shared.hpp> + +namespace isc { +namespace db { + +AuditEntry::AuditEntry(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const boost::posix_time::ptime& modification_time, + const uint64_t revision_id, + const std::string& log_message) + : object_type_(object_type), + object_id_(object_id), + modification_type_(modification_type), + modification_time_(modification_time), + revision_id_(revision_id), + log_message_(log_message) { + // Check if the provided values are sane. + validate(); +} + +AuditEntry::AuditEntry(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const uint64_t revision_id, + const std::string& log_message) + : object_type_(object_type), + object_id_(object_id), + modification_type_(modification_type), + modification_time_(boost::posix_time::microsec_clock::local_time()), + revision_id_(revision_id), + log_message_(log_message) { + // Check if the provided values are sane. + validate(); +} + +AuditEntryPtr +AuditEntry::create(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const boost::posix_time::ptime& modification_time, + const uint64_t revision_id, + const std::string& log_message) { + return (boost::make_shared<AuditEntry>(object_type, object_id, + modification_type, + modification_time, + revision_id, + log_message)); +} + +AuditEntryPtr +AuditEntry::create(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const uint64_t revision_id, + const std::string& log_message) { + return (boost::make_shared<AuditEntry>(object_type, object_id, + modification_type, + revision_id, + log_message)); +} + +void +AuditEntry::validate() const { + // object type can't be empty + if (object_type_.empty()) { + isc_throw(BadValue, "object type can't be empty in the database " + "audit entry"); + + // modification time must be a valid date time value + } else if (modification_time_.is_not_a_date_time()) { + isc_throw(BadValue, "modification time value is not a valid time " + "object in the database audit entry"); + } +} + +} // end of namespace isc::db +} // end of namespace isc diff --git a/src/lib/database/audit_entry.h b/src/lib/database/audit_entry.h new file mode 100644 index 0000000..6c46175 --- /dev/null +++ b/src/lib/database/audit_entry.h @@ -0,0 +1,299 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef AUDIT_ENTRY_H +#define AUDIT_ENTRY_H + +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/multi_index/composite_key.hpp> +#include <boost/multi_index_container.hpp> +#include <boost/multi_index/hashed_index.hpp> +#include <boost/multi_index/mem_fun.hpp> +#include <boost/multi_index/ordered_index.hpp> +#include <boost/shared_ptr.hpp> +#include <cstdint> +#include <string> + +namespace isc { +namespace db { + +class AuditEntry; + +/// @brief Pointer to the @c AuditEntry object. +typedef boost::shared_ptr<AuditEntry> AuditEntryPtr; + +/// @brief Represents a single entry in the audit table. +/// +/// The audit tables are used in the databases to track incremental +/// changes, e.g. configuration changes applied via the configuration +/// backend. The backend can query for the entries in the audit table +/// to identify the SQL table in which the records were modified, the +/// identifiers of the modified objects, type of the modifications, +/// time of the modifications and optional log messages associated +/// with the modifications. +/// +/// The server should remember the most recent modification time out of +/// all audit entries it has fetched. During the next attempt to fetch +/// the most recent modifications in the database it will query for all +/// entries with later modification time than stored. That way the +/// server queries only for the audit entries it hasn't fetched yet. +/// In the case two (or more) successive audit entries have the same +/// modification time the strictly increasing modification id is used. +/// +/// When the modification type of the entry is set to +/// @c AuditEntry::ModificationType::DELETE, the corresponding +/// configuration entry is already gone from the database. For example: +/// when a subnet with ID of 123 is deleted from the dhcp4_subnet +/// table, the audit entry similar to this will be stored in the audit +/// table: +/// +/// - object_type: "dhcp4_subnet" +/// - object_id: 123 +/// - modification_type: 3 (DELETE) +/// - modification_time: "2019-01-15 15:45:23" +/// - revision id: 234 +/// - log_message: "DHCPv4 subnet 123 deleted" +/// +/// The subnet is instantly removed from the dhcp4_subnet table. When +/// the server finds such entry in the audit table, it removes the +/// subnet 123 from its (in-memory) configuration. There is no need +/// make additional queries to fetch updated data from the dhcp4_subnet +/// table, unless there are also audit entries indicating that some +/// new subnets have been added or some subnets have been updated. +class AuditEntry { +public: + + /// @brief Types of the modifications. + /// + /// The numbers representing those modification types correspond + /// to the values representing them in the database. + enum class ModificationType : uint8_t { + CREATE = 0, + UPDATE = 1, + DELETE = 2 + }; + + /// @brief Constructor using explicit modification time and id. + /// + /// @param object_type name of the table where data was modified. + /// @param object_id identifier of the modified record in this table. + /// @param modification_type type of the modification, e.g. DELETE. + /// @param modification_time time of modification for that record. + /// @param revision_id identifier of the revision. + /// @param log_message optional log message associated with the + /// modification. + AuditEntry(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const boost::posix_time::ptime& modification_time, + const uint64_t revision_id, + const std::string& log_message); + + /// @brief Constructor using default modification time. + /// + /// @param object_type name of the table where data was modified. + /// @param object_id identifier of the modified record in this table. + /// @param modification_type type of the modification, e.g. DELETE. + /// @param revision_id identifier of the revision. + /// @param log_message optional log message associated with the + /// modification. + AuditEntry(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const uint64_t revision_id, + const std::string& log_message); + + /// @brief Factory function creating an instance of @c AuditEntry. + /// + /// This function should be used to create an instance of the audit + /// entry within a hooks library in cases when the library may be + /// unloaded before the object is destroyed. This ensures that the + /// ownership of the object by the Kea process is retained. + /// + /// @param object_type name of the table where data was modified. + /// @param object_id identifier of the modified record in this table. + /// @param modification_type type of the modification, e.g. DELETE. + /// @param modification_time time of modification for that record. + /// @param revision_id identifier of the revision. + /// @param log_message optional log message associated with the + /// modification. + /// + /// @return Pointer to the @c AuditEntry instance. + static AuditEntryPtr create(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const boost::posix_time::ptime& modification_time, + const uint64_t revision_id, + const std::string& log_message); + + /// @brief Factory function creating an instance of @c AuditEntry. + /// + /// This function should be used to create an instance of the audit + /// entry within a hooks library in cases when the library may be + /// unloaded before the object is destroyed. This ensures that the + /// ownership of the object by the Kea process is retained. + /// + /// @param object_type name of the table where data was modified. + /// @param object_id identifier of the modified record in this table. + /// @param modification_type type of the modification, e.g. DELETE. + /// @param revision_id identifier of the revision. + /// @param log_message optional log message associated with the + /// modification. + /// + /// @return Pointer to the @c AuditEntry instance. + static AuditEntryPtr create(const std::string& object_type, + const uint64_t object_id, + const ModificationType& modification_type, + const uint64_t revision_id, + const std::string& log_message); + + /// @brief Returns object type. + /// + /// @return Name of the table in which the modification is present. + std::string getObjectType() const { + return (object_type_); + } + + /// @brief Returns object id. + /// + /// @return Identifier of the added, updated or deleted object. + uint64_t getObjectId() const { + return (object_id_); + } + + /// @brief Returns modification type. + /// + /// @return Type of the modification, e.g. DELETE. + ModificationType getModificationType() const { + return (modification_type_); + } + + /// @brief Returns modification time. + /// + /// @return Modification time of the corresponding record. + boost::posix_time::ptime getModificationTime() const { + return (modification_time_); + } + + /// @brief Returns revision id. + /// + /// The revision id is used when two audit entries have the same + /// modification time. + /// + /// @return Identifier of the revision. + uint64_t getRevisionId() const { + return (revision_id_); + } + + /// @brief Returns log message. + /// + /// @return Optional log message corresponding to the changes. + std::string getLogMessage() const { + return (log_message_); + } + +private: + + /// @brief Validates the values specified for the audit entry. + /// + /// @throw BadValue if the object type is empty or if the + /// modification time is not a date time value. + void validate() const; + + /// @brief Object type. + std::string object_type_; + + /// @brief Object id. + uint64_t object_id_; + + /// @brief Modification type. + ModificationType modification_type_; + + /// @brief Modification time. + boost::posix_time::ptime modification_time_; + + /// @brief Revision id. + /// + /// The revision id is used when two audit entries have the same + /// modification time. + uint64_t revision_id_; + + /// @brief Log message. + std::string log_message_; +}; + +/// @brief Tag used to access index by object type. +struct AuditEntryObjectTypeTag { }; + +/// @brief Tag used to access index by modification time. +struct AuditEntryModificationTimeIdTag { }; + +/// @brief Tag used to access index by object id. +struct AuditEntryObjectIdTag { }; + +/// @brief Multi index container holding @c AuditEntry instances. +/// +/// This container provides indexes to access the audit entries +/// by object type and modification time. +typedef boost::multi_index_container< + // The container holds pointers to @c AuditEntry objects. + AuditEntryPtr, + // First index allows for accessing by the object type. + boost::multi_index::indexed_by< + boost::multi_index::ordered_non_unique< + boost::multi_index::tag<AuditEntryObjectTypeTag>, + boost::multi_index::composite_key< + AuditEntry, + boost::multi_index::const_mem_fun< + AuditEntry, + std::string, + &AuditEntry::getObjectType + >, + boost::multi_index::const_mem_fun< + AuditEntry, + AuditEntry::ModificationType, + &AuditEntry::getModificationType + > + > + >, + + // Second index allows for accessing by the modification time and id. + boost::multi_index::ordered_non_unique< + boost::multi_index::tag<AuditEntryModificationTimeIdTag>, + boost::multi_index::composite_key< + AuditEntry, + boost::multi_index::const_mem_fun< + AuditEntry, + boost::posix_time::ptime, + &AuditEntry::getModificationTime + >, + boost::multi_index::const_mem_fun< + AuditEntry, + uint64_t, + &AuditEntry::getRevisionId + > + > + >, + + // Third index allows for accessing by the object id. + boost::multi_index::hashed_non_unique< + boost::multi_index::tag<AuditEntryObjectIdTag>, + boost::multi_index::const_mem_fun< + AuditEntry, + uint64_t, + &AuditEntry::getObjectId + > + > + > +> AuditEntryCollection; + +//// @brief Pointer to the @c AuditEntryCollection object. +typedef boost::shared_ptr<AuditEntryCollection> AuditEntryCollectionPtr; + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/database/backend_selector.cc b/src/lib/database/backend_selector.cc new file mode 100644 index 0000000..611017c --- /dev/null +++ b/src/lib/database/backend_selector.cc @@ -0,0 +1,165 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/backend_selector.h> +#include <exceptions/exceptions.h> +#include <limits> +#include <sstream> + +using namespace isc::data; + +namespace isc { +namespace db { + +BackendSelector::BackendSelector() + : backend_type_(BackendSelector::Type::UNSPEC), + host_(), port_(0) { +} + +BackendSelector::BackendSelector(const Type& backend_type) + : backend_type_(backend_type), + host_(), port_(0) { +} + +BackendSelector::BackendSelector(const std::string& host, + const uint16_t port) + : backend_type_(BackendSelector::Type::UNSPEC), + host_(host), port_(port) { + validate(); +} + +BackendSelector::BackendSelector(const data::ConstElementPtr& access_map) + : backend_type_(BackendSelector::Type::UNSPEC), + host_(), port_(0) { + if (access_map->getType() != Element::map) { + isc_throw(BadValue, "database access information must be a map"); + } + + ConstElementPtr t = access_map->get("type"); + if (t) { + if (t->getType() != Element::string) { + isc_throw(BadValue, "'type' parameter must be a string"); + } + backend_type_ = stringToBackendType(t->stringValue()); + } + + ConstElementPtr h = access_map->get("host"); + if (h) { + if (h->getType() != Element::string) { + isc_throw(BadValue, "'host' parameter must be a string"); + } + host_ = h->stringValue(); + } + + ConstElementPtr p = access_map->get("port"); + if (p) { + if ((p->getType() != Element::integer) || + (p->intValue() < 0) || + (p->intValue() > std::numeric_limits<uint16_t>::max())) { + isc_throw(BadValue, "'port' parameter must be a number in range from 0 " + "to " << std::numeric_limits<uint16_t>::max()); + } + port_ = static_cast<uint16_t>(p->intValue()); + } + + validate(); +} + +const BackendSelector& +BackendSelector::UNSPEC() { + static BackendSelector selector; + return (selector); +} + +bool +BackendSelector::amUnspecified() const { + return ((backend_type_ == BackendSelector::Type::UNSPEC) && + (host_.empty()) && + (port_ == 0)); +} + +std::string +BackendSelector::toText() const { + std::ostringstream s; + if (amUnspecified()) { + s << "unspecified"; + + } else { + if (backend_type_ != BackendSelector::Type::UNSPEC) { + s << "type=" << backendTypeToString(backend_type_) << ","; + } + + if (!host_.empty()) { + s << "host=" << host_ << ","; + + if (port_ > 0) { + s << "port=" << port_ << ","; + } + } + } + + std::string text = s.str(); + if ((!text.empty() && (text.back() == ','))) { + text.pop_back(); + } + + return (text); +} + +ElementPtr +BackendSelector::toElement() const { + if (backend_type_ == BackendSelector::Type::UNSPEC) { + isc_throw(BadValue, "toElement: backend selector type is unspecified"); + } + ElementPtr result = Element::createMap(); + result->set("type", Element::create(backendTypeToString(backend_type_))); + if (!host_.empty()) { + result->set("host", Element::create(host_)); + if (port_ > 0) { + result->set("port", Element::create(static_cast<long int>(port_))); + } + } + return (result); +} + +BackendSelector::Type +BackendSelector::stringToBackendType(const std::string& type) { + if (type == "mysql") { + return (BackendSelector::Type::MYSQL); + + } else if (type == "postgresql") { + return (BackendSelector::Type::POSTGRESQL); + + } else { + isc_throw(BadValue, "unsupported configuration backend type '" << type << "'"); + } +} + +std::string +BackendSelector::backendTypeToString(const BackendSelector::Type& type) { + switch (type) { + case BackendSelector::Type::MYSQL: + return ("mysql"); + case BackendSelector::Type::POSTGRESQL: + return ("postgresql"); + default: + ; + } + + return (std::string()); +} + +void +BackendSelector::validate() const { + if ((port_ != 0) && (host_.empty())) { + isc_throw(BadValue, "'host' must be specified along with 'port' parameter"); + } +} + +} // end of namespace isc::db +} // end of namespace isc diff --git a/src/lib/database/backend_selector.h b/src/lib/database/backend_selector.h new file mode 100644 index 0000000..db207df --- /dev/null +++ b/src/lib/database/backend_selector.h @@ -0,0 +1,215 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef BACKEND_SELECTOR_H +#define BACKEND_SELECTOR_H + +#include <cc/data.h> +#include <cc/cfg_to_element.h> +#include <cstdint> +#include <string> + +namespace isc { +namespace db { + +/// @brief Config Backend selector. +/// +/// Each Kea server using database as a configuration respository +/// may use multiple configuration backends simultaneously. The most +/// typical case is to use a single configuration backend per server, +/// but there are use cases when configuration information is distributed +/// accross multiple database instances. In the future, there may be +/// also caching mechanisms implemented, which will allow for storing +/// results of certain database queries in memory. +/// +/// From the server perspective, the most common use of the configuration +/// backend is to fetch entire configuration information from the +/// databases (upon startup) or fetch the latest updates to the +/// configuration, e.g. new subnet added, DHCP option modified etc. +/// In those cases, it is not so important from the server which backend +/// this data come from. Therefore, the server would fetch this information +/// from all available backends. +/// +/// When the server administrator wants to insert some new data into +/// the database, modify existing data or simply wants to check the +/// contents of one of the database instance, he would specify which +/// database backend he wants to direct queries to. +/// +/// The @c BackendSelector class provides means to specify whether +/// the queries should be directed to any backend (see server case +/// above) or to a specific backend (data insertion case above). +/// In addition, the @c BackendSelector allows for using various +/// criteria for selecting a backend to use. Currently those criteria +/// are: database type (e.g. mysql), database host and database port. +/// In order to use a specific port, the database host must also be +/// specified. Note that in a general case multiple backends of the +/// same type can be used simultaneously, e.g. multiple MySQL backends. +/// In that case, it may be necessary to specify host (and port) to +/// issue a query to the right one. +/// +/// The @c BackendSelector class may be extended in the future to provide +/// additional backend selection criteria. +class BackendSelector : public data::CfgToElement { +public: + + /// @brief Supported database types. + /// + /// The @c UNSPEC indicates that the database type is not specified + /// as selection criteria. + enum class Type { + MYSQL, + POSTGRESQL, + UNSPEC + }; + + /// @brief Default constructor. + /// + /// It sets the selector to "unspecified". When this selector is used + /// the backend pool will use "any" backend. This has a different meaning + /// for each type of query. See the @c BaseConfigBackendPool for details. + explicit BackendSelector(); + + /// @brief Constructor specifying backend type as a selection criteria. + /// + /// @param backend_type Type of the backend to be selected. + explicit BackendSelector(const Type& backend_type); + + /// @brief Constructor for specifying host and optionally port as a + /// selection criteria. + /// + /// @param host Hostname to be used for selecting a backend. + /// @param port Port number to be used for selecting a backend. This value + /// is optional and is ignored when set to 0. It must be used in conjunction + /// with hostname. + explicit BackendSelector(const std::string& host, const uint16_t port = 0); + + /// @brief Constructor for selecting a backend using JSON access map. + /// + /// The provided access map must have the same structure as an element + /// of the "config-databases" configuration parameter. However, it merely + /// takes into account: "type", "host" and "port" parameters. In addition, + /// these parameters are optional. The following are valid combinations: + /// + /// @code + /// { + /// "type": "mysql" + /// } + /// @endcode + /// + /// @code + /// { + /// "host": "somehost.example.org" + /// } + /// @endcode + /// + /// @code + /// { + /// "host": "somehost.example.org", + /// "port": 1234 + /// } + /// @endcode + /// + /// @code + /// { + /// "type": "mysql" + /// "host": "somehost.example.org", + /// } + /// @endcode + /// + /// @code + /// { + /// "type": "mysql" + /// "host": "somehost.example.org", + /// "port": 1234 + /// } + /// @endcode + /// + /// where "type" can be any of the supported backend types. + /// + /// This constructor is useful for creating backend selectors from the + /// received control commands. + /// + /// @param access_map Access map as provided above. + explicit BackendSelector(const data::ConstElementPtr& access_map); + + /// @brief Returns instance of the "unspecified" backend selector. + static const BackendSelector& UNSPEC(); + + /// @brief Checks if selector is "unspecified". + /// + /// @return true if backend type is @c UNSPEC, hostname is empty and + /// port number 0, false otherwise. + bool amUnspecified() const; + + /// @brief Returns backend type selected. + Type getBackendType() const { + return (backend_type_); + } + + /// @brief Returns host selected. + /// + /// @return host if specified or empty string if host is not + /// specified. + std::string getBackendHost() const { + return (host_); + } + + /// @brief Returns port selected. + /// + /// @return port number of the selected backend or 0 if port number + /// is not specified. + uint16_t getBackendPort() const { + return (port_); + } + + /// @brief Returns selections as text. + /// + /// @return Collection of comma separated selections, e.g. + /// "type=mysql,host=somehost.example.org,port=1234". + std::string toText() const; + + /// @brief Unparse a backend selector object. + /// + /// The caller must ensure that the selector type is specified. + /// + /// @return A pointer to unparsed backend selector configuration. + /// @throw BadValue If the backend selector type is unspecified. + virtual data::ElementPtr toElement() const; + + /// @brief Converts string to backend type. + /// + /// @param type Backend type as string. + static Type stringToBackendType(const std::string& type); + + /// @brief Converts backend type to string. + /// + /// @param type Backend type to be converted. + static std::string backendTypeToString(const Type& type); + +private: + + /// @brief Checks if the specified selector is valid. + /// + /// It checks if the port number is specified in conjunction with + /// host. + /// @throw BadValue if selector validation fails. + void validate() const; + + /// @brief Backend type selected. + Type backend_type_; + + /// @brief Host selected. + std::string host_; + + /// @brief Port number selected. + uint16_t port_; +}; + + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/database/database.dox b/src/lib/database/database.dox new file mode 100644 index 0000000..6612d6d --- /dev/null +++ b/src/lib/database/database.dox @@ -0,0 +1,24 @@ +// Copyright (C) 2020-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/** + @page libdatabase libkea-database - Kea Database Library + +@section databaseMTConsiderations Multi-Threading Consideration for Database + +MySQL and PostgreSQL provide connection pools which are used to make +lease, host and legal log backends thread safe. + +MySQL and PostgreSQL are inter-process safe only when transactions are used +(including the MySQL auto-transaction mode which includes queries into +a transaction). For MySQL this means that transactions must be supported +by the database engine (the engine selection is done in the schema). + +Note the InnoDB engine used by Kea for MySQL databases cancels a transaction +when a deadlock is detected (rare but possible event) and leaves the +responsibility to retry the transaction to the caller. + +*/ diff --git a/src/lib/database/database_connection.cc b/src/lib/database/database_connection.cc new file mode 100644 index 0000000..50175c6 --- /dev/null +++ b/src/lib/database/database_connection.cc @@ -0,0 +1,285 @@ +// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/cfg_to_element.h> +#include <database/database_connection.h> +#include <database/db_exceptions.h> +#include <database/db_log.h> +#include <database/db_messages.h> +#include <exceptions/exceptions.h> +#include <util/strutil.h> + +#include <boost/algorithm/string.hpp> +#include <boost/foreach.hpp> +#include <vector> + +using namespace isc::util; +using namespace std; + +namespace isc { +namespace db { + +const time_t DatabaseConnection::MAX_DB_TIME = 2147483647; + +std::string +DatabaseConnection::getParameter(const std::string& name) const { + ParameterMap::const_iterator param = parameters_.find(name); + if (param == parameters_.end()) { + isc_throw(BadValue, "Parameter " << name << " not found"); + } + return (param->second); +} + +DatabaseConnection::ParameterMap +DatabaseConnection::parse(const std::string& dbaccess) { + DatabaseConnection::ParameterMap mapped_tokens; + std::string dba = dbaccess; + + if (!dba.empty()) { + try { + vector<string> tokens; + + // Handle the special case of a password which is enclosed in apostrophes. + // Such password may include whitespace. + std::string password_prefix = "password='"; + auto password_pos = dba.find(password_prefix); + if (password_pos != string::npos) { + // Password starts with apostrophe, so let's find ending apostrophe. + auto password_end_pos = dba.find('\'', password_pos + password_prefix.length()); + if (password_end_pos == string::npos) { + // No ending apostrophe. This is wrong. + isc_throw(InvalidParameter, "Apostrophe (') expected at the end of password"); + } + // Extract the password value. It starts after the password=' prefix and ends + // at the position of ending apostrophe. + auto password = dba.substr(password_pos + password_prefix.length(), + password_end_pos - password_pos - password_prefix.length()); + mapped_tokens.insert(make_pair("password", password)); + + // We need to erase the password from the access string because the generic + // algorithm parsing other parameters requires that there are no whitespaces + // within the parameter values. + dba.erase(password_pos, password_prefix.length() + password.length() + 2); + // Leading or trailing whitespace may remain after the password removal. + dba = util::str::trim(dba); + // If the password was the only parameter in the access string, there is + // nothing more to do. + if (dba.empty()) { + return (mapped_tokens); + } + } + + // We need to pass a string to is_any_of, not just char*. Otherwise + // there are cryptic warnings on Debian6 running g++ 4.4 in + // /usr/include/c++/4.4/bits/stl_algo.h:2178 "array subscript is above + // array bounds" + boost::split(tokens, dba, boost::is_any_of(string("\t "))); + BOOST_FOREACH(std::string token, tokens) { + size_t pos = token.find("="); + if (pos != string::npos) { + string name = token.substr(0, pos); + string value = token.substr(pos + 1); + mapped_tokens.insert(make_pair(name, value)); + } else { + isc_throw(InvalidParameter, "Cannot parse " << token + << ", expected format is name=value"); + } + } + } catch (const std::exception& ex) { + // We'd obscure the password if we could parse the access string. + DB_LOG_ERROR(DB_INVALID_ACCESS).arg(dbaccess); + throw; + } + } + + return (mapped_tokens); +} + +std::string +DatabaseConnection::redactedAccessString(const ParameterMap& parameters) { + // Reconstruct the access string: start of with an empty string, then + // work through all the parameters in the original string and add them. + std::string access; + for (DatabaseConnection::ParameterMap::const_iterator i = parameters.begin(); + i != parameters.end(); ++i) { + + // Separate second and subsequent tokens are preceded by a space. + if (!access.empty()) { + access += " "; + } + + // Append name of parameter... + access += i->first; + access += "="; + + // ... and the value, except in the case of the password, where a + // redacted value is appended. + if (i->first == std::string("password")) { + access += "*****"; + } else { + access += i->second; + } + } + + return (access); +} + +bool +DatabaseConnection::configuredReadOnly() const { + std::string readonly_value = "false"; + try { + readonly_value = getParameter("readonly"); + boost::algorithm::to_lower(readonly_value); + } catch (...) { + // Parameter "readonly" hasn't been specified so we simply use + // the default value of "false". + } + + if ((readonly_value != "false") && (readonly_value != "true")) { + isc_throw(DbInvalidReadOnly, "invalid value '" << readonly_value + << "' specified for boolean parameter 'readonly'"); + } + + return (readonly_value == "true"); +} + +void +DatabaseConnection::makeReconnectCtl(const std::string& timer_name) { + string type = "unknown"; + unsigned int retries = 0; + unsigned int interval = 0; + + // Assumes that parsing ensures only valid values are present + try { + type = getParameter("type"); + } catch (...) { + // Wasn't specified so we'll use default of "unknown". + } + + std::string parm_str; + try { + parm_str = getParameter("max-reconnect-tries"); + retries = boost::lexical_cast<unsigned int>(parm_str); + } catch (...) { + // Wasn't specified so we'll use default of 0; + } + + try { + parm_str = getParameter("reconnect-wait-time"); + interval = boost::lexical_cast<unsigned int>(parm_str); + } catch (...) { + // Wasn't specified so we'll use default of 0; + } + + OnFailAction action = OnFailAction::STOP_RETRY_EXIT; + try { + parm_str = getParameter("on-fail"); + action = ReconnectCtl::onFailActionFromText(parm_str); + } catch (...) { + // Wasn't specified so we'll use default of "stop-retry-exit"; + } + + reconnect_ctl_ = boost::make_shared<ReconnectCtl>(type, timer_name, retries, + interval, action); +} + +bool +DatabaseConnection::invokeDbLostCallback(const ReconnectCtlPtr& db_reconnect_ctl) { + if (DatabaseConnection::db_lost_callback_) { + return (DatabaseConnection::db_lost_callback_(db_reconnect_ctl)); + } + + return (false); +} + +bool +DatabaseConnection::invokeDbRecoveredCallback(const ReconnectCtlPtr& db_reconnect_ctl) { + if (DatabaseConnection::db_recovered_callback_) { + return (DatabaseConnection::db_recovered_callback_(db_reconnect_ctl)); + } + + return (false); +} + +bool +DatabaseConnection::invokeDbFailedCallback(const ReconnectCtlPtr& db_reconnect_ctl) { + if (DatabaseConnection::db_failed_callback_) { + return (DatabaseConnection::db_failed_callback_(db_reconnect_ctl)); + } + + return (false); +} + +isc::data::ElementPtr +DatabaseConnection::toElement(const ParameterMap& params) { + isc::data::ElementPtr result = isc::data::Element::createMap(); + + for (auto param: params) { + std::string keyword = param.first; + std::string value = param.second; + + if ((keyword == "lfc-interval") || + (keyword == "connect-timeout") || + (keyword == "read-timeout") || + (keyword == "write-timeout") || + (keyword == "tcp-user-timeout") || + (keyword == "reconnect-wait-time") || + (keyword == "max-reconnect-tries") || + (keyword == "port") || + (keyword == "max-row-errors")) { + // integer parameters + int64_t int_value; + try { + int_value = boost::lexical_cast<int64_t>(value); + result->set(keyword, isc::data::Element::create(int_value)); + } catch (...) { + LOG_ERROR(database_logger, DATABASE_TO_JSON_INTEGER_ERROR) + .arg(keyword).arg(value); + } + } else if ((keyword == "persist") || + (keyword == "readonly")) { + if (value == "true") { + result->set(keyword, isc::data::Element::create(true)); + } else if (value == "false") { + result->set(keyword, isc::data::Element::create(false)); + } else { + LOG_ERROR(database_logger, DATABASE_TO_JSON_BOOLEAN_ERROR) + .arg(keyword).arg(value); + } + } else if ((keyword == "type") || + (keyword == "user") || + (keyword == "password") || + (keyword == "host") || + (keyword == "name") || + (keyword == "on-fail") || + (keyword == "trust-anchor") || + (keyword == "cert-file") || + (keyword == "key-file") || + (keyword == "cipher-list")) { + result->set(keyword, isc::data::Element::create(value)); + } else { + LOG_ERROR(database_logger, DATABASE_TO_JSON_UNKNOWN_TYPE_ERROR) + .arg(keyword).arg(value); + } + } + + return (result); +} + +isc::data::ElementPtr +DatabaseConnection::toElementDbAccessString(const std::string& dbaccess) { + ParameterMap params = parse(dbaccess); + return (toElement(params)); +} + +DbCallback DatabaseConnection::db_lost_callback_ = 0; +DbCallback DatabaseConnection::db_recovered_callback_ = 0; +DbCallback DatabaseConnection::db_failed_callback_ = 0; + +} // namespace db +} // namespace isc diff --git a/src/lib/database/database_connection.h b/src/lib/database/database_connection.h new file mode 100644 index 0000000..eafc062 --- /dev/null +++ b/src/lib/database/database_connection.h @@ -0,0 +1,280 @@ +// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DATABASE_CONNECTION_H +#define DATABASE_CONNECTION_H + +#include <asiolink/io_service.h> +#include <cc/data.h> +#include <boost/noncopyable.hpp> +#include <boost/shared_ptr.hpp> +#include <exceptions/exceptions.h> +#include <util/reconnect_ctl.h> +#include <functional> +#include <map> +#include <string> + +namespace isc { +namespace db { + +/// @brief Exception thrown if name of database is not specified +class NoDatabaseName : public Exception { +public: + NoDatabaseName(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception thrown on failure to open database +class DbOpenError : public Exception { +public: + DbOpenError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception thrown on failure to execute a database function +class DbOperationError : public Exception { +public: + DbOperationError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception thrown when a specific connection has been rendered unusable +/// either through loss of connectivity or API lib error +class DbConnectionUnusable : public Exception { +public: + DbConnectionUnusable(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + + +/// @brief Invalid type exception +/// +/// Thrown when the factory doesn't recognize the type of the backend. +class InvalidType : public Exception { +public: + InvalidType(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Invalid Timeout +/// +/// Thrown when the timeout specified for the database connection is invalid. +class DbInvalidTimeout : public Exception { +public: + DbInvalidTimeout(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Invalid port number +/// +/// Thrown when the port number specified for the database connection is invalid. +class DbInvalidPort : public Exception { +public: + DbInvalidPort(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Invalid 'readonly' value specification. +/// +/// Thrown when the value of the 'readonly' boolean parameter is invalid. +class DbInvalidReadOnly : public Exception { +public: + DbInvalidReadOnly(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Defines a callback prototype for propagating events upward +typedef std::function<bool (util::ReconnectCtlPtr db_reconnect_ctl)> DbCallback; + +/// @brief Function which returns the IOService that can be used to recover the +/// connection. +/// +/// This accessor is used to lazy retrieve the IOService when the connection is +/// lost. It is useful to retrieve it at a later time to support hook libraries +/// which create managers on load and set IOService later on by using the +/// dhcp4_srv_configured and dhcp6_srv_configured hooks. +typedef std::function<isc::asiolink::IOServicePtr ()> IOServiceAccessor; + +/// @brief Pointer to an instance of IOServiceAccessor +typedef boost::shared_ptr<IOServiceAccessor> IOServiceAccessorPtr; + +/// @brief Common database connection class. +/// +/// This class provides functions that are common for establishing +/// connection with different types of databases; enables operations +/// on access parameters strings. In particular, it provides a way +/// to parse parameters in key=value format. This class is expected +/// to be a base class for all @ref isc::dhcp::LeaseMgr and possibly +/// @ref isc::dhcp::BaseHostDataSource derived classes. +class DatabaseConnection : public boost::noncopyable { +public: + + /// @brief Defines maximum value for time that can be reliably stored. + /// + /// @todo: Is this common for MySQL and Postgres? Maybe we should have + /// specific values for each backend? + /// + /// If I'm still alive I'll be too old to care. You fix it. + static const time_t MAX_DB_TIME; + + /// @brief Database configuration parameter map + typedef std::map<std::string, std::string> ParameterMap; + + /// @brief Constructor + /// + /// @param parameters A data structure relating keywords and values + /// concerned with the database. + /// @param callback The connection recovery callback. + DatabaseConnection(const ParameterMap& parameters, + DbCallback callback = DbCallback()) + : parameters_(parameters), callback_(callback), unusable_(false) { + } + + /// @brief Destructor + virtual ~DatabaseConnection(){}; + + /// @brief Instantiates a ReconnectCtl based on the connection's + /// reconnect parameters + /// + /// @param timer_name of the timer used for the ReconnectCtl object. + virtual void makeReconnectCtl(const std::string& timer_name); + + /// @brief The reconnect settings. + /// + /// @return The reconnect settings. + util::ReconnectCtlPtr reconnectCtl() { + return (reconnect_ctl_); + } + + /// @brief Returns value of a connection parameter. + /// + /// @param name Name of the parameter which value should be returned. + /// @return Value of one of the connection parameters. + /// @throw BadValue if parameter is not found + std::string getParameter(const std::string& name) const; + + /// @brief Parse database access string + /// + /// Parses the string of "keyword=value" pairs and separates them + /// out into the map. A value of the password parameter may include + /// whitespace in which case it must be surrounded by apostrophes. + /// + /// @param dbaccess Database access string. + /// + /// @return @ref ParameterMap of keyword/value pairs. + static ParameterMap parse(const std::string& dbaccess); + + /// @brief Redact database access string + /// + /// Takes the database parameters and returns a database access string + /// passwords replaced by asterisks. This string is used in log messages. + /// + /// @param parameters Database access parameters (output of "parse"). + /// + /// @return Redacted database access string. + static std::string redactedAccessString(const ParameterMap& parameters); + + /// @brief Convenience method checking if database should be opened with + /// read only access. + /// + /// @return true if "readonly" parameter is specified and set to true; + /// false if "readonly" parameter is not specified or it is specified + /// and set to false. + bool configuredReadOnly() const; + + /// @brief Invokes the connection's lost connectivity callback + /// + /// @return Returns the result of the callback or false if there is no + /// callback. + static bool invokeDbLostCallback(const util::ReconnectCtlPtr& db_reconnect_ctl); + + /// @brief Invokes the connection's restored connectivity callback + /// + /// @return Returns the result of the callback or false if there is no + /// callback. + static bool invokeDbRecoveredCallback(const util::ReconnectCtlPtr& db_reconnect_ctl); + + /// @brief Invokes the connection's restore failed connectivity callback + /// + /// @return Returns the result of the callback or false if there is no + /// callback. + static bool invokeDbFailedCallback(const util::ReconnectCtlPtr& db_reconnect_ctl); + + /// @brief Unparse a parameter map + /// + /// @param params the parameter map to unparse + /// @return a pointer to configuration + static isc::data::ElementPtr toElement(const ParameterMap& params); + + /// @brief Unparse an access string + /// + /// @param dbaccess the database access string + /// @return a pointer to configuration + static isc::data::ElementPtr toElementDbAccessString(const std::string& dbaccess); + + /// @brief Optional callback function to invoke if an opened connection is + /// lost + static DbCallback db_lost_callback_; + + /// @brief Optional callback function to invoke if an opened connection + /// recovery succeeded + static DbCallback db_recovered_callback_; + + /// @brief Optional callback function to invoke if an opened connection + /// recovery failed + static DbCallback db_failed_callback_; + + /// @brief Throws an exception if the connection is not usable. + /// @throw DbConnectionUnusable + void checkUnusable() { + if (unusable_) { + isc_throw (DbConnectionUnusable, "Attempt to use an invalid connection"); + } + } + + /// @brief Flag which indicates if connection is unusable. + /// + /// @return true if the connection is unusable, false otherwise + bool isUnusable() { + return (unusable_); + } + +protected: + /// @brief Sets the unusable flag to true. + void markUnusable() { unusable_ = true; } + +private: + + /// @brief List of parameters passed in dbconfig + /// + /// That will be mostly used for storing database name, username, + /// password and other parameters required for DB access. It is not + /// intended to keep any DHCP-related parameters. + ParameterMap parameters_; + +protected: + + /// @brief The callback used to recover the connection. + DbCallback callback_; + +private: + + /// @brief Indicates if the connection can no longer be used for normal + /// operations. Typically a connection is marked unusable after an unrecoverable + /// DB error. There may be a time when the connection exists, after + /// such an event, during which it cannot be used for anything beyond checking + /// parameters and error information. This flag can be used as a guard in + /// code to prevent inadvertent use of a broken connection. + bool unusable_; + + /// @brief Reconnect settings. + util::ReconnectCtlPtr reconnect_ctl_; +}; + +} // namespace db +} // namespace isc + +#endif // DATABASE_CONNECTION_H diff --git a/src/lib/database/db_exceptions.h b/src/lib/database/db_exceptions.h new file mode 100644 index 0000000..9656ab0 --- /dev/null +++ b/src/lib/database/db_exceptions.h @@ -0,0 +1,99 @@ +// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DB_EXCEPTIONS_H +#define DB_EXCEPTIONS_H + +#include <exceptions/exceptions.h> + +namespace isc { +namespace db { + +/// @brief Multiple lease records found where one expected +class MultipleRecords : public Exception { +public: + MultipleRecords(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Data is truncated +class DataTruncated : public Exception { +public: + DataTruncated(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Database duplicate entry error +class DuplicateEntry : public Exception { +public: + DuplicateEntry(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Key is NULL but was specified NOT NULL +class NullKeyError : public Exception { +public: + NullKeyError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Attempt to modify data in read-only database. +class ReadOnlyDb : public Exception { +public: + ReadOnlyDb(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Upper bound address is lower than lower bound address while +/// retrieving a range of leases. +class InvalidRange : public Exception { +public: + InvalidRange(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Invalid address family used as input to Lease Manager. +class InvalidAddressFamily : public Exception { +public: + InvalidAddressFamily(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Error detected in the database configuration. +class DbConfigError : public Exception { +public: + DbConfigError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Error when specified database could not be found in the server +/// configuration. +class NoSuchDatabase : public Exception { +public: + NoSuchDatabase(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Specification of the database backend to be used yields +/// multiple results. +class AmbiguousDatabase : public Exception { +public: + AmbiguousDatabase(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Thrown when it is expected that some rows are affected, +/// usually during a DELETE or an UPDATE, but none are. +class NoRowsAffected : public Exception { +public: + NoRowsAffected(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +} // namespace isc +} // namespace db + +#endif diff --git a/src/lib/database/db_log.cc b/src/lib/database/db_log.cc new file mode 100644 index 0000000..395bd57 --- /dev/null +++ b/src/lib/database/db_log.cc @@ -0,0 +1,137 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// Defines the logger used by the NSAS + +#include <config.h> + +#include <exceptions/exceptions.h> +#include <database/db_log.h> +#include <database/db_messages.h> + +using namespace isc::log; + +namespace isc { +namespace db { + +/// @brief Database logging levels. +const int DB_DBG_TRACE_DETAIL = isc::log::DBGLVL_TRACE_DETAIL; + +/// @brief Map of translated messages. +const DbLogger::MessageMap db_message_map = { + { DB_INVALID_ACCESS, DATABASE_INVALID_ACCESS }, + + { PGSQL_DEALLOC_ERROR, DATABASE_PGSQL_DEALLOC_ERROR }, + { PGSQL_FATAL_ERROR, DATABASE_PGSQL_FATAL_ERROR }, + { PGSQL_START_TRANSACTION, DATABASE_PGSQL_START_TRANSACTION }, + { PGSQL_COMMIT, DATABASE_PGSQL_COMMIT }, + { PGSQL_ROLLBACK, DATABASE_PGSQL_ROLLBACK }, + { PGSQL_CREATE_SAVEPOINT, DATABASE_PGSQL_CREATE_SAVEPOINT }, + { PGSQL_ROLLBACK_SAVEPOINT, DATABASE_PGSQL_ROLLBACK_SAVEPOINT }, + { PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED, DATABASE_PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED }, + + { MYSQL_FATAL_ERROR, DATABASE_MYSQL_FATAL_ERROR }, + { MYSQL_START_TRANSACTION, DATABASE_MYSQL_START_TRANSACTION }, + { MYSQL_COMMIT, DATABASE_MYSQL_COMMIT }, + { MYSQL_ROLLBACK, DATABASE_MYSQL_ROLLBACK }, +}; + +isc::log::Logger database_logger("database"); + +DbLogger db_logger_translator(database_logger, db_message_map); + +DbLoggerStack db_logger_stack = { db_logger_translator }; + +std::mutex db_logger_mutex; + +const MessageID& +DbLogger::translateMessage(const DbMessageID& id) const { + try { + return (map_.at(id)); + } catch (const std::out_of_range&) { + isc_throw(isc::Unexpected, "can't map message: " << id); + } +} + +void checkDbLoggerStack() { + if (db_logger_stack.empty()) { + isc_throw(isc::Unexpected, "database logger stack is empty"); + } +} + +template <> +isc::log::Logger::Formatter +DB_LOG<fatal>::formatter(DbMessageID const message_id, + int const /* debug_level = 0 */) { + return isc::db::db_logger_stack.back().logger_.fatal( + isc::db::db_logger_stack.back().translateMessage(message_id)); +} + +template <> +isc::log::Logger::Formatter +DB_LOG<error>::formatter(DbMessageID const message_id, + int const /* debug_level = 0 */) { + return isc::db::db_logger_stack.back().logger_.error( + isc::db::db_logger_stack.back().translateMessage(message_id)); +} + +template <> +isc::log::Logger::Formatter +DB_LOG<warn>::formatter(DbMessageID const message_id, + int const /* debug_level = 0 */) { + return isc::db::db_logger_stack.back().logger_.warn( + isc::db::db_logger_stack.back().translateMessage(message_id)); +} + +template <> +isc::log::Logger::Formatter +DB_LOG<info>::formatter(DbMessageID const message_id, + int const /* debug_level = 0 */) { + return isc::db::db_logger_stack.back().logger_.info( + isc::db::db_logger_stack.back().translateMessage(message_id)); +} + +template <> +isc::log::Logger::Formatter +DB_LOG<debug>::formatter(DbMessageID const message_id, + int const debug_level /* = 0 */) { + return isc::db::db_logger_stack.back().logger_.debug( + debug_level, + isc::db::db_logger_stack.back().translateMessage(message_id)); +} + +template <> +bool +DB_LOG<fatal>::isEnabled(int const /* debug_level = 0 */) const { + return db_logger_stack.back().logger_.isFatalEnabled(); +} + +template <> +bool +DB_LOG<error>::isEnabled(int const /* debug_level = 0 */) const { + return db_logger_stack.back().logger_.isErrorEnabled(); +} + +template <> +bool +DB_LOG<warn>::isEnabled(int const /* debug_level = 0 */) const { + return db_logger_stack.back().logger_.isWarnEnabled(); +} + +template <> +bool +DB_LOG<info>::isEnabled(int const /* debug_level = 0 */) const { + return db_logger_stack.back().logger_.isInfoEnabled(); +} + +template <> +bool +DB_LOG<debug>::isEnabled(int const debug_level /* = 0 */) const { + return db_logger_stack.back().logger_.isDebugEnabled(debug_level); +} + +} // namespace db +} // namespace isc diff --git a/src/lib/database/db_log.h b/src/lib/database/db_log.h new file mode 100644 index 0000000..a873df7 --- /dev/null +++ b/src/lib/database/db_log.h @@ -0,0 +1,215 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DB_LOG_H +#define DB_LOG_H + +#include <log/macros.h> + +#include <map> +#include <mutex> +#include <list> + +/// @file db_log.h +/// +/// We want to reuse the database backend connection and exchange code +/// for other uses, in particular for hook libraries. But this code +/// includes some calls to the system logger for debug and uncommon +/// cases and of course we do not want to get log messages from +/// a hook library to seem to come from DHCP server core. +/// +/// The solution is to use a database logger which calls the right +/// logger with mapped messages. + +namespace isc { +namespace db { + +///@{ +/// @brief Database logging levels +/// +/// Defines the levels used to output debug messages in the database +/// support. Note that higher numbers equate to more verbose (and detailed) +/// output. + +/// @brief Additional information +/// +/// Record detailed tracing. This is generally reserved for tracing access to +/// the lease database. +extern const int DB_DBG_TRACE_DETAIL; + +///@} + +/// @brief Common database library logger. +extern isc::log::Logger database_logger; + +///@{ +/// @brief Database messages +/// +enum DbMessageID { + DB_INVALID_ACCESS, + + PGSQL_DEALLOC_ERROR, + PGSQL_FATAL_ERROR, + PGSQL_START_TRANSACTION, + PGSQL_COMMIT, + PGSQL_ROLLBACK, + PGSQL_CREATE_SAVEPOINT, + PGSQL_ROLLBACK_SAVEPOINT, + PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED, + + MYSQL_FATAL_ERROR, + MYSQL_START_TRANSACTION, + MYSQL_COMMIT, + MYSQL_ROLLBACK, +}; +///@} + +/// @brief Database logger class +/// +class DbLogger { +public: + /// @brief Translation map type + typedef std::map<DbMessageID, isc::log::MessageID> MessageMap; + + /// @brief Constructor + /// + /// @param logger logger which will be called + /// @param map message id translation map + DbLogger(isc::log::Logger& logger, const MessageMap& map) + : logger_(logger), map_(map) { + } + + /// @brief Translate message + /// + /// @param id database message id + /// @return logger message + /// @throw Unexpected if the id is not in the message map + const isc::log::MessageID& translateMessage(const DbMessageID& id) const; + + /// @brief The logger + isc::log::Logger& logger_; + + /// @brief The translation map + const MessageMap& map_; +}; + +/// @brief Database logger stack +typedef std::list<DbLogger> DbLoggerStack; + +/// @brief Global database logger stack (initialized to database logger) +extern DbLoggerStack db_logger_stack; + +/// @brief Global mutex to protect logger stack +extern std::mutex db_logger_mutex; + +/// @brief Check database logger stack +/// +/// @throw Unexpected if the stack is empty +void checkDbLoggerStack(); + +/// @brief log type enumerations for use in DB_LOG specializations +enum log_type_t { + fatal, + error, + warn, + info, + debug, +}; + +/// @brief DB_LOG_* logic +template <log_type_t log_type> +struct DB_LOG { + /// @brief To preserve the old way of logging, this constructor facilitates + /// initiating the DB_LOG_* chain call. + DB_LOG(DbMessageID const message_id, int const debug_level = 0) { + std::lock_guard<std::mutex> lock(isc::db::db_logger_mutex); + isc::db::checkDbLoggerStack(); + if (isEnabled()) { + formatter_ = formatter(message_id, debug_level); + } + } + + /// @brief Pass parameters to replace logger placeholders. + /// + /// @param first the parameter to be processed now + /// @param args the parameters to be processes in recursive calls + /// + /// @return reference to this object so that these calls may be chained. + template <typename T, typename... Args> + DB_LOG& arg(T first, Args... args) { + formatter_.arg(first); + return arg(args...); + } + + /// @brief The last invocation of the arg() which is without parameters. + /// + /// Required when using variadic arguments. + /// + /// @return reference to this object so that these calls may be chained. + DB_LOG& arg() { + return *this; + } + +private: + /// @brief Initializes the logging formatter. + /// + /// @param message_id one of the DbMessageID enums + /// @param debug_level one of debug levels specified in log_dbglevels.h + /// + /// @return the formatter responsible for logging + isc::log::Logger::Formatter + formatter(DbMessageID const message_id, int const debug_level = 0); + + /// @brief Check if the logger is ready to log. + /// + /// @param debug_level required only for debug log type + /// + /// @return true if the logger is enabled, false otherwise + bool isEnabled(int const debug_level = 0) const; + + /// @brief the formatter responsible for logging + isc::log::Logger::Formatter formatter_; +}; + +/// @brief all DB_LOG specializations +/// @{ +struct DB_LOG_FATAL : DB_LOG<fatal> { + DB_LOG_FATAL(DbMessageID const message_id) : DB_LOG(message_id) { + } +}; + +struct DB_LOG_ERROR : DB_LOG<error> { + DB_LOG_ERROR(DbMessageID const message_id) : DB_LOG(message_id) { + } +}; + +struct DB_LOG_WARN : DB_LOG<warn> { + DB_LOG_WARN(DbMessageID const message_id) : DB_LOG(message_id) { + } +}; + +struct DB_LOG_INFO : DB_LOG<info> { + DB_LOG_INFO(DbMessageID const message_id) : DB_LOG(message_id) { + } +}; + +struct DB_LOG_DEBUG : DB_LOG<debug> { + DB_LOG_DEBUG(int const debug_level, DbMessageID const message_id) + : DB_LOG(message_id, debug_level) { + } +}; +///@} + +/// @brief DHCP server database message map +extern const db::DbLogger::MessageMap db_message_map; + +/// @brief Database logger translator. +extern db::DbLogger db_logger_translator; + +} // namespace db +} // namespace isc + +#endif // DB_LOG_H diff --git a/src/lib/database/db_messages.cc b/src/lib/database/db_messages.cc new file mode 100644 index 0000000..b680baa --- /dev/null +++ b/src/lib/database/db_messages.cc @@ -0,0 +1,55 @@ +// File created from ../../../src/lib/database/db_messages.mes + +#include <cstddef> +#include <log/message_types.h> +#include <log/message_initializer.h> + +namespace isc { +namespace db { + +extern const isc::log::MessageID DATABASE_INVALID_ACCESS = "DATABASE_INVALID_ACCESS"; +extern const isc::log::MessageID DATABASE_MYSQL_COMMIT = "DATABASE_MYSQL_COMMIT"; +extern const isc::log::MessageID DATABASE_MYSQL_FATAL_ERROR = "DATABASE_MYSQL_FATAL_ERROR"; +extern const isc::log::MessageID DATABASE_MYSQL_ROLLBACK = "DATABASE_MYSQL_ROLLBACK"; +extern const isc::log::MessageID DATABASE_MYSQL_START_TRANSACTION = "DATABASE_MYSQL_START_TRANSACTION"; +extern const isc::log::MessageID DATABASE_PGSQL_COMMIT = "DATABASE_PGSQL_COMMIT"; +extern const isc::log::MessageID DATABASE_PGSQL_CREATE_SAVEPOINT = "DATABASE_PGSQL_CREATE_SAVEPOINT"; +extern const isc::log::MessageID DATABASE_PGSQL_DEALLOC_ERROR = "DATABASE_PGSQL_DEALLOC_ERROR"; +extern const isc::log::MessageID DATABASE_PGSQL_FATAL_ERROR = "DATABASE_PGSQL_FATAL_ERROR"; +extern const isc::log::MessageID DATABASE_PGSQL_ROLLBACK = "DATABASE_PGSQL_ROLLBACK"; +extern const isc::log::MessageID DATABASE_PGSQL_ROLLBACK_SAVEPOINT = "DATABASE_PGSQL_ROLLBACK_SAVEPOINT"; +extern const isc::log::MessageID DATABASE_PGSQL_START_TRANSACTION = "DATABASE_PGSQL_START_TRANSACTION"; +extern const isc::log::MessageID DATABASE_PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED = "DATABASE_PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED"; +extern const isc::log::MessageID DATABASE_TO_JSON_BOOLEAN_ERROR = "DATABASE_TO_JSON_BOOLEAN_ERROR"; +extern const isc::log::MessageID DATABASE_TO_JSON_INTEGER_ERROR = "DATABASE_TO_JSON_INTEGER_ERROR"; +extern const isc::log::MessageID DATABASE_TO_JSON_UNKNOWN_TYPE_ERROR = "DATABASE_TO_JSON_UNKNOWN_TYPE_ERROR"; + +} // namespace db +} // namespace isc + +namespace { + +const char* values[] = { + "DATABASE_INVALID_ACCESS", "invalid database access string: %1", + "DATABASE_MYSQL_COMMIT", "committing to MySQL database", + "DATABASE_MYSQL_FATAL_ERROR", "Unrecoverable MySQL error occurred: %1 for <%2>, reason: %3 (error code: %4).", + "DATABASE_MYSQL_ROLLBACK", "rolling back MySQL database", + "DATABASE_MYSQL_START_TRANSACTION", "starting new MySQL transaction", + "DATABASE_PGSQL_COMMIT", "committing to PostgreSQL database", + "DATABASE_PGSQL_CREATE_SAVEPOINT", "creating a new PostgreSQL savepoint: %1", + "DATABASE_PGSQL_DEALLOC_ERROR", "An error occurred deallocating SQL statements while closing the PostgreSQL lease database: %1", + "DATABASE_PGSQL_FATAL_ERROR", "Unrecoverable PostgreSQL error occurred: Statement: <%1>, reason: %2 (error code: %3).", + "DATABASE_PGSQL_ROLLBACK", "rolling back PostgreSQL database", + "DATABASE_PGSQL_ROLLBACK_SAVEPOINT", "rolling back PostgreSQL database to savepoint: $1", + "DATABASE_PGSQL_START_TRANSACTION", "starting a new PostgreSQL transaction", + "DATABASE_PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED", "tcp_user_timeout is not supported in this PostgreSQL version", + "DATABASE_TO_JSON_BOOLEAN_ERROR", "Internal logic error: invalid boolean value found in database connection parameters: %1=%2", + "DATABASE_TO_JSON_INTEGER_ERROR", "Internal logic error: invalid integer value found in database connection parameters: %1=%2", + "DATABASE_TO_JSON_UNKNOWN_TYPE_ERROR", "Internal logic error: unknown element found in database connection parameters: %1=%2", + NULL +}; + +const isc::log::MessageInitializer initializer(values); + +} // Anonymous namespace + diff --git a/src/lib/database/db_messages.h b/src/lib/database/db_messages.h new file mode 100644 index 0000000..ad367e2 --- /dev/null +++ b/src/lib/database/db_messages.h @@ -0,0 +1,31 @@ +// File created from ../../../src/lib/database/db_messages.mes + +#ifndef DB_MESSAGES_H +#define DB_MESSAGES_H + +#include <log/message_types.h> + +namespace isc { +namespace db { + +extern const isc::log::MessageID DATABASE_INVALID_ACCESS; +extern const isc::log::MessageID DATABASE_MYSQL_COMMIT; +extern const isc::log::MessageID DATABASE_MYSQL_FATAL_ERROR; +extern const isc::log::MessageID DATABASE_MYSQL_ROLLBACK; +extern const isc::log::MessageID DATABASE_MYSQL_START_TRANSACTION; +extern const isc::log::MessageID DATABASE_PGSQL_COMMIT; +extern const isc::log::MessageID DATABASE_PGSQL_CREATE_SAVEPOINT; +extern const isc::log::MessageID DATABASE_PGSQL_DEALLOC_ERROR; +extern const isc::log::MessageID DATABASE_PGSQL_FATAL_ERROR; +extern const isc::log::MessageID DATABASE_PGSQL_ROLLBACK; +extern const isc::log::MessageID DATABASE_PGSQL_ROLLBACK_SAVEPOINT; +extern const isc::log::MessageID DATABASE_PGSQL_START_TRANSACTION; +extern const isc::log::MessageID DATABASE_PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED; +extern const isc::log::MessageID DATABASE_TO_JSON_BOOLEAN_ERROR; +extern const isc::log::MessageID DATABASE_TO_JSON_INTEGER_ERROR; +extern const isc::log::MessageID DATABASE_TO_JSON_UNKNOWN_TYPE_ERROR; + +} // namespace db +} // namespace isc + +#endif // DB_MESSAGES_H diff --git a/src/lib/database/db_messages.mes b/src/lib/database/db_messages.mes new file mode 100644 index 0000000..acec88c --- /dev/null +++ b/src/lib/database/db_messages.mes @@ -0,0 +1,105 @@ +# Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$NAMESPACE isc::db + +% DATABASE_INVALID_ACCESS invalid database access string: %1 +This is logged when an attempt has been made to parse a database access string +and the attempt ended in error. The access string in question - which +should be of the form 'keyword=value keyword=value...' is included in +the message. + +% DATABASE_MYSQL_COMMIT committing to MySQL database +The code has issued a commit call. All outstanding transactions will be +committed to the database. Note that depending on the MySQL settings, +the committal may not include a write to disk. + +% DATABASE_MYSQL_FATAL_ERROR Unrecoverable MySQL error occurred: %1 for <%2>, reason: %3 (error code: %4). +An error message indicating that communication with the MySQL database server +has been lost. If automatic recovery has been enabled, then the server will +attempt to recover connectivity. If not, then the server will exit with a +non-zero exit code. The cause of such an error is most likely a network issue +or the MySQL server has gone down. + +% DATABASE_MYSQL_ROLLBACK rolling back MySQL database +The code has issued a rollback call. All outstanding transaction will +be rolled back and not committed to the database. + +% DATABASE_MYSQL_START_TRANSACTION starting new MySQL transaction +A debug message issued when a new MySQL transaction is being started. +This message is typically not issued when inserting data into a +single table because the server doesn't explicitly start +transactions in this case. This message is issued when data is +inserted into multiple tables with multiple INSERT statements +and there may be a need to rollback the whole transaction if +any of these INSERT statements fail. + +% DATABASE_PGSQL_COMMIT committing to PostgreSQL database +The code has issued a commit call. All outstanding transactions will be +committed to the database. Note that depending on the PostgreSQL settings, +the committal may not include a write to disk. + +% DATABASE_PGSQL_CREATE_SAVEPOINT creating a new PostgreSQL savepoint: %1 +The code is issuing a call to create a savepoint within the current +transaction. Database modifications made up to this point will be preserved +should a subsequent call to rollback to this savepoint occurs prior to the +transaction being committed. + +% DATABASE_PGSQL_DEALLOC_ERROR An error occurred deallocating SQL statements while closing the PostgreSQL lease database: %1 +This is an error message issued when a DHCP server (either V4 or V6) experienced +and error freeing database SQL resources as part of closing its connection to +the PostgreSQL database. The connection is closed as part of normal server +shutdown. This error is most likely a programmatic issue that is highly +unlikely to occur or negatively impact server operation. + +% DATABASE_PGSQL_FATAL_ERROR Unrecoverable PostgreSQL error occurred: Statement: <%1>, reason: %2 (error code: %3). +An error message indicating that communication with the PostgreSQL database server +has been lost. If automatic recovery has been enabled, then the server will +attempt to recover the connectivity. If not, then the server will exit with a +non-zero exit code. The cause of such an error is most likely a network issue +or the PostgreSQL server has gone down. + +% DATABASE_PGSQL_ROLLBACK rolling back PostgreSQL database +The code has issued a rollback call. All outstanding transaction will +be rolled back and not committed to the database. + +% DATABASE_PGSQL_ROLLBACK_SAVEPOINT rolling back PostgreSQL database to savepoint: $1 +The code is issuing a call to rollback to the given savepoint. Any database +modifications that were made after the savepoint was created will be rolled back +and not committed to the database. + +% DATABASE_PGSQL_START_TRANSACTION starting a new PostgreSQL transaction +A debug message issued when a new PostgreSQL transaction is being started. +This message is typically not issued when inserting data into a +single table because the server doesn't explicitly start +transactions in this case. This message is issued when data is +inserted into multiple tables with multiple INSERT statements +and there may be a need to rollback the whole transaction if +any of these INSERT statements fail. + +% DATABASE_PGSQL_TCP_USER_TIMEOUT_UNSUPPORTED tcp_user_timeout is not supported in this PostgreSQL version +This warning message is issued when a user has configured the tcp_user_timeout +parameter in the connection to the PostgreSQL database but the installed database +does not support this parameter. It is supported by the PostgreSQL version 12 or later. +The parameter setting will be ignored. + +% DATABASE_TO_JSON_BOOLEAN_ERROR Internal logic error: invalid boolean value found in database connection parameters: %1=%2 +This error message is printed when conversion to JSON of the internal state is requested, +but the connection string contains a boolean parameter with invalid value. It is a programming +error. The software will continue operation, but the returned JSON data will be syntactically +valid, but incomplete. The culprit parameter will not be converted. + +% DATABASE_TO_JSON_INTEGER_ERROR Internal logic error: invalid integer value found in database connection parameters: %1=%2 +This error message is printed when conversion to JSON of the internal state is requested, +but the connection string contains the integer parameter with a wrong value. It is a programming +error. The software will continue operation, but the returned JSON data will be syntactically +valid, but incomplete. The culprit parameter will not be converted. + +% DATABASE_TO_JSON_UNKNOWN_TYPE_ERROR Internal logic error: unknown element found in database connection parameters: %1=%2 +This error message is printed when conversion to JSON of the internal state is requested, +but the connection string contains unrecognized parameter. It is a programming error. +The software will continue operation, but the returned JSON data will be syntactically +valid, but incomplete. The unknown parameter will not be converted. diff --git a/src/lib/database/dbaccess_parser.cc b/src/lib/database/dbaccess_parser.cc new file mode 100644 index 0000000..2048b3b --- /dev/null +++ b/src/lib/database/dbaccess_parser.cc @@ -0,0 +1,293 @@ +// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/database_connection.h> +#include <database/db_exceptions.h> +#include <database/dbaccess_parser.h> + +#include <boost/lexical_cast.hpp> + +#include <map> +#include <string> +#include <utility> + +using namespace std; +using namespace isc::data; + +namespace isc { +namespace db { + + +// Factory function to build the parser +DbAccessParser::DbAccessParser() + : values_() { +} + +// Parse the configuration and check that the various keywords are consistent. +void +DbAccessParser::parse(std::string& access_string, + ConstElementPtr database_config) { + + // To cope with incremental updates, the strategy is: + // 1. Take a copy of the stored keyword/value pairs. + // 2. Update the copy with the passed keywords. + // 3. Perform validation checks on the updated keyword/value pairs. + // 4. If all is OK, update the stored keyword/value pairs. + // 5. Save resulting database access string in the Configuration + // Manager. + + // Note only range checks can fail with a database_config from + // a flex/bison parser. + + // 1. Take a copy of the stored keyword/value pairs. + DatabaseConnection::ParameterMap values_copy = values_; + + int64_t lfc_interval = 0; + int64_t connect_timeout = 0; + int64_t read_timeout = 0; + int64_t write_timeout = 0; + int64_t tcp_user_timeout = 0; + int64_t port = 0; + int64_t max_reconnect_tries = 0; + int64_t reconnect_wait_time = 0; + int64_t max_row_errors = 0; + + // 2. Update the copy with the passed keywords. + for (std::pair<std::string, ConstElementPtr> param : database_config->mapValue()) { + try { + if ((param.first == "persist") || + (param.first == "readonly")) { + values_copy[param.first] = (param.second->boolValue() ? + "true" : "false"); + + } else if (param.first == "lfc-interval") { + lfc_interval = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(lfc_interval); + + } else if (param.first == "connect-timeout") { + connect_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(connect_timeout); + + } else if (param.first == "read-timeout") { + read_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(read_timeout); + + } else if (param.first == "write-timeout") { + write_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(write_timeout); + + } else if (param.first == "tcp-user-timeout") { + tcp_user_timeout = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(tcp_user_timeout); + + } else if (param.first == "max-reconnect-tries") { + max_reconnect_tries = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(max_reconnect_tries); + + } else if (param.first == "reconnect-wait-time") { + reconnect_wait_time = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(reconnect_wait_time); + + } else if (param.first == "port") { + port = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(port); + + } else if (param.first == "max-row-errors") { + max_row_errors = param.second->intValue(); + values_copy[param.first] = + boost::lexical_cast<std::string>(max_row_errors); + } else { + + // all remaining string parameters + // type + // user + // password + // host + // name + // on-fail + // trust-anchor + // cert-file + // key-file + // cipher-list + values_copy[param.first] = param.second->stringValue(); + } + } catch (const isc::data::TypeError& ex) { + // Append position of the element. + isc_throw(DbConfigError, "invalid value type specified for " + "parameter '" << param.first << "' (" + << param.second->getPosition() << ")"); + } + } + + // 3. Perform validation checks on the updated set of keyword/values. + // + // a. Check if the "type" keyword exists and thrown an exception if not. + auto type_ptr = values_copy.find("type"); + if (type_ptr == values_copy.end()) { + isc_throw(DbConfigError, + "database access parameters must " + "include the keyword 'type' to determine type of database " + "to be accessed (" << database_config->getPosition() << ")"); + } + + // b. Check if the 'type' keyword known and throw an exception if not. + // + // Please note when you add a new database backend you have to add + // the new type here and in server grammars. + string dbtype = type_ptr->second; + if ((dbtype != "memfile") && + (dbtype != "mysql") && + (dbtype != "postgresql")) { + ConstElementPtr value = database_config->get("type"); + isc_throw(DbConfigError, "unknown backend database type: " << dbtype + << " (" << value->getPosition() << ")"); + } + + // c. Check that the lfc-interval is within a reasonable range. + if ((lfc_interval < 0) || + (lfc_interval > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("lfc-interval"); + isc_throw(DbConfigError, "lfc-interval value: " << lfc_interval + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + + // d. Check that the timeouts are within a reasonable range. + if ((connect_timeout < 0) || + (connect_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("connect-timeout"); + isc_throw(DbConfigError, "connect-timeout value: " << connect_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if ((read_timeout < 0) || + (read_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("read-timeout"); + isc_throw(DbConfigError, "read-timeout value: " << read_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if (read_timeout > 0 && (dbtype != "mysql")) { + ConstElementPtr value = database_config->get("read-timeout"); + isc_throw(DbConfigError, "read-timeout value is only supported by the mysql backend" + << " (" << value->getPosition() << ")"); + } + if ((write_timeout < 0) || + (write_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("write-timeout"); + isc_throw(DbConfigError, "write-timeout value: " << write_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if (write_timeout > 0 && (dbtype != "mysql")) { + ConstElementPtr value = database_config->get("write-timeout"); + isc_throw(DbConfigError, "write-timeout value is only supported by the mysql backend" + << " (" << value->getPosition() << ")"); + } + if ((tcp_user_timeout < 0) || + (tcp_user_timeout > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("tcp-user-timeout"); + isc_throw(DbConfigError, "tcp-user-timeout value: " << tcp_user_timeout + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + if (tcp_user_timeout > 0 && (dbtype != "postgresql")) { + ConstElementPtr value = database_config->get("tcp-user-timeout"); + isc_throw(DbConfigError, "tcp-user-timeout value is only supported by the postgresql backend" + << " (" << value->getPosition() << ")"); + } + + // e. Check that the port is within a reasonable range. + if ((port < 0) || + (port > std::numeric_limits<uint16_t>::max())) { + ConstElementPtr value = database_config->get("port"); + isc_throw(DbConfigError, "port value: " << port + << " is out of range, expected value: 0.." + << std::numeric_limits<uint16_t>::max() + << " (" << value->getPosition() << ")"); + } + + // f. Check that the max-row-errors is within a reasonable range. + if ((max_row_errors < 0) || + (max_row_errors > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("max-row-errors"); + isc_throw(DbConfigError, "max-row-errors value: " << max_row_errors + << " is out of range, expected value: 0.." + << std::numeric_limits<uint32_t>::max() + << " (" << value->getPosition() << ")"); + } + + // Check that the max-reconnect-tries is reasonable. + if (max_reconnect_tries < 0) { + ConstElementPtr value = database_config->get("max-reconnect-tries"); + isc_throw(DbConfigError, + "max-reconnect-tries cannot be less than zero: (" + << value->getPosition() << ")"); + } + + // Check that the reconnect-wait-time is reasonable. + if ((reconnect_wait_time < 0) || + (reconnect_wait_time > std::numeric_limits<uint32_t>::max())) { + ConstElementPtr value = database_config->get("reconnect-wait-time"); + isc_throw(DbConfigError, "reconnect-wait-time " << reconnect_wait_time + << " must be in range 0...MAX_UINT32 (4294967295) " + << "(" << value->getPosition() << ")"); + } + + // 4. If all is OK, update the stored keyword/value pairs. We do this by + // swapping contents - values_copy is destroyed immediately after the + // operation (when the method exits), so we are not interested in its new + // value. + values_.swap(values_copy); + + // 5. Save the database access string in the Configuration Manager. + access_string = getDbAccessString(); +} + +// Create the database access string +std::string +DbAccessParser::getDbAccessString() const { + + // Construct the database access string from all keywords and values in the + // parameter map where the value is not null. + string dbaccess; + for (auto keyval : values_) { + if (!keyval.second.empty()) { + + // Separate keyword/value pair from predecessor (if there is one). + if (!dbaccess.empty()) { + dbaccess += std::string(" "); + } + + // Add the keyword/value pair to the access string. + auto val = keyval.second; + if (val.find_first_of("\t ") != string::npos){ + val = "'" + val + "'"; + } + dbaccess += (keyval.first + std::string("=") + val); + } + } + + return (dbaccess); +} + +} // namespace db +} // namespace isc diff --git a/src/lib/database/dbaccess_parser.h b/src/lib/database/dbaccess_parser.h new file mode 100644 index 0000000..30a4ec9 --- /dev/null +++ b/src/lib/database/dbaccess_parser.h @@ -0,0 +1,83 @@ +// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DBACCESS_PARSER_H +#define DBACCESS_PARSER_H + +#include <cc/data.h> +#include <cc/simple_parser.h> +#include <database/database_connection.h> +#include <exceptions/exceptions.h> + +#include <string> + +namespace isc { +namespace db { + +/// @brief Parse Database Parameters +/// +/// This class is the parser for the database configuration. This is a +/// map under the top-level "lease-database", "hosts-database" and +/// "config-database" elements, and comprises a map of strings. +class DbAccessParser: public isc::data::SimpleParser { +public: + /// @brief Constructor + DbAccessParser(); + + /// The destructor. + virtual ~DbAccessParser() + {} + + /// @brief Parse configuration value. + /// + /// Parses the set of strings forming the database access specification and + /// checks that all are OK. In particular it checks: + /// + /// - "type" is "memfile", "mysql" or "postgresql" + /// - "lfc-interval" is a number from the range of 0 to 4294967295. + /// - "connect-timeout" is a number from the range of 0 to 4294967295. + /// - "port" is a number from the range of 0 to 65535. + /// + /// Once all has been validated, constructs the database access string. + /// + /// @param [out] access_string Generated database access string. + /// @param database_config The configuration value for the "*-database" + /// identifier. + /// + /// @throw isc::dhcp::DbConfigError The connection parameters or their + /// combination is invalid. + void parse(std::string& access_string, + isc::data::ConstElementPtr database_config); + + /// @brief Get database access parameters + /// + /// Used in testing to check that the configuration information has been + /// parsed correctly. + /// + /// @return Reference to the internal map of keyword/value pairs + /// representing database access information. This is valid only + /// for so long as the parser remains in existence. + const DatabaseConnection::ParameterMap& getDbAccessParameters() const { + return (values_); + } +protected: + + /// @brief Construct database access string + /// + /// Constructs the database access string from the stored parameters. + /// + /// @return Database access string + std::string getDbAccessString() const; + +private: + + DatabaseConnection::ParameterMap values_; ///< Stored parameter values +}; + +} // namespace db +} // namespace isc + +#endif // DBACCESS_PARSER_H diff --git a/src/lib/database/server.cc b/src/lib/database/server.cc new file mode 100644 index 0000000..1b6fcd1 --- /dev/null +++ b/src/lib/database/server.cc @@ -0,0 +1,44 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/server.h> +#include <exceptions/exceptions.h> +#include <boost/make_shared.hpp> + +using namespace isc::db; +using namespace isc::data; + +namespace isc { +namespace db { + +Server::Server(const ServerTag& tag, const std::string& description) + : BaseStampedElement(), server_tag_(tag), description_(description) { + + if (description_.length() > 65536) { + isc_throw(BadValue, "server description must not be longer than" + " 65536 characters"); + } +} + +ServerPtr +Server::create(const ServerTag& tag, const std::string& description) { + return (boost::make_shared<Server>(tag, description)); +} + +ElementPtr +Server::toElement() const { + ElementPtr result = Element::createMap(); + + result->set("server-tag", Element::create(getServerTagAsText())); + result->set("description", Element::create(getDescription())); + + return (result); +} + +} // end of namespace isc::db +} // end of namespace isc diff --git a/src/lib/database/server.h b/src/lib/database/server.h new file mode 100644 index 0000000..8ca8a43 --- /dev/null +++ b/src/lib/database/server.h @@ -0,0 +1,90 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DB_SERVER_H +#define DB_SERVER_H + +#include <cc/base_stamped_element.h> +#include <cc/cfg_to_element.h> +#include <cc/server_tag.h> +#include <boost/shared_ptr.hpp> +#include <string> + +namespace isc { +namespace db { + +class Server; + +/// @brief Shared pointer to the @c Server class. +typedef boost::shared_ptr<Server> ServerPtr; + +/// @brief Represents information about a Kea server in the database. +/// +/// The configuration backend holds the information about the servers +/// for which the backend holds the configuration information. The +/// information includes the server tag (unique name), server description +/// provided by the administrator and the metadata. +/// +/// This class extends the base class with the server description field. +class Server : public data::BaseStampedElement, public data::CfgToElement { +public: + + /// @brief Constructor. + /// + /// @param tag server tag. + /// @param description server description. + Server(const data::ServerTag& tag, const std::string& description); + + /// @brief Factory function to be used to create an instance of the + /// @c Server object. + /// + /// @param tag server tag. + /// @param description server description. + /// + /// @return Pointer to the server instance. + /// @throw BadValue if the server tag exceeds 256 characters or the + /// description exceeds 65536 characters. + static ServerPtr create(const data::ServerTag& tag, + const std::string& description = ""); + + /// @brief Returns server tag. + data::ServerTag getServerTag() const { + return (server_tag_); + } + + /// @brief Returns server tag as text. + /// + /// @return Server tag as text. + std::string getServerTagAsText() const { + return (server_tag_.get()); + } + + /// @brief Returns the description of the server. + /// + /// @return Description of the server or an empty string if no + /// description was specified. + std::string getDescription() const { + return (description_); + } + + /// @brief Unparses server object. + /// + /// @return A pointer to unparsed server configuration. + virtual data::ElementPtr toElement() const; + +private: + + /// @brief Server tag. + data::ServerTag server_tag_; + + /// @brief Description of the server. + std::string description_; +}; + +} // end of namespace isc::db +} // end of namespace isc + +#endif // DB_SERVER_H diff --git a/src/lib/database/server_collection.cc b/src/lib/database/server_collection.cc new file mode 100644 index 0000000..0785bdf --- /dev/null +++ b/src/lib/database/server_collection.cc @@ -0,0 +1,28 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/server_collection.h> + +using namespace isc::data; + +namespace isc { +namespace db { + +ServerPtr +ServerFetcher::get(const ServerCollection& collection, + const ServerTag& server_tag) { + auto& index = collection.get<ServerTagIndexTag>(); + auto s = index.find(server_tag.get()); + if (s != index.end()) { + return (*s); + } + return (ServerPtr()); +} + +} // end of namespace isc::db +} // end of namespace isc diff --git a/src/lib/database/server_collection.h b/src/lib/database/server_collection.h new file mode 100644 index 0000000..fe9d91f --- /dev/null +++ b/src/lib/database/server_collection.h @@ -0,0 +1,53 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DB_SERVER_COLLECTION_H +#define DB_SERVER_COLLECTION_H + +#include <database/server.h> +#include <boost/multi_index/mem_fun.hpp> +#include <boost/multi_index/indexed_by.hpp> +#include <boost/multi_index/ordered_index.hpp> +#include <boost/multi_index_container.hpp> + +namespace isc { +namespace db { + +/// @brief Tag identifying an index by server tag. +struct ServerTagIndexTag { }; + +/// @brief Multi index container for @c Server. +/// +/// It merely contains one index at the moment, but the number of +/// indexes is likely to grow. +typedef boost::multi_index_container< + ServerPtr, + boost::multi_index::indexed_by< + boost::multi_index::ordered_unique< + boost::multi_index::tag<ServerTagIndexTag>, + boost::multi_index::const_mem_fun<Server, std::string, + &Server::getServerTagAsText> + > + > +> ServerCollection; + +/// @brief Utility class used to fetch @c Server objects from the +/// @c ServerCollection. +class ServerFetcher { +public: + + /// @brief Fetches server from the collection by tag. + /// + /// @return Pointer to the @c Server object or null if no such object + /// was found. + static ServerPtr get(const ServerCollection& collection, + const data::ServerTag& server_tag); +}; + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/database/server_selector.cc b/src/lib/database/server_selector.cc new file mode 100644 index 0000000..18fbdbd --- /dev/null +++ b/src/lib/database/server_selector.cc @@ -0,0 +1,51 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/server_selector.h> +#include <exceptions/exceptions.h> + +using namespace isc::data; + +namespace isc { +namespace db { + +ServerSelector +ServerSelector::MULTIPLE(const std::set<std::string>& server_tags) { + if (server_tags.empty()) { + isc_throw(InvalidOperation, "ServerSelector: expecting at least one" + " server tag"); + } + + std::set<ServerTag> tags; + + // Create a set of tags from strings. + for (auto tag : server_tags) { + tags.insert(ServerTag(tag)); + } + + ServerSelector selector(tags); + return (selector); +} + +ServerSelector::ServerSelector(const Type& type) + : type_(type), tags_() { + if (type_ == Type::ALL) { + tags_.insert(ServerTag()); + } +} + +ServerSelector::ServerSelector(const ServerTag& server_tag) + : type_(server_tag.amAll() ? Type::ALL : Type::SUBSET), tags_({server_tag}) { +} + +ServerSelector::ServerSelector(const std::set<ServerTag>& server_tags) + : type_(Type::SUBSET), tags_(server_tags) { +} + +} // end of namespace isc::db +} // end of namespace isc diff --git a/src/lib/database/server_selector.h b/src/lib/database/server_selector.h new file mode 100644 index 0000000..a9dcccc --- /dev/null +++ b/src/lib/database/server_selector.h @@ -0,0 +1,165 @@ +// Copyright (C) 2018-2019,2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef SERVER_SELECTOR_H +#define SERVER_SELECTOR_H + +#include <cc/server_tag.h> +#include <set> +#include <string> + +namespace isc { +namespace db { + +/// @brief Server selector for associating objects in a database with +/// specific servers. +/// +/// Configuration information stored in the configuration backends can be +/// associated with selected servers, all servers or no particular server. +/// For example: a particular subnet definition in the database may be +/// associated with one server or can be shared by multiple servers. +/// In the latter case, a subnet may be associated with a subset of +/// servers or all servers. An administrator may also add the +/// configuration data into the database and do not associate this data +/// with any particular server. +/// +/// When fetching the configuration data from a database or when storing +/// data in the database there is a need to specify which servers this +/// data is associated with. The @c ServerSelector class represents +/// such associations. +/// +/// It includes three modes of selection: UNASSIGNED, ALL and SUBSET and +/// several factory functions making associations described above. +/// +/// The @c ServerSelector class should be used in objects derived from +/// @c BaseConfigBackendPool and in objects derived from +/// @c BaseConfigBackend to indicate which servers the specific calls +/// exposed by these objects refer to. +/// +/// @todo Add server selector for selecting only those configuration +/// elements that are associated with all servers (and not with any +/// particular server tags). Translating this to SQL queries it would +/// probably be an empty or non-existing server tag. +class ServerSelector { +public: + + /// @brief Type of the server selection. + enum class Type { + UNASSIGNED, + ALL, + SUBSET, + ANY + }; + + /// @brief Factory returning "unassigned" server selector. + static ServerSelector UNASSIGNED() { + ServerSelector selector(Type::UNASSIGNED); + return (selector); + } + + /// @brief Factory returning "all servers" selector. + static ServerSelector ALL() { + ServerSelector selector(Type::ALL); + return (selector); + } + + /// @brief Factory returning selector of one server. + /// + /// @param server_tag tag of the single server to be selected. + static ServerSelector ONE(const std::string& server_tag) { + ServerSelector selector((data::ServerTag(server_tag))); + return (selector); + } + + /// @brief Factory returning "multiple servers" selector. + /// + /// @param server_tags set of server tags to be selected. + /// @throw InvalidOperation if no server tags provided. + static ServerSelector MULTIPLE(const std::set<std::string>& server_tags); + + /// @brief Factory returning "any server" selector. + static ServerSelector ANY() { + ServerSelector selector(Type::ANY); + return (selector); + } + + /// @brief Returns type of the selector. + Type getType() const { + return (type_); + } + + /// @brief Returns tags associated with the selector. + /// + /// @return server tags for multiple selections and for one server, + /// empty set for all servers and and unassigned. + std::set<data::ServerTag> getTags() const { + return (tags_); + } + + /// @brief Convenience method checking if the server selector has no tags. + /// + /// @return true when the server selector has no tags, false otherwise. + bool hasNoTags() const { + return (tags_.empty()); + } + + /// @brief Convenience method checking if the server selector is "unassigned". + /// + /// @return true if the selector is "unassigned", false otherwise. + bool amUnassigned() const { + return (getType() == Type::UNASSIGNED); + } + + /// @brief Convenience method checking if the server selector is "all". + /// + /// @return true if the selector is "all", false otherwise. + bool amAll() const { + return (getType() == Type::ALL); + } + + /// @brief Convenience method checking if the server selector is "any". + /// + /// @return true if the selector is "any", false otherwise. + bool amAny() const { + return (getType() == Type::ANY); + } + + /// @brief Convenience method checking if the server selector has multiple tags. + /// + /// @return true if it has multiple tags, false otherwise. + bool hasMultipleTags() const { + return (tags_.size() > 1); + } + +private: + + /// @brief Constructor used for "unassigned" and "all" selection types. + /// + /// @param type selector type. + explicit ServerSelector(const Type& type); + + /// @brief Constructor used for selecting a single server. + /// + /// @param server_tag tag of the server to be selected. + explicit ServerSelector(const data::ServerTag& server_tag); + + /// @brief Constructor used for selecting multiple servers. + /// + /// @param server_tags set of server tags. + explicit ServerSelector(const std::set<data::ServerTag>& server_tags); + + /// @brief Selection type used. + Type type_; + + /// @brief Holds tags of explicitly selected servers. + std::set<data::ServerTag> tags_; +}; + + +} // end of namespace isc::db +} // end of namespace isc + +#endif diff --git a/src/lib/database/tests/Makefile.am b/src/lib/database/tests/Makefile.am new file mode 100644 index 0000000..f4cf4f0 --- /dev/null +++ b/src/lib/database/tests/Makefile.am @@ -0,0 +1,44 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +AM_CPPFLAGS += $(BOOST_INCLUDES) +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +CLEANFILES = *.gcno *.gcda + +TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST +TESTS += libdatabase_unittests + +libdatabase_unittests_SOURCES = audit_entry_unittest.cc +libdatabase_unittests_SOURCES += backend_selector_unittest.cc +libdatabase_unittests_SOURCES += database_connection_unittest.cc +libdatabase_unittests_SOURCES += database_log_unittest.cc +libdatabase_unittests_SOURCES += dbaccess_parser_unittest.cc +libdatabase_unittests_SOURCES += run_unittests.cc +libdatabase_unittests_SOURCES += server_unittest.cc +libdatabase_unittests_SOURCES += server_selector_unittest.cc + +libdatabase_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +libdatabase_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) + +libdatabase_unittests_LDADD = $(top_builddir)/src/lib/database/libkea-database.la +libdatabase_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la +libdatabase_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +libdatabase_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +libdatabase_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +libdatabase_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +libdatabase_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libdatabase_unittests_LDADD += $(LOG4CPLUS_LIBS) $(BOOST_LIBS) $(GTEST_LDADD) + +endif + +noinst_PROGRAMS = $(TESTS) diff --git a/src/lib/database/tests/Makefile.in b/src/lib/database/tests/Makefile.in new file mode 100644 index 0000000..a1c7c64 --- /dev/null +++ b/src/lib/database/tests/Makefile.in @@ -0,0 +1,1130 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +TESTS = $(am__EXEEXT_1) +@HAVE_GTEST_TRUE@am__append_1 = libdatabase_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/lib/database/tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_cpp20.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_netconf.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@HAVE_GTEST_TRUE@am__EXEEXT_1 = libdatabase_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +am__libdatabase_unittests_SOURCES_DIST = audit_entry_unittest.cc \ + backend_selector_unittest.cc database_connection_unittest.cc \ + database_log_unittest.cc dbaccess_parser_unittest.cc \ + run_unittests.cc server_unittest.cc \ + server_selector_unittest.cc +@HAVE_GTEST_TRUE@am_libdatabase_unittests_OBJECTS = libdatabase_unittests-audit_entry_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libdatabase_unittests-backend_selector_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libdatabase_unittests-database_connection_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libdatabase_unittests-database_log_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libdatabase_unittests-dbaccess_parser_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libdatabase_unittests-run_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libdatabase_unittests-server_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ libdatabase_unittests-server_selector_unittest.$(OBJEXT) +libdatabase_unittests_OBJECTS = $(am_libdatabase_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@libdatabase_unittests_DEPENDENCIES = $(top_builddir)/src/lib/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/testutils/libkea-testutils.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libdatabase_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(libdatabase_unittests_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = \ + ./$(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Po \ + ./$(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Po \ + ./$(DEPDIR)/libdatabase_unittests-database_connection_unittest.Po \ + ./$(DEPDIR)/libdatabase_unittests-database_log_unittest.Po \ + ./$(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Po \ + ./$(DEPDIR)/libdatabase_unittests-run_unittests.Po \ + ./$(DEPDIR)/libdatabase_unittests-server_selector_unittest.Po \ + ./$(DEPDIR)/libdatabase_unittests-server_unittest.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(libdatabase_unittests_SOURCES) +DIST_SOURCES = $(am__libdatabase_unittests_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@ +DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@ +DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_NETCONF = @HAVE_NETCONF@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@ +LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@ +LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@ +LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@ +LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@ +LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@ +LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@ +LIBYANG_LIBS = @LIBYANG_LIBS@ +LIBYANG_PREFIX = @LIBYANG_PREFIX@ +LIBYANG_VERSION = @LIBYANG_VERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_PLUGINS_PATH = @SR_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@ +SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@ +SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@ +SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_PREFIX = @SYSREPO_PREFIX@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + $(BOOST_INCLUDES) \ + -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/lib/config/tests\" +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +CLEANFILES = *.gcno *.gcda +TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) +@HAVE_GTEST_TRUE@libdatabase_unittests_SOURCES = \ +@HAVE_GTEST_TRUE@ audit_entry_unittest.cc \ +@HAVE_GTEST_TRUE@ backend_selector_unittest.cc \ +@HAVE_GTEST_TRUE@ database_connection_unittest.cc \ +@HAVE_GTEST_TRUE@ database_log_unittest.cc \ +@HAVE_GTEST_TRUE@ dbaccess_parser_unittest.cc run_unittests.cc \ +@HAVE_GTEST_TRUE@ server_unittest.cc \ +@HAVE_GTEST_TRUE@ server_selector_unittest.cc +@HAVE_GTEST_TRUE@libdatabase_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@libdatabase_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS) +@HAVE_GTEST_TRUE@libdatabase_unittests_LDADD = $(top_builddir)/src/lib/database/libkea-database.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/testutils/libkea-testutils.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(BOOST_LIBS) \ +@HAVE_GTEST_TRUE@ $(GTEST_LDADD) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/database/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/database/tests/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +libdatabase_unittests$(EXEEXT): $(libdatabase_unittests_OBJECTS) $(libdatabase_unittests_DEPENDENCIES) $(EXTRA_libdatabase_unittests_DEPENDENCIES) + @rm -f libdatabase_unittests$(EXEEXT) + $(AM_V_CXXLD)$(libdatabase_unittests_LINK) $(libdatabase_unittests_OBJECTS) $(libdatabase_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-database_connection_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-database_log_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-run_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-server_selector_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabase_unittests-server_unittest.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libdatabase_unittests-audit_entry_unittest.o: audit_entry_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-audit_entry_unittest.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Tpo -c -o libdatabase_unittests-audit_entry_unittest.o `test -f 'audit_entry_unittest.cc' || echo '$(srcdir)/'`audit_entry_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Tpo $(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='audit_entry_unittest.cc' object='libdatabase_unittests-audit_entry_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-audit_entry_unittest.o `test -f 'audit_entry_unittest.cc' || echo '$(srcdir)/'`audit_entry_unittest.cc + +libdatabase_unittests-audit_entry_unittest.obj: audit_entry_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-audit_entry_unittest.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Tpo -c -o libdatabase_unittests-audit_entry_unittest.obj `if test -f 'audit_entry_unittest.cc'; then $(CYGPATH_W) 'audit_entry_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/audit_entry_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Tpo $(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='audit_entry_unittest.cc' object='libdatabase_unittests-audit_entry_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-audit_entry_unittest.obj `if test -f 'audit_entry_unittest.cc'; then $(CYGPATH_W) 'audit_entry_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/audit_entry_unittest.cc'; fi` + +libdatabase_unittests-backend_selector_unittest.o: backend_selector_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-backend_selector_unittest.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Tpo -c -o libdatabase_unittests-backend_selector_unittest.o `test -f 'backend_selector_unittest.cc' || echo '$(srcdir)/'`backend_selector_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Tpo $(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='backend_selector_unittest.cc' object='libdatabase_unittests-backend_selector_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-backend_selector_unittest.o `test -f 'backend_selector_unittest.cc' || echo '$(srcdir)/'`backend_selector_unittest.cc + +libdatabase_unittests-backend_selector_unittest.obj: backend_selector_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-backend_selector_unittest.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Tpo -c -o libdatabase_unittests-backend_selector_unittest.obj `if test -f 'backend_selector_unittest.cc'; then $(CYGPATH_W) 'backend_selector_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/backend_selector_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Tpo $(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='backend_selector_unittest.cc' object='libdatabase_unittests-backend_selector_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-backend_selector_unittest.obj `if test -f 'backend_selector_unittest.cc'; then $(CYGPATH_W) 'backend_selector_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/backend_selector_unittest.cc'; fi` + +libdatabase_unittests-database_connection_unittest.o: database_connection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-database_connection_unittest.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-database_connection_unittest.Tpo -c -o libdatabase_unittests-database_connection_unittest.o `test -f 'database_connection_unittest.cc' || echo '$(srcdir)/'`database_connection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-database_connection_unittest.Tpo $(DEPDIR)/libdatabase_unittests-database_connection_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='database_connection_unittest.cc' object='libdatabase_unittests-database_connection_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-database_connection_unittest.o `test -f 'database_connection_unittest.cc' || echo '$(srcdir)/'`database_connection_unittest.cc + +libdatabase_unittests-database_connection_unittest.obj: database_connection_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-database_connection_unittest.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-database_connection_unittest.Tpo -c -o libdatabase_unittests-database_connection_unittest.obj `if test -f 'database_connection_unittest.cc'; then $(CYGPATH_W) 'database_connection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/database_connection_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-database_connection_unittest.Tpo $(DEPDIR)/libdatabase_unittests-database_connection_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='database_connection_unittest.cc' object='libdatabase_unittests-database_connection_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-database_connection_unittest.obj `if test -f 'database_connection_unittest.cc'; then $(CYGPATH_W) 'database_connection_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/database_connection_unittest.cc'; fi` + +libdatabase_unittests-database_log_unittest.o: database_log_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-database_log_unittest.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-database_log_unittest.Tpo -c -o libdatabase_unittests-database_log_unittest.o `test -f 'database_log_unittest.cc' || echo '$(srcdir)/'`database_log_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-database_log_unittest.Tpo $(DEPDIR)/libdatabase_unittests-database_log_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='database_log_unittest.cc' object='libdatabase_unittests-database_log_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-database_log_unittest.o `test -f 'database_log_unittest.cc' || echo '$(srcdir)/'`database_log_unittest.cc + +libdatabase_unittests-database_log_unittest.obj: database_log_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-database_log_unittest.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-database_log_unittest.Tpo -c -o libdatabase_unittests-database_log_unittest.obj `if test -f 'database_log_unittest.cc'; then $(CYGPATH_W) 'database_log_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/database_log_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-database_log_unittest.Tpo $(DEPDIR)/libdatabase_unittests-database_log_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='database_log_unittest.cc' object='libdatabase_unittests-database_log_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-database_log_unittest.obj `if test -f 'database_log_unittest.cc'; then $(CYGPATH_W) 'database_log_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/database_log_unittest.cc'; fi` + +libdatabase_unittests-dbaccess_parser_unittest.o: dbaccess_parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-dbaccess_parser_unittest.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Tpo -c -o libdatabase_unittests-dbaccess_parser_unittest.o `test -f 'dbaccess_parser_unittest.cc' || echo '$(srcdir)/'`dbaccess_parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Tpo $(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dbaccess_parser_unittest.cc' object='libdatabase_unittests-dbaccess_parser_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-dbaccess_parser_unittest.o `test -f 'dbaccess_parser_unittest.cc' || echo '$(srcdir)/'`dbaccess_parser_unittest.cc + +libdatabase_unittests-dbaccess_parser_unittest.obj: dbaccess_parser_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-dbaccess_parser_unittest.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Tpo -c -o libdatabase_unittests-dbaccess_parser_unittest.obj `if test -f 'dbaccess_parser_unittest.cc'; then $(CYGPATH_W) 'dbaccess_parser_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dbaccess_parser_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Tpo $(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='dbaccess_parser_unittest.cc' object='libdatabase_unittests-dbaccess_parser_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-dbaccess_parser_unittest.obj `if test -f 'dbaccess_parser_unittest.cc'; then $(CYGPATH_W) 'dbaccess_parser_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/dbaccess_parser_unittest.cc'; fi` + +libdatabase_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-run_unittests.Tpo -c -o libdatabase_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-run_unittests.Tpo $(DEPDIR)/libdatabase_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libdatabase_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +libdatabase_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-run_unittests.Tpo -c -o libdatabase_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-run_unittests.Tpo $(DEPDIR)/libdatabase_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='libdatabase_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi` + +libdatabase_unittests-server_unittest.o: server_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-server_unittest.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-server_unittest.Tpo -c -o libdatabase_unittests-server_unittest.o `test -f 'server_unittest.cc' || echo '$(srcdir)/'`server_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-server_unittest.Tpo $(DEPDIR)/libdatabase_unittests-server_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='server_unittest.cc' object='libdatabase_unittests-server_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-server_unittest.o `test -f 'server_unittest.cc' || echo '$(srcdir)/'`server_unittest.cc + +libdatabase_unittests-server_unittest.obj: server_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-server_unittest.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-server_unittest.Tpo -c -o libdatabase_unittests-server_unittest.obj `if test -f 'server_unittest.cc'; then $(CYGPATH_W) 'server_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/server_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-server_unittest.Tpo $(DEPDIR)/libdatabase_unittests-server_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='server_unittest.cc' object='libdatabase_unittests-server_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-server_unittest.obj `if test -f 'server_unittest.cc'; then $(CYGPATH_W) 'server_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/server_unittest.cc'; fi` + +libdatabase_unittests-server_selector_unittest.o: server_selector_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-server_selector_unittest.o -MD -MP -MF $(DEPDIR)/libdatabase_unittests-server_selector_unittest.Tpo -c -o libdatabase_unittests-server_selector_unittest.o `test -f 'server_selector_unittest.cc' || echo '$(srcdir)/'`server_selector_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-server_selector_unittest.Tpo $(DEPDIR)/libdatabase_unittests-server_selector_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='server_selector_unittest.cc' object='libdatabase_unittests-server_selector_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-server_selector_unittest.o `test -f 'server_selector_unittest.cc' || echo '$(srcdir)/'`server_selector_unittest.cc + +libdatabase_unittests-server_selector_unittest.obj: server_selector_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT libdatabase_unittests-server_selector_unittest.obj -MD -MP -MF $(DEPDIR)/libdatabase_unittests-server_selector_unittest.Tpo -c -o libdatabase_unittests-server_selector_unittest.obj `if test -f 'server_selector_unittest.cc'; then $(CYGPATH_W) 'server_selector_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/server_selector_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabase_unittests-server_selector_unittest.Tpo $(DEPDIR)/libdatabase_unittests-server_selector_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='server_selector_unittest.cc' object='libdatabase_unittests-server_selector_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabase_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o libdatabase_unittests-server_selector_unittest.obj `if test -f 'server_selector_unittest.cc'; then $(CYGPATH_W) 'server_selector_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/server_selector_unittest.cc'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(PROGRAMS) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-database_connection_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-database_log_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-run_unittests.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-server_selector_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-server_unittest.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/libdatabase_unittests-audit_entry_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-backend_selector_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-database_connection_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-database_log_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-dbaccess_parser_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-run_unittests.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-server_selector_unittest.Po + -rm -f ./$(DEPDIR)/libdatabase_unittests-server_unittest.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-TESTS check-am clean clean-generic \ + clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/database/tests/audit_entry_unittest.cc b/src/lib/database/tests/audit_entry_unittest.cc new file mode 100644 index 0000000..1d41872 --- /dev/null +++ b/src/lib/database/tests/audit_entry_unittest.cc @@ -0,0 +1,362 @@ +// Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#include <config.h> +#include <database/audit_entry.h> +#include <exceptions/exceptions.h> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <boost/make_shared.hpp> +#include <gtest/gtest.h> + +using namespace isc; +using namespace isc::db; + +namespace { + +/// @brief Test fixture class for testing @c AuditEntry. +class AuditEntryTest : public ::testing::Test { +public: + + /// @brief Constructor. + AuditEntryTest() + : fixed_time_(now()) { + } + + /// @brief Returns current time. + static boost::posix_time::ptime now() { + return (boost::posix_time::microsec_clock::local_time()); + } + + /// @brief Returns always the same time value. + /// + /// The value is initialized when the test it started. + boost::posix_time::ptime fixedTime() const { + return (fixed_time_); + } + + /// @brief Checks if the given time value is "close" to the + /// current time. + /// + /// This is useful in tests when the @c AuditEntry class sets the + /// modification time to a default value (in its constructor). + /// Because the test doesn't know the exact value to which the + /// modification time is set, it merely checks that this value + /// is earlier than current time and within the range of 1s. + /// + /// @param t time value to be checked. + bool almostEqualTime(const boost::posix_time::ptime& t) const { + auto current = now(); + + // The provided value must be a valid time. + if (t.is_not_a_date_time()) { + ADD_FAILURE() << "provided value is not a date time"; + return (false); + } + + // It must be earlier than current time. + if (t > current) { + ADD_FAILURE() << "provided time value is later than current time"; + return (false); + } + + // The difference must be lower than 1 second. + boost::posix_time::time_duration dur = current - t; + return (dur.total_milliseconds() < 1000); + } + + /// @brief Fixed time value initialized in the constructor. + /// + /// This is used in tests that require the exact time values. + boost::posix_time::ptime fixed_time_; +}; + +// Checks that the modification time value can be cast to a number. +TEST_F(AuditEntryTest, modificationType) { + EXPECT_EQ(0, static_cast<int>(AuditEntry::ModificationType::CREATE)); + EXPECT_EQ(1, static_cast<int>(AuditEntry::ModificationType::UPDATE)); + EXPECT_EQ(2, static_cast<int>(AuditEntry::ModificationType::DELETE)); +} + +// Checks that the audit entry can be created. +TEST_F(AuditEntryTest, create) { + + AuditEntryPtr audit_entry; + + { + SCOPED_TRACE("create with modification time"); + + ASSERT_NO_THROW(audit_entry = AuditEntry::create + ("dhcp4_subnet", 10, AuditEntry::ModificationType::DELETE, + fixedTime(), 123, "deleted subnet 10")); + EXPECT_EQ("dhcp4_subnet", audit_entry->getObjectType()); + EXPECT_EQ(10, audit_entry->getObjectId()); + EXPECT_EQ(AuditEntry::ModificationType::DELETE, audit_entry->getModificationType()); + EXPECT_EQ(fixedTime(), audit_entry->getModificationTime()); + EXPECT_EQ(123, audit_entry->getRevisionId()); + EXPECT_EQ("deleted subnet 10", audit_entry->getLogMessage()); + } + + { + SCOPED_TRACE("create with default modification time"); + + ASSERT_NO_THROW(audit_entry = AuditEntry::create + ("dhcp4_option", 123, AuditEntry::ModificationType::CREATE, + 234, "")); + EXPECT_EQ("dhcp4_option", audit_entry->getObjectType()); + EXPECT_EQ(123, audit_entry->getObjectId()); + EXPECT_EQ(AuditEntry::ModificationType::CREATE, audit_entry->getModificationType()); + EXPECT_TRUE(almostEqualTime(audit_entry->getModificationTime())); + EXPECT_EQ(234, audit_entry->getRevisionId()); + EXPECT_TRUE(audit_entry->getLogMessage().empty()); + } +} + +// Checks that invalid values for the audit entry are rejected. +TEST_F(AuditEntryTest, createFailures) { + { + SCOPED_TRACE("empty object type"); + EXPECT_THROW(AuditEntry("", 10, AuditEntry::ModificationType::DELETE, + fixedTime(), 123, "deleted subnet 10"), + BadValue); + } + + { + SCOPED_TRACE("not a date time"); + EXPECT_THROW(AuditEntry("dhcp4_subnet", 10, + AuditEntry::ModificationType::DELETE, + boost::posix_time::ptime(), 123, + "deleted subnet 10"), + BadValue); + } +} + +/// @brief Test fixture class for testing @c AuditEntryCollection. +class AuditEntryCollectionTest : public AuditEntryTest { +public: + + /// @brief Constructor. + /// + /// Creates a collection of audit entries used in the tests. + AuditEntryCollectionTest() + : AuditEntryTest(), audit_entries_() { + createTestAuditEntries(); + } + + /// @brief Returns a time value being being a specified number of + /// seconds later or earlier than the time returned by @c fixedTime. + /// + /// @param secs offset in seconds since the @c fixedTime output. If + /// the parameter is negative, the returned time is earlier than the + /// fixed time. Otherwise, it is later than fixed time. + boost::posix_time::ptime diffTime(const long secs) { + if (secs < 0) { + return (fixedTime() - boost::posix_time::seconds(-secs)); + } + return (fixedTime() + boost::posix_time::seconds(secs)); + } + + /// @brief Creates an @c AuditEntry instance and inserts it to + /// the @c audit_entries_ collection. + /// + /// @tparam Args types of the arguments to be passed to the @c AuditEntry + /// constructors. + /// @param args arguments to be passed to the @c AuditEntry constructors. + template<typename... Args> + void create(Args&& ...args) { + audit_entries_.insert(boost::make_shared<AuditEntry>(args...)); + } + + /// @brief Creates a collection of @c AuditEntry objects to be used by + /// the tests. + void createTestAuditEntries() { + create("dhcp4_subnet", 10, AuditEntry::ModificationType::CREATE, + diffTime(-5), 100, "added subnet 10"); + create("dhcp4_shared_network", 1, AuditEntry::ModificationType::CREATE, + diffTime(-5), 110, "added shared network 1"); + create("dhcp4_shared_network", 120, AuditEntry::ModificationType::UPDATE, + diffTime(-8), 90, "updated shared network 120"); + create("dhcp4_subnet", 120, AuditEntry::ModificationType::DELETE, + diffTime(8), 130, "deleted subnet 120"); + create("dhcp4_subnet", 1000, AuditEntry::ModificationType::CREATE, + diffTime(4), 120, "created subnet 1000"); + create("dhcp4_option", 15, AuditEntry::ModificationType::UPDATE, + diffTime(16), 140, "updated option 15"); + } + + /// @brief Checks if the returned results range contains an @c AuditEntry + /// with a given object type and identifier. + /// + /// @param object_type expected object type. + /// @param object_id expected object id. + /// @param begin beginning of the results range to be examined. + /// @param end end of the results range to be examined. + template<typename Iterator> + bool includes(const std::string& object_type, const uint64_t object_id, + Iterator begin, Iterator end) { + // Iterate over the results range and look for the entry. + for (auto it = begin; it != end; ++it) { + if (((*it)->getObjectType() == object_type) && + ((*it)->getObjectId() == object_id)) { + // Entry found. + return (true); + } + } + + // Entry not found. + return (false); + } + + /// @brief Checks if the returned results range contains an @c AuditEntry + /// with a given object and modification types, and object identifier. + /// + /// @param object_type expected object type. + /// @param object_id expected object id. + /// @param modification_type expected modification type. + /// @param begin beginning of the results range to be examined. + /// @param end end of the results range to be examined. + template<typename Iterator> + bool includes(const std::string& object_type, const uint64_t object_id, + const AuditEntry::ModificationType& modification_type, + Iterator begin, Iterator end) { + // Iterate over the results range and look for the entry. + for (auto it = begin; it != end; ++it) { + if (((*it)->getObjectType() == object_type) && + ((*it)->getObjectId() == object_id) && + ((*it)->getModificationType() == modification_type)) { + // Entry found. + return (true); + } + } + + // Entry not found. + return (false); + } + + /// @brief Audit entries used in the tests. + AuditEntryCollection audit_entries_; + +}; + +// Checks that entries can be found by object type. +TEST_F(AuditEntryCollectionTest, getByObjectType) { + const auto& object_type_idx = audit_entries_.get<AuditEntryObjectTypeTag>(); + + // Search for "dhcp4_subnet" objects. + auto range = object_type_idx.equal_range("dhcp4_subnet"); + ASSERT_EQ(3, std::distance(range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_subnet", 10, range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_subnet", 120, range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_subnet", 1000, range.first, range.second)); + + // Search for "dhcp4_shared_network" objects. + range = object_type_idx.equal_range("dhcp4_shared_network"); + ASSERT_EQ(2, std::distance(range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_shared_network", 1, range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_shared_network", 120, range.first, range.second)); + + // Search for "dhcp4_option" objects. + range = object_type_idx.equal_range("dhcp4_option"); + ASSERT_EQ(1, std::distance(range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_option", 15, range.first, range.second)); +} + +// Checks that entries can be found by modification time. +TEST_F(AuditEntryCollectionTest, getByModificationTime) { + const auto& mod_time_idx = audit_entries_.get<AuditEntryModificationTimeIdTag>(); + + // Search for objects later than fixed time - 10s. + auto lb = mod_time_idx.lower_bound(diffTime(-10)); + ASSERT_EQ(6, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 10, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_shared_network", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); + + // Search for objects later than fixed time - 7s. + lb = mod_time_idx.lower_bound(diffTime(-7)); + ASSERT_EQ(5, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 10, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); + + // Search for objects later than fixed time - 1s. + lb = mod_time_idx.lower_bound(diffTime(-1)); + ASSERT_EQ(3, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); + + // Search for objects later than fixed time + 6s. + lb = mod_time_idx.lower_bound(diffTime(6)); + ASSERT_EQ(2, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); + + // Search for objects later than fixed time + 10s. + lb = mod_time_idx.lower_bound(diffTime(10)); + ASSERT_EQ(1, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); + + // Search for objects later than fixed time + 20s. + lb = mod_time_idx.lower_bound(diffTime(20)); + // None found. + ASSERT_EQ(0, std::distance(lb, mod_time_idx.end())); +} + +// Checks that entries can be found by modification time and id. +TEST_F(AuditEntryCollectionTest, getByModificationTimeAndId) { + const auto& mod_time_idx = audit_entries_.get<AuditEntryModificationTimeIdTag>(); + + // Search for objects later than added added subnet 10. + auto mod = boost::make_tuple(diffTime(-5), 100 + 1); + auto lb = mod_time_idx.lower_bound(mod); + ASSERT_EQ(4, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); + + // Check the order is time first, id after. + create("dhcp4_subnet", 1000, AuditEntry::ModificationType::UPDATE, + diffTime(-8), 200, "updated subnet 1000"); + lb = mod_time_idx.lower_bound(mod); + ASSERT_EQ(4, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); +} + +// Checks that entries can be found by object id. +TEST_F(AuditEntryCollectionTest, getByObjectId) { + const auto& object_id_idx = audit_entries_.get<AuditEntryObjectIdTag>(); + + // Search for object id 10. + auto range = object_id_idx.equal_range(10); + ASSERT_EQ(1, std::distance(range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_subnet", 10, range.first, range.second)); + + // Add another entry. + create("dhcp4_subnet", 10, AuditEntry::ModificationType::UPDATE, + diffTime(0), 111, "updated subnet 10"); + + // Now search should return two entries. + range = object_id_idx.equal_range(10); + ASSERT_EQ(2, std::distance(range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_subnet", 10, + AuditEntry::ModificationType::CREATE, + range.first, range.second)); + EXPECT_TRUE(includes("dhcp4_subnet", 10, + AuditEntry::ModificationType::UPDATE, + range.first, range.second)); +} + +} diff --git a/src/lib/database/tests/backend_selector_unittest.cc b/src/lib/database/tests/backend_selector_unittest.cc new file mode 100644 index 0000000..d66ca78 --- /dev/null +++ b/src/lib/database/tests/backend_selector_unittest.cc @@ -0,0 +1,197 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +#include <config.h> +#include <database/backend_selector.h> +#include <testutils/test_to_element.h> +#include <boost/scoped_ptr.hpp> +#include <gtest/gtest.h> + +using namespace isc; +using namespace isc::db; +using namespace isc::data; + +namespace { + +// Verifies defaults of the backend selector. +TEST(BackendSelectorTest, defaults) { + BackendSelector sel; + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel.getBackendType()); + EXPECT_TRUE(sel.getBackendHost().empty()); + EXPECT_EQ(0, sel.getBackendPort()); + EXPECT_TRUE(sel.amUnspecified()); + EXPECT_EQ("unspecified", sel.toText()); +} + +// Verifies that the backend selector can be set to "unspecified". +TEST(BackendSelectorTest, unspec) { + BackendSelector sel = BackendSelector::UNSPEC(); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel.getBackendType()); + EXPECT_TRUE(sel.getBackendHost().empty()); + EXPECT_EQ(0, sel.getBackendPort()); + EXPECT_TRUE(sel.amUnspecified()); + EXPECT_EQ("unspecified", sel.toText()); +} + +// Verifies that it is possible to select backend by type. +TEST(BackendSelectorTest, backendTypeSpec) { + boost::scoped_ptr<BackendSelector> sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(BackendSelector::Type::MYSQL)) + ); + EXPECT_EQ(BackendSelector::Type::MYSQL, sel->getBackendType()); + EXPECT_TRUE(sel->getBackendHost().empty()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("type=mysql", sel->toText()); +} + +// Verifies that backend can be selected by host and port. +TEST(BackendSelectorTest, backendHostPortSpec) { + boost::scoped_ptr<BackendSelector> sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector("myhost", 1234)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("myhost", sel->getBackendHost()); + EXPECT_EQ(1234, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=myhost,port=1234", sel->toText()); +} + +// Verifies that backend can be selected by host. +TEST(BackendSelectorTest, backendHostSpec) { + boost::scoped_ptr<BackendSelector> sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector("otherhost")) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("otherhost", sel->getBackendHost()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=otherhost", sel->toText()); +} + +// Verifies that backend becomes unspecified if the access +// map is empty. +TEST(BackendSelectorTest, accessMapTypeUnSpec) { + ElementPtr access_map = Element::createMap(); + boost::scoped_ptr<BackendSelector> sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_TRUE(sel->getBackendHost().empty()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_TRUE(sel->amUnspecified()); + EXPECT_EQ("unspecified", sel->toText()); +} + +// Verifies that backend can be selected by type using access map. +TEST(BackendSelectorTest, accessMapTypeSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("type", Element::create("mysql")); + boost::scoped_ptr<BackendSelector> sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::MYSQL, sel->getBackendType()); + EXPECT_TRUE(sel->getBackendHost().empty()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("type=mysql", sel->toText()); +} + +// Verifies that backend can be selected by host and port using +// access map. +TEST(BackendSelectorTest, accessMapHostPortSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("host", Element::create("myhost")); + access_map->set("port", Element::create(int64_t(1234))); + boost::scoped_ptr<BackendSelector> sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("myhost", sel->getBackendHost()); + EXPECT_EQ(1234, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=myhost,port=1234", sel->toText()); +} + +// Verifies that the backend can be selected by host using access +// map. +TEST(BackendSelectorTest, accessMapHostSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("host", Element::create("myhost")); + boost::scoped_ptr<BackendSelector> sel; + ASSERT_NO_THROW( + sel.reset(new BackendSelector(access_map)) + ); + EXPECT_EQ(BackendSelector::Type::UNSPEC, sel->getBackendType()); + EXPECT_EQ("myhost", sel->getBackendHost()); + EXPECT_EQ(0, sel->getBackendPort()); + EXPECT_FALSE(sel->amUnspecified()); + EXPECT_EQ("host=myhost", sel->toText()); +} + +// Verifies that selecting backend by port only is not possible. +TEST(BackendSelectorTest, accessMapPortSpec) { + ElementPtr access_map = Element::createMap(); + access_map->set("port", Element::create(int64_t(1234))); + boost::scoped_ptr<BackendSelector> sel; + EXPECT_THROW(sel.reset(new BackendSelector(access_map)), + BadValue); +} + +// Tests conversions of strings to backend types. +TEST(BackendSelectorTest, stringToBackendType) { + EXPECT_EQ(BackendSelector::Type::MYSQL, + BackendSelector::stringToBackendType("mysql")); + EXPECT_EQ(BackendSelector::Type::POSTGRESQL, + BackendSelector::stringToBackendType("postgresql")); + EXPECT_THROW(BackendSelector::stringToBackendType("unsupported"), + BadValue); +} + +// Tests conversions of backend types to strings. +TEST(BackendSelectorTest, backendTypeToString) { + EXPECT_EQ("mysql", + BackendSelector::backendTypeToString(BackendSelector::Type::MYSQL)); + EXPECT_EQ("postgresql", + BackendSelector::backendTypeToString(BackendSelector::Type::POSTGRESQL)); +} + +// Tests toElement from backend selectors. +TEST(BackendSelectorTest, backendToElement) { + // Unspecified. + boost::scoped_ptr<BackendSelector> sel(new BackendSelector()); + EXPECT_THROW(sel->toElement(), BadValue); + + // Unspecified type. + sel.reset(new BackendSelector("myhost", 1234)); + EXPECT_THROW(sel->toElement(), BadValue); + + // Type only. + EXPECT_NO_THROW(sel.reset(new BackendSelector(BackendSelector::Type::MYSQL))); + ElementPtr expected = Element::createMap(); + expected->set("type", Element::create("mysql")); + test::runToElementTest<BackendSelector>(expected, *sel); + + // Add host. + expected->set("host", Element::create("myhost")); + EXPECT_NO_THROW(sel.reset(new BackendSelector(expected))); + test::runToElementTest<BackendSelector>(expected, *sel); + + // Add port. + expected->set("port", Element::create(1234L)); + EXPECT_NO_THROW(sel.reset(new BackendSelector(expected))); + test::runToElementTest<BackendSelector>(expected, *sel); +} + +} + diff --git a/src/lib/database/tests/database_connection_unittest.cc b/src/lib/database/tests/database_connection_unittest.cc new file mode 100644 index 0000000..b34f2e8 --- /dev/null +++ b/src/lib/database/tests/database_connection_unittest.cc @@ -0,0 +1,643 @@ +// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <cc/cfg_to_element.h> +#include <cc/data.h> +#include <database/database_connection.h> +#include <database/dbaccess_parser.h> +#include <exceptions/exceptions.h> +#include <gtest/gtest.h> + +#include <functional> + +using namespace isc::data; +using namespace isc::db; +using namespace isc::util; +namespace ph = std::placeholders; + +/// @brief Test fixture for exercising DbLostCallback invocation +class DatabaseConnectionCallbackTest : public ::testing::Test { +public: + /// Constructor + DatabaseConnectionCallbackTest() + : db_reconnect_ctl_(0) { + DatabaseConnection::db_lost_callback_ = 0; + DatabaseConnection::db_recovered_callback_ = 0; + DatabaseConnection::db_failed_callback_ = 0; + } + + /// Destructor + ~DatabaseConnectionCallbackTest() { + DatabaseConnection::db_lost_callback_ = 0; + DatabaseConnection::db_recovered_callback_ = 0; + DatabaseConnection::db_failed_callback_ = 0; + } + + /// @brief Callback to register with a DatabaseConnection + /// + /// @param db_reconnect_ctl ReconnectCtl containing reconnect + /// parameters + bool dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) { + if (!db_reconnect_ctl) { + isc_throw(isc::BadValue, "db_reconnect_ctl should not be null"); + } + + db_reconnect_ctl_ = db_reconnect_ctl; + return (true); + } + + /// @brief Callback to register with a DatabaseConnection + /// + /// @param db_reconnect_ctl ReconnectCtl containing reconnect + /// parameters + bool dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) { + if (!db_reconnect_ctl) { + isc_throw(isc::BadValue, "db_reconnect_ctl should not be null"); + } + + db_reconnect_ctl_ = db_reconnect_ctl; + db_reconnect_ctl_->resetRetries(); + return (true); + } + + /// @brief Callback to register with a DatabaseConnection + /// + /// @param db_reconnect_ctl ReconnectCtl containing reconnect + /// parameters + bool dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) { + if (!db_reconnect_ctl) { + isc_throw(isc::BadValue, "db_reconnect_ctl should not be null"); + } + + db_reconnect_ctl_ = db_reconnect_ctl; + db_reconnect_ctl_->resetRetries(); + return (false); + } + + /// @brief Retainer for the control passed into the callback + ReconnectCtlPtr db_reconnect_ctl_; +}; + +/// @brief getParameter test +/// +/// This test checks if the LeaseMgr can be instantiated and that it +/// parses parameters string properly. +TEST(DatabaseConnectionTest, getParameter) { + + DatabaseConnection::ParameterMap pmap; + pmap[std::string("param1")] = std::string("value1"); + pmap[std::string("param2")] = std::string("value2"); + DatabaseConnection datasrc(pmap); + + EXPECT_EQ("value1", datasrc.getParameter("param1")); + EXPECT_EQ("value2", datasrc.getParameter("param2")); + EXPECT_THROW(datasrc.getParameter("param3"), isc::BadValue); +} + +/// @brief NoDbLostCallback +/// +/// This test verifies that DatabaseConnection::invokeDbLostCallback +/// returns false if the connection has no registered DbLostCallback. +TEST_F(DatabaseConnectionCallbackTest, NoDbLostCallback) { + DatabaseConnection::ParameterMap pmap; + pmap[std::string("type")] = std::string("test"); + pmap[std::string("max-reconnect-tries")] = std::string("3"); + pmap[std::string("reconnect-wait-time")] = std::string("60000"); + DatabaseConnection datasrc(pmap); + datasrc.makeReconnectCtl("timer"); + + bool ret = false; + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl())); + EXPECT_FALSE(ret); + EXPECT_FALSE(db_reconnect_ctl_); +} + +/// @brief NoDbRecoveredCallback +/// +/// This test verifies that DatabaseConnection::invokeDbRecoveredCallback +/// returns false if the connection has no registered DbRecoveredCallback. +TEST_F(DatabaseConnectionCallbackTest, NoDbRecoveredCallback) { + DatabaseConnection::ParameterMap pmap; + pmap[std::string("type")] = std::string("test"); + pmap[std::string("max-reconnect-tries")] = std::string("3"); + pmap[std::string("reconnect-wait-time")] = std::string("60000"); + DatabaseConnection datasrc(pmap); + datasrc.makeReconnectCtl("timer"); + + bool ret = false; + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbRecoveredCallback(datasrc.reconnectCtl())); + EXPECT_FALSE(ret); + EXPECT_FALSE(db_reconnect_ctl_); +} + +/// @brief NoDbFailedCallback +/// +/// This test verifies that DatabaseConnection::invokeDbFailedCallback +/// returns false if the connection has no registered DbFailedCallback. +TEST_F(DatabaseConnectionCallbackTest, NoDbFailedCallback) { + DatabaseConnection::ParameterMap pmap; + pmap[std::string("type")] = std::string("test"); + pmap[std::string("max-reconnect-tries")] = std::string("3"); + pmap[std::string("reconnect-wait-time")] = std::string("60000"); + DatabaseConnection datasrc(pmap); + datasrc.makeReconnectCtl("timer"); + + bool ret = false; + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbFailedCallback(datasrc.reconnectCtl())); + EXPECT_FALSE(ret); + EXPECT_FALSE(db_reconnect_ctl_); +} + +/// @brief dbLostCallback +/// +/// This test verifies that DatabaseConnection::invokeDbLostCallback +/// safely invokes the registered DbCallback. It also tests +/// operation of DbReconnectCtl retry accounting methods. +TEST_F(DatabaseConnectionCallbackTest, dbLostCallback) { + /// Create a Database configuration that includes the reconnect + /// control parameters. + DatabaseConnection::ParameterMap pmap; + pmap[std::string("type")] = std::string("test"); + pmap[std::string("max-reconnect-tries")] = std::string("3"); + pmap[std::string("reconnect-wait-time")] = std::string("60000"); + + /// Install the callback. + DatabaseConnection::db_lost_callback_ = + std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1); + /// Create the connection.. + DatabaseConnection datasrc(pmap); + datasrc.makeReconnectCtl("timer"); + bool ret = false; + + /// We should be able to invoke the callback and get + /// the correct reconnect control parameters from it. + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl())); + EXPECT_TRUE(ret); + ASSERT_TRUE(db_reconnect_ctl_); + ASSERT_EQ("test", db_reconnect_ctl_->backendType()); + ASSERT_EQ("timer", db_reconnect_ctl_->timerName()); + ASSERT_EQ(3, db_reconnect_ctl_->maxRetries()); + ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval()); + + /// Verify that checkRetries() correctly decrements + /// down to zero, and that retriesLeft() returns + /// the correct value. + for (int i = 3; i > 1 ; --i) { + ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft()); + ASSERT_TRUE(db_reconnect_ctl_->checkRetries()); + } + + /// Retries are exhausted, verify that's reflected. + EXPECT_FALSE(db_reconnect_ctl_->checkRetries()); + EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(3, db_reconnect_ctl_->maxRetries()); +} + +/// @brief dbRecoveredCallback +/// +/// This test verifies that DatabaseConnection::invokeDbRecoveredCallback +/// safely invokes the registered DbRecoveredCallback. It also tests +/// operation of DbReconnectCtl retry reset method. +TEST_F(DatabaseConnectionCallbackTest, dbRecoveredCallback) { + /// Create a Database configuration that includes the reconnect + /// control parameters. + DatabaseConnection::ParameterMap pmap; + pmap[std::string("type")] = std::string("test"); + pmap[std::string("max-reconnect-tries")] = std::string("3"); + pmap[std::string("reconnect-wait-time")] = std::string("60000"); + + /// Install the callback. + DatabaseConnection::db_lost_callback_ = + std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1); + DatabaseConnection::db_recovered_callback_ = + std::bind(&DatabaseConnectionCallbackTest::dbRecoveredCallback, this, ph::_1); + /// Create the connection.. + DatabaseConnection datasrc(pmap); + datasrc.makeReconnectCtl("timer"); + bool ret = false; + + /// We should be able to invoke the callback and get + /// the correct reconnect control parameters from it. + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl())); + EXPECT_TRUE(ret); + ASSERT_TRUE(db_reconnect_ctl_); + ASSERT_EQ("test", db_reconnect_ctl_->backendType()); + ASSERT_EQ("timer", db_reconnect_ctl_->timerName()); + ASSERT_EQ(3, db_reconnect_ctl_->maxRetries()); + ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval()); + + /// Verify that checkRetries() correctly decrements + /// down to zero, and that retriesLeft() returns + /// the correct value. + for (int i = 3; i > 1 ; --i) { + ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft()); + ASSERT_TRUE(db_reconnect_ctl_->checkRetries()); + } + + /// Retries are exhausted, verify that's reflected. + EXPECT_FALSE(db_reconnect_ctl_->checkRetries()); + EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(3, db_reconnect_ctl_->maxRetries()); + + /// Reset the reconnect ctl object to verify that it is set again. + db_reconnect_ctl_.reset(); + + /// We should be able to invoke the callback and get + /// the correct reconnect control parameters from it. + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbRecoveredCallback(datasrc.reconnectCtl())); + EXPECT_TRUE(ret); + ASSERT_TRUE(db_reconnect_ctl_); + ASSERT_EQ("test", db_reconnect_ctl_->backendType()); + ASSERT_EQ("timer", db_reconnect_ctl_->timerName()); + EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval()); + + /// Retries are reset, verify that's reflected. + EXPECT_EQ(3, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(3, db_reconnect_ctl_->maxRetries()); +} + +/// @brief dbFailedCallback +/// +/// This test verifies that DatabaseConnection::invokeDbFailedCallback +/// safely invokes the registered DbFailedCallback. +TEST_F(DatabaseConnectionCallbackTest, dbFailedCallback) { + /// Create a Database configuration that includes the reconnect + /// control parameters. + DatabaseConnection::ParameterMap pmap; + pmap[std::string("type")] = std::string("test"); + pmap[std::string("max-reconnect-tries")] = std::string("3"); + pmap[std::string("reconnect-wait-time")] = std::string("60000"); + + /// Install the callback. + DatabaseConnection::db_lost_callback_ = + std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1); + DatabaseConnection::db_failed_callback_ = + std::bind(&DatabaseConnectionCallbackTest::dbFailedCallback, this, ph::_1); + /// Create the connection.. + DatabaseConnection datasrc(pmap); + datasrc.makeReconnectCtl("timer"); + bool ret = false; + + /// We should be able to invoke the callback and get + /// the correct reconnect control parameters from it. + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl())); + EXPECT_TRUE(ret); + ASSERT_TRUE(db_reconnect_ctl_); + ASSERT_EQ("test", db_reconnect_ctl_->backendType()); + ASSERT_EQ("timer", db_reconnect_ctl_->timerName()); + ASSERT_EQ(3, db_reconnect_ctl_->maxRetries()); + ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval()); + + /// Verify that checkRetries() correctly decrements + /// down to zero, and that retriesLeft() returns + /// the correct value. + for (int i = 3; i > 1 ; --i) { + ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft()); + ASSERT_TRUE(db_reconnect_ctl_->checkRetries()); + } + + /// Retries are exhausted, verify that's reflected. + EXPECT_FALSE(db_reconnect_ctl_->checkRetries()); + EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(3, db_reconnect_ctl_->maxRetries()); + + /// Reset the reconnect ctl object to verify that it is set again. + db_reconnect_ctl_.reset(); + + /// We should be able to invoke the callback and get + /// the correct reconnect control parameters from it. + ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbFailedCallback(datasrc.reconnectCtl())); + EXPECT_FALSE(ret); + ASSERT_TRUE(db_reconnect_ctl_); + ASSERT_EQ("test", db_reconnect_ctl_->backendType()); + ASSERT_EQ("timer", db_reconnect_ctl_->timerName()); + EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval()); + + /// Retries are reset, verify that's reflected. + EXPECT_EQ(3, db_reconnect_ctl_->retriesLeft()); + EXPECT_EQ(3, db_reconnect_ctl_->maxRetries()); +} + +// This test checks that a database access string can be parsed correctly. +TEST(DatabaseConnectionTest, parse) { + + DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse( + "user=me password='forbidden' name=kea somethingelse= type=mysql"); + + EXPECT_EQ(5, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("forbidden", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + EXPECT_EQ("", parameters["somethingelse"]); +} + +// This test checks that it is allowed to specify password including whitespaces +// assuming that the password is enclosed in ''. +TEST(DatabaseConnectionTest, parsePasswordWithWhitespace) { + + // Case 1: password in the middle. + DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse( + "user=me password='forbidden with space' name=kea type=mysql"); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("forbidden with space", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // Case 2: password at the end. + parameters = DatabaseConnection::parse("user=me name=kea type=mysql password='forbidden with space'"); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("forbidden with space", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // Case 3: Empty password at the end. + parameters = DatabaseConnection::parse("user=me name=kea type=mysql password=''"); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // Case 4: password at the beginning. + parameters = DatabaseConnection::parse("password='forbidden with space' user=me name=kea type=mysql"); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("forbidden with space", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // Case 5: Empty password at the beginning. + parameters = DatabaseConnection::parse("password='' user=me name=kea type=mysql"); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + + // Case 6: Password is a sole parameter. + parameters = DatabaseConnection::parse("password='forbidden with spaces'"); + + EXPECT_EQ(1, parameters.size()); + EXPECT_EQ("forbidden with spaces", parameters["password"]); +} + +// This test checks that an invalid database access string behaves as expected. +TEST(DatabaseConnectionTest, parseInvalid) { + + // No tokens in the string, so we expect no parameters + std::string invalid = ""; + DatabaseConnection::ParameterMap parameters = DatabaseConnection::parse(invalid); + EXPECT_EQ(0, parameters.size()); + + // With spaces, there are some tokens so we expect invalid parameter + // as there are no equals signs. + invalid = " \t "; + EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter); + + invalid = " noequalshere "; + EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter); + + // Mismatched single quote. + invalid = "password='xyz"; + EXPECT_THROW(DatabaseConnection::parse(invalid), isc::InvalidParameter); + + // A single "=" is valid string, but is placed here as the result is + // expected to be nothing. + invalid = "="; + parameters = DatabaseConnection::parse(invalid); + EXPECT_EQ(1, parameters.size()); + EXPECT_EQ("", parameters[""]); +} + +/// @brief redactedAccessString test +/// +/// Checks that the redacted configuration string includes the password only +/// as a set of asterisks. +TEST(DatabaseConnectionTest, redactAccessString) { + + DatabaseConnection::ParameterMap parameters = + DatabaseConnection::parse("user=me password=forbidden name=kea type=mysql"); + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("forbidden", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // Redact the result. To check, break the redacted string down into its + // components. + std::string redacted = DatabaseConnection::redactedAccessString(parameters); + parameters = DatabaseConnection::parse(redacted); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("*****", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); +} + +/// @brief redactedAccessString test - empty password +/// +/// Checks that the redacted configuration string includes the password only +/// as a set of asterisks, even if the password is null. +TEST(DatabaseConnectionTest, redactAccessStringEmptyPassword) { + + DatabaseConnection::ParameterMap parameters = + DatabaseConnection::parse("user=me name=kea type=mysql password="); + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // Redact the result. To check, break the redacted string down into its + // components. + std::string redacted = DatabaseConnection::redactedAccessString(parameters); + parameters = DatabaseConnection::parse(redacted); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("*****", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // ... and again to check that the position of the empty password in the + // string does not matter. + parameters = DatabaseConnection::parse("user=me password= name=kea type=mysql"); + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + redacted = DatabaseConnection::redactedAccessString(parameters); + parameters = DatabaseConnection::parse(redacted); + + EXPECT_EQ(4, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("*****", parameters["password"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); +} + +/// @brief redactedAccessString test - no password +/// +/// Checks that the redacted configuration string excludes the password if there +/// was no password to begin with. +TEST(DatabaseConnectionTest, redactAccessStringNoPassword) { + + DatabaseConnection::ParameterMap parameters = + DatabaseConnection::parse("user=me name=kea type=mysql"); + EXPECT_EQ(3, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); + + // Redact the result. To check, break the redacted string down into its + // components. + std::string redacted = DatabaseConnection::redactedAccessString(parameters); + parameters = DatabaseConnection::parse(redacted); + + EXPECT_EQ(3, parameters.size()); + EXPECT_EQ("me", parameters["user"]); + EXPECT_EQ("kea", parameters["name"]); + EXPECT_EQ("mysql", parameters["type"]); +} + +// Check that the toElementDbAccessString() handles all valid parameters +// Note that because toElementDbAccessString() utilizes +// toElement() this tests both. +TEST(DatabaseConnection, toElementDbAccessStringValid) { + const char* configs[] = { + "{\n" + "\"connect-timeout\": 200, \n" + "\"on-fail\": \"stop-retry-exit\", \n" + "\"lfc-interval\" : 100, \n" + "\"host\": \"whatevah\", \n" + "\"max-reconnect-tries\": 5, \n" + "\"name\": \"name_str\", \n" + "\"password\": \"password_str\", \n" + "\"persist\" : true, \n" + "\"port\" : 300, \n" + "\"readonly\" : false, \n" + "\"reconnect-wait-time\": 99, \n" + "\"type\": \"memfile\", \n" + "\"user\": \"user_str\", \n" + "\"max-row-errors\": 50, \n" + "\"trust-anchor\": \"my-ca\", \n" + "\"cert-file\": \"my-cert.crt\", \n" + "\"key-file\": \"my-key.key\", \n" + "\"cipher-list\": \"AES\" \n" + "}\n" + }; + + DbAccessParser parser; + std::string access_str; + ConstElementPtr json_elements; + + ASSERT_NO_THROW(json_elements = Element::fromJSON(configs[0])); + ASSERT_NO_THROW(parser.parse(access_str, json_elements)); + + ElementPtr round_trip = DatabaseConnection::toElementDbAccessString(access_str); + + ASSERT_TRUE(json_elements->equals(*round_trip)); +} + +// Check that the toElementDbAccessString() handles Postgres backend +// specific parameters. +TEST(DatabaseConnection, toElementDbAccessStringValidPostgresql) { + const char* configs[] = { + "{\n" + "\"connect-timeout\": 200, \n" + "\"tcp-user-timeout\": 10, \n" + "\"type\": \"postgresql\", \n" + "\"user\": \"user_str\" \n" + "}\n" + }; + + DbAccessParser parser; + std::string access_str; + ConstElementPtr json_elements; + + ASSERT_NO_THROW(json_elements = Element::fromJSON(configs[0])); + ASSERT_NO_THROW(parser.parse(access_str, json_elements)); + + ElementPtr round_trip = DatabaseConnection::toElementDbAccessString(access_str); + + ASSERT_TRUE(json_elements->equals(*round_trip)); +} + +// Check that the toElementDbAccessString() handles MySQL backend +// specific parameters. +TEST(DatabaseConnection, toElementDbAccessStringValidMySql) { + const char* configs[] = { + "{\n" + "\"connect-timeout\": 200, \n" + "\"read-timeout\": 10, \n" + "\"write-timeout\": 10, \n" + "\"type\": \"mysql\", \n" + "\"user\": \"user_str\" \n" + "}\n" + }; + + DbAccessParser parser; + std::string access_str; + ConstElementPtr json_elements; + + ASSERT_NO_THROW(json_elements = Element::fromJSON(configs[0])); + ASSERT_NO_THROW(parser.parse(access_str, json_elements)); + + ElementPtr round_trip = DatabaseConnection::toElementDbAccessString(access_str); + + ASSERT_TRUE(json_elements->equals(*round_trip)); +} + +// Check that toElementDbAccessString() catches invalid parameters. +// Note that because toElementDbAccessString() utilizes +// toElement() this tests both. +// +// Test has been disabled. The recent change turned handling of unknown connection +// string params. Instead of throwing, it logs an error and continues. This gives +// us better resiliency. However, we don't have any means implemented to +// test whether it was printed or not. It's reasonably easy to implement such a +// check, but we don't have time for this. +TEST(DatabaseConnection, DISABLED_toElementDbAccessStringInvalid) { + std::vector<std::string> access_strs = { + "bogus-param=memfile", + "lfc-interval=not-an-integer", + "connect-timeout=not-an-integer", + "port=not-an-integer", + "persist=not-boolean", + "readonly=not-boolean" + }; + + for (auto access_str : access_strs) { + /// @todo: verify that an ERROR is logged. + ASSERT_NO_THROW(DatabaseConnection::toElementDbAccessString(access_str)); + } +} + +// Check that toElementDbAccessString() handles empty access string +// Note that because toElementDbAccessString() utilizes +// toElement() this tests both. +TEST(DatabaseConnection, toElementDbAccessStringEmpty) { + ConstElementPtr elements; + ASSERT_NO_THROW(elements = DatabaseConnection::toElementDbAccessString("")); + ASSERT_TRUE(elements); + ASSERT_EQ(0, elements->size()); +} diff --git a/src/lib/database/tests/database_log_unittest.cc b/src/lib/database/tests/database_log_unittest.cc new file mode 100644 index 0000000..1caa12c --- /dev/null +++ b/src/lib/database/tests/database_log_unittest.cc @@ -0,0 +1,48 @@ +// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <database/db_log.h> + +#include <gtest/gtest.h> + +using isc::db::DB_DBG_TRACE_DETAIL; +using isc::db::DB_INVALID_ACCESS; +using isc::db::DB_LOG_FATAL; +using isc::db::DB_LOG_ERROR; +using isc::db::DB_LOG_WARN; +using isc::db::DB_LOG_INFO; +using isc::db::DB_LOG_DEBUG; +using isc::db::db_logger_mutex; + +namespace { + +/// Test that the mutex unlocks after a call to DB_LOG. +/// Let's use DB_INVALID_ACCESS as an example for all. +TEST(DatabaseLogTest, mutexIsolation) { + DB_LOG_FATAL(DB_INVALID_ACCESS).arg("hello"); + EXPECT_TRUE(db_logger_mutex.try_lock()); + db_logger_mutex.unlock(); + + DB_LOG_ERROR(DB_INVALID_ACCESS).arg("hello"); + EXPECT_TRUE(db_logger_mutex.try_lock()); + db_logger_mutex.unlock(); + + DB_LOG_WARN(DB_INVALID_ACCESS).arg("hello"); + EXPECT_TRUE(db_logger_mutex.try_lock()); + db_logger_mutex.unlock(); + + DB_LOG_INFO(DB_INVALID_ACCESS).arg("hello"); + EXPECT_TRUE(db_logger_mutex.try_lock()); + db_logger_mutex.unlock(); + + DB_LOG_DEBUG(DB_DBG_TRACE_DETAIL, DB_INVALID_ACCESS).arg("hello"); + EXPECT_TRUE(db_logger_mutex.try_lock()); + db_logger_mutex.unlock(); +} + +} // namespace diff --git a/src/lib/database/tests/dbaccess_parser_unittest.cc b/src/lib/database/tests/dbaccess_parser_unittest.cc new file mode 100644 index 0000000..29322e0 --- /dev/null +++ b/src/lib/database/tests/dbaccess_parser_unittest.cc @@ -0,0 +1,1001 @@ +// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/command_interpreter.h> +#include <database/database_connection.h> +#include <database/db_exceptions.h> +#include <database/dbaccess_parser.h> +#include <log/logger_support.h> + +#include <gtest/gtest.h> + +#include <map> +#include <string> + +using namespace std; +using namespace isc; +using namespace isc::db; +using namespace isc::data; +using namespace isc::config; + +namespace { + +/// @brief Database Access Parser test fixture class +class DbAccessParserTest : public ::testing::Test { +public: + /// @brief Constructor + DbAccessParserTest() { + } + /// @brief Destructor + /// + /// As some of the tests have the side-effect of altering the logging + /// settings (when the parser's "parse" method is called), ensure that + /// the logging is reset to the default after each test completes. + ~DbAccessParserTest() { + isc::log::setDefaultLoggingOutput(); + } + + /// @brief Build JSON String + /// + /// Given a array of "const char*" strings representing in order, keyword, + /// value, keyword, value, ... and terminated by a NULL, return a string + /// that represents the JSON map for the keywords and values. + /// + /// E.g. given the array of strings: alpha, one, beta, two, NULL, it would + /// return the string '{ "alpha": "one", "beta": "two" }' + /// + /// @param keyval Array of "const char*" strings in the order keyword, + /// value, keyword, value ... A NULL entry terminates the list. + /// + /// @return JSON map for the keyword value array. + std::string toJson(const char* keyval[]) { + const std::string quote = "\""; + const std::string colon = ":"; + const std::string space = " "; + + string result = "{ "; + + for (size_t i = 0; keyval[i] != NULL; i+= 2) { + // Get the value. This should not be NULL. As ASSERT_NE will + // cause a return - which gives compilation problems as a return + // statement is expected to return a string - use EXPECT_NE and + // explicitly return if the expected array is incorrect. + EXPECT_NE(static_cast<const char*>(NULL), keyval[i + 1]) << + "Supplied reference keyword/value list does not contain values " + "for all keywords"; + if (keyval[i + 1] == NULL) { + return (std::string("")); + } + + // Add the separating comma if not the first. + if (i != 0) { + result += ", "; + } + + // Add the keyword and value - make sure that they are quoted. + // The parameters which are not quoted are persist, readonly and + // lfc-interval as they are boolean and integer respectively. + result += quote + keyval[i] + quote + colon + space; + if (!quoteValue(std::string(keyval[i]))) { + result += keyval[i + 1]; + + } else { + result += quote + keyval[i + 1] + quote; + } + } + + // Add the terminating brace + result += " }"; + + return (result); + } + + /// @brief Check for Keywords + /// + /// Takes a database access string and checks it against a list of keywords + /// and values. It checks that: + /// + /// a. Every keyword in the string appears once and only once in the + /// list. + /// b. Every keyword in the list appears in the string. + /// c. Every keyword's value is the same as that in the string. + /// + /// To parse the access string, we use the parsing function in the + /// DHCP lease manager. + /// + /// @param trace_string String that will be used to set the value of a + /// SCOPED_TRACE for this call. + /// @param dbaccess set of database access parameters to check + /// @param keyval Array of "const char*" strings in the order keyword, + /// value, keyword, value ... A NULL entry terminates the list. + void checkAccessString(const char* trace_string, + const DatabaseConnection::ParameterMap& parameters, + const char* keyval[]) { + SCOPED_TRACE(trace_string); + + // Construct a map of keyword value pairs. + std::map<string, string> expected; + size_t expected_count = 0; + for (size_t i = 0; keyval[i] != NULL; i += 2) { + // Get the value. This should not be NULL + ASSERT_NE(static_cast<const char*>(NULL), keyval[i + 1]) << + "Supplied reference keyword/value list does not contain values " + "for all keywords"; + expected[keyval[i]] = keyval[i + 1]; + + // One more keyword processed + ++expected_count; + } + + // Check no duplicates in the test set of reference keywords. + ASSERT_EQ(expected_count, expected.size()) << + "Supplied reference keyword/value list contains duplicate keywords"; + + // The passed parameter map should have the same number of entries as + // the reference set of keywords. + EXPECT_EQ(expected_count, parameters.size()); + + // Check that the keywords and keyword values are the same: loop + // through the keywords in the database access string. + for (DatabaseConnection::ParameterMap::const_iterator actual = parameters.begin(); + actual != parameters.end(); ++actual) { + + // Does the keyword exist in the set of expected keywords? + std::map<string, string>::iterator corresponding = + expected.find(actual->first); + ASSERT_TRUE(corresponding != expected.end()); + + // Keyword exists, is the value the same? + EXPECT_EQ(corresponding->second, actual->second); + } + } + +private: + + /// @brief Checks if the value of the specified parameter should be + /// quoted in the configuration. + /// + /// @param parameter A parameter for which it should be checked whether + /// the value should be quoted or not. + /// + /// @return true if the value of the parameter should be quoted. + bool quoteValue(const std::string& parameter) const { + return ((parameter != "persist") && (parameter != "lfc-interval") && + (parameter != "connect-timeout") && + (parameter != "read-timeout") && + (parameter != "write-timeout") && + (parameter != "tcp-user-timeout") && + (parameter != "port") && + (parameter != "max-row-errors") && + (parameter != "readonly")); + } + +}; + + +/// @brief Version of parser with protected methods public +/// +/// Some of the methods in DbAccessParser are not required to be public in Kea. +/// Instead of being declared "private", they are declared "protected" so that +/// they can be accessed through a derived class in the unit tests. +class TestDbAccessParser : public DbAccessParser { +public: + + /// @brief Constructor + TestDbAccessParser() + : DbAccessParser() + {} + + /// @brief Destructor + virtual ~TestDbAccessParser() + {} + + /// @brief Parse configuration value + /// + /// @param database_config Configuration to be parsed. + void parse(ConstElementPtr database_config) { + std::string db_access_string; + DbAccessParser::parse(db_access_string, database_config); + } + + /// @brief Get database access parameters + /// + /// Used in testing to check that the configuration information has been + /// parsed corrected. + /// + /// @return Map of keyword/value pairs representing database access + /// information. + const DatabaseConnection::ParameterMap& getDbAccessParameters() const { + return (DbAccessParser::getDbAccessParameters()); + } + + /// @brief Construct database access string + /// + /// Constructs the database access string from the stored parameters. + /// + /// @return Database access string + std::string getDbAccessString() const { + return (DbAccessParser::getDbAccessString()); + } +}; + +// Check that the parser works with a simple configuration. +TEST_F(DbAccessParserTest, validTypeMemfile) { + const char* config[] = {"type", "memfile", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid memfile", parser.getDbAccessParameters(), config); +} + +// Check that the parser works with a simple configuration for host database. +TEST_F(DbAccessParserTest, hosts) { + const char* config[] = {"type", "memfile", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid memfile", parser.getDbAccessParameters(), config); +} + +// Check that the parser works with a simple configuration that +// includes empty elements. +TEST_F(DbAccessParserTest, emptyKeyword) { + const char* config[] = {"type", "memfile", + "name", "", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid memfile", parser.getDbAccessParameters(), config); +} + +// Check that the parser works with more complex configuration when +// lease file path is specified for DHCPv4. +TEST_F(DbAccessParserTest, persistV4Memfile) { + const char* config[] = {"type", "memfile", + "persist", "true", + "name", "/opt/var/lib/kea/kea-leases4.csv", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + + checkAccessString("Valid memfile", parser.getDbAccessParameters(), + config); +} + +// Check that the parser works with more complex configuration when +// lease file path is specified for DHCPv6. +TEST_F(DbAccessParserTest, persistV6Memfile) { + const char* config[] = {"type", "memfile", + "persist", "true", + "name", "/opt/var/lib/kea/kea-leases6.csv", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + + checkAccessString("Valid memfile", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser accepts the valid value of the +// lfc-interval parameter. +TEST_F(DbAccessParserTest, validLFCInterval) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "lfc-interval", "3600", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid LFC Interval", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// lfc-interval parameter. +TEST_F(DbAccessParserTest, negativeLFCInterval) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "lfc-interval", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects the too large (greater than +// the max uint32_t) value of the lfc-interval parameter. +TEST_F(DbAccessParserTest, largeLFCInterval) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "lfc-interval", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser accepts the valid value of the +// connect-timeout parameter. +TEST_F(DbAccessParserTest, validConnectTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "connect-timeout", "3600", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid timeout", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// connect-timeout parameter. +TEST_F(DbAccessParserTest, negativeConnectTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "connect-timeout", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint32_t) value of the connecttimeout parameter. +TEST_F(DbAccessParserTest, largeConnectTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "connect-timeout", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser accepts the valid value of the +// read-timeout parameter. +TEST_F(DbAccessParserTest, validReadTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "read-timeout", "3600", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid read timeout", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// read-timeout parameter. +TEST_F(DbAccessParserTest, negativeReadTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "read-timeout", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint32_t) value of the read-timeout parameter. +TEST_F(DbAccessParserTest, largeReadTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "read-timeout", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser accepts the valid value of the +// write-timeout parameter. +TEST_F(DbAccessParserTest, validWriteTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "write-timeout", "3600", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid write timeout", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// write-timeout parameter. +TEST_F(DbAccessParserTest, negativeWriteTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "write-timeout", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint32_t) value of the write-timeout parameter. +TEST_F(DbAccessParserTest, largeWriteTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "write-timeout", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser accepts the valid value of the +// tcp-user-timeout parameter. +TEST_F(DbAccessParserTest, validTcpUserTimeout) { + const char* config[] = {"type", "postgresql", + "name", "keatest", + "tcp-user-timeout", "3600", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid write timeout", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// tcp-user-timeout parameter. +TEST_F(DbAccessParserTest, negativeTcpUserTimeout) { + const char* config[] = {"type", "postgresql", + "name", "keatest", + "tcp-user-timeout", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint32_t) value of the tcp-user-timeout parameter. +TEST_F(DbAccessParserTest, largeTcpUserTimeout) { + const char* config[] = {"type", "postgresql", + "name", "keatest", + "tcp-user-timeout", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test verifies that specifying the tcp-user-timeout for the +// memfile backend is not allowed. +TEST_F(DbAccessParserTest, memfileTcpUserTimeout) { + const char* config[] = {"type", "memfile", + "name", "keatest", + "tcp-user-timeout", "10", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test verifies that specifying the tcp-user-timeout for the +// mysql backend is not allowed. +TEST_F(DbAccessParserTest, mysqlTcpUserTimeout) { + const char* config[] = {"type", "mysql", + "name", "keatest", + "tcp-user-timeout", "10", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test verifies that specifying the read-timeout for the +// memfile backend is not allowed. +TEST_F(DbAccessParserTest, memfileReadTimeout) { + const char* config[] = {"type", "memfile", + "name", "keatest", + "read-timeout", "10", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test verifies that specifying the read-timeout for the +// postgresql backend is not allowed. +TEST_F(DbAccessParserTest, postgresqlReadTimeout) { + const char* config[] = {"type", "postgresql", + "name", "keatest", + "read-timeout", "10", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test verifies that specifying the write-timeout for the +// memfile backend is not allowed. +TEST_F(DbAccessParserTest, memfileWriteTimeout) { + const char* config[] = {"type", "memfile", + "name", "keatest", + "write-timeout", "10", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test verifies that specifying the write-timeout for the +// postgresql backend is not allowed. +TEST_F(DbAccessParserTest, postgresqlWriteTimeout) { + const char* config[] = {"type", "postgresql", + "name", "keatest", + "write-timeout", "10", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser accepts the valid value of the +// port parameter. +TEST_F(DbAccessParserTest, validPort) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "port", "3306", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid port", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser rejects the negative value of the +// port parameter. +TEST_F(DbAccessParserTest, negativePort) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "port", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint16_t) value of the timeout parameter. +TEST_F(DbAccessParserTest, largePort) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "port", "65536", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser accepts a value of zero for the +// max-row-errors parameter. +TEST_F(DbAccessParserTest, zeroMaxRowErrors) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "max-row-errors", "0", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Zero max-row-errors", parser.getDbAccessParameters(), + config); +} + +// This test checks that the parser accepts the valid value of the +// max-row-errors parameter. +TEST_F(DbAccessParserTest, validZeroMaxRowErrors) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "max-row-errors", "50", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid max-row-errors", parser.getDbAccessParameters(), + config); +} + + +// This test checks that the parser rejects the negative value of the +// max-row-errors parameter. +TEST_F(DbAccessParserTest, negativeMaxRowErrors) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "max-row-errors", "-1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// This test checks that the parser rejects a too large (greater than +// the max uint32_t) value of the max-row-errors parameter. +TEST_F(DbAccessParserTest, largeMaxRowErrors) { + const char* config[] = {"type", "memfile", + "name", "/opt/var/lib/kea/kea-leases6.csv", + "max-row-errors", "4294967296", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// Check that the parser works with a valid MySQL configuration +TEST_F(DbAccessParserTest, validTypeMysql) { + const char* config[] = {"type", "mysql", + "host", "erewhon", + "port", "3306", + "user", "kea", + "password", "keapassword", + "name", "keatest", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Valid mysql", parser.getDbAccessParameters(), config); +} + +// A missing 'type' keyword should cause an exception to be thrown. +TEST_F(DbAccessParserTest, missingTypeKeyword) { + const char* config[] = {"host", "erewhon", + "port", "3306", + "user", "kea", + "password", "keapassword", + "name", "keatest", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// Check reconfiguration. Checks that incremental changes applied to the +// database configuration are incremental. +TEST_F(DbAccessParserTest, incrementalChanges) { + const char* config1[] = {"type", "memfile", + NULL}; + + // Applying config2 will cause a wholesale change. + const char* config2[] = {"type", "mysql", + "host", "erewhon", + "port", "3306", + "user", "kea", + "password", "keapassword", + "name", "keatest", + NULL}; + + // Applying incremental2 should cause a change to config3. + const char* incremental2[] = {"user", "me", + "password", "meagain", + NULL}; + const char* config3[] = {"type", "mysql", + "host", "erewhon", + "port", "3306", + "user", "me", + "password", "meagain", + "name", "keatest", + NULL}; + + // incremental3 will cause an exception. There should be no change + // to the returned value. + const char* incremental3[] = {"type", "invalid", + "user", "you", + "password", "youagain", + NULL}; + + // incremental4 is a compatible change and should cause a transition + // to config4. + const char* incremental4[] = {"user", "them", + "password", "", + NULL}; + const char* config4[] = {"type", "mysql", + "host", "erewhon", + "port", "3306", + "user", "them", + "password", "", + "name", "keatest", + NULL}; + + TestDbAccessParser parser; + + // First configuration string should cause a representation of that string + // to be held. + string json_config = toJson(config1); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Initial configuration", parser.getDbAccessParameters(), + config1); + + // Applying a wholesale change will cause the access string to change + // to a representation of the new configuration. + json_config = toJson(config2); + json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Subsequent configuration", parser.getDbAccessParameters(), + config2); + + // Applying an incremental change will cause the representation to change + // incrementally. + json_config = toJson(incremental2); + json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Incremental configuration", parser.getDbAccessParameters(), + config3); + + // Applying the next incremental change should cause an exception to be + // thrown and there be no change to the access string. + json_config = toJson(incremental3); + json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + EXPECT_THROW(parser.parse(json_elements), DbConfigError); + checkAccessString("Incompatible incremental change", parser.getDbAccessParameters(), + config3); + + // Applying an incremental change will cause the representation to change + // incrementally. + json_config = toJson(incremental4); + json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + EXPECT_NO_THROW(parser.parse(json_elements)); + checkAccessString("Compatible incremental change", parser.getDbAccessParameters(), + config4); +} + +// Check that the database access string is constructed correctly. +TEST_F(DbAccessParserTest, getDbAccessString) { + const char* config[] = {"type", "mysql", + "host", "", + "name", "keatest", + "password", "password with spaces", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + + // Get the database access string + std::string dbaccess = parser.getDbAccessString(); + + // String should be either "type=mysql name=keatest" or + // "name=keatest type=mysql". The "host" entry is null, so should not be + // output. + EXPECT_EQ(dbaccess, "name=keatest password='password with spaces' type=mysql"); +} + +// Check that the configuration is accepted for the valid value +// of "readonly". +TEST_F(DbAccessParserTest, validReadOnly) { + const char* config[] = {"type", "mysql", + "user", "keatest", + "password", "keatest", + "name", "keatest", + "readonly", "true", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_NO_THROW(parser.parse(json_elements)); + + checkAccessString("Valid readonly parameter", + parser.getDbAccessParameters(), + config); +} + +// Check that for the invalid value of the "readonly" parameter +// an exception is thrown. +TEST_F(DbAccessParserTest, invalidReadOnly) { + const char* config[] = {"type", "mysql", + "user", "keatest", + "password", "keatest", + "name", "keatest", + "readonly", "1", + NULL}; + + string json_config = toJson(config); + ConstElementPtr json_elements = Element::fromJSON(json_config); + EXPECT_TRUE(json_elements); + + TestDbAccessParser parser; + EXPECT_THROW(parser.parse(json_elements), DbConfigError); +} + +// Check that multiple host storages are correctly parsed. +TEST_F(DbAccessParserTest, multipleHost) { + const char* config1[] = {"type", "mysql", + "name", "keatest1", + NULL}; + const char* config2[] = {"type", "mysql", + "name", "keatest2", + NULL}; + + string json_config1 = toJson(config1); + string json_config2 = toJson(config2); + ConstElementPtr json_elements1 = Element::fromJSON(json_config1); + ConstElementPtr json_elements2 = Element::fromJSON(json_config2); + + TestDbAccessParser parser1; + TestDbAccessParser parser2; + EXPECT_NO_THROW(parser1.parse(json_elements1)); + EXPECT_NO_THROW(parser2.parse(json_elements2)); + + checkAccessString("First config", + parser1.getDbAccessParameters(), + config1); + checkAccessString("Second config", + parser2.getDbAccessParameters(), + config2); +} + +}; // Anonymous namespace diff --git a/src/lib/database/tests/run_unittests.cc b/src/lib/database/tests/run_unittests.cc new file mode 100644 index 0000000..4e83d4b --- /dev/null +++ b/src/lib/database/tests/run_unittests.cc @@ -0,0 +1,20 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <log/logger_support.h> +#include <gtest/gtest.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + isc::log::initLogger(); + + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/lib/database/tests/server_selector_unittest.cc b/src/lib/database/tests/server_selector_unittest.cc new file mode 100644 index 0000000..00328ba --- /dev/null +++ b/src/lib/database/tests/server_selector_unittest.cc @@ -0,0 +1,77 @@ +// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <cc/server_tag.h> +#include <database/server_selector.h> +#include <gtest/gtest.h> + +using namespace isc::data; +using namespace isc::db; + +namespace { + +// Check that server selector can be set to UNASSIGNED. +TEST(ServerSelectorTest, unassigned) { + ServerSelector selector = ServerSelector::UNASSIGNED(); + EXPECT_EQ(ServerSelector::Type::UNASSIGNED, selector.getType()); + EXPECT_TRUE(selector.amUnassigned()); + EXPECT_TRUE(selector.hasNoTags()); + EXPECT_FALSE(selector.hasMultipleTags()); +} + +// Check that server selector can be set to ALL. +TEST(ServerSelectorTest, all) { + ServerSelector selector = ServerSelector::ALL(); + EXPECT_EQ(ServerSelector::Type::ALL, selector.getType()); + EXPECT_FALSE(selector.amUnassigned()); + + EXPECT_FALSE(selector.hasNoTags()); + auto tags = selector.getTags(); + EXPECT_EQ(1, tags.size()); + EXPECT_EQ(1, tags.count(ServerTag("all"))); + EXPECT_TRUE(selector.amAll()); + EXPECT_FALSE(selector.hasMultipleTags()); +} + +// Check that a single server can be selected. +TEST(ServerSelectorTest, one) { + ServerSelector selector = ServerSelector::ONE("some-tag"); + EXPECT_EQ(ServerSelector::Type::SUBSET, selector.getType()); + EXPECT_FALSE(selector.amUnassigned()); + + EXPECT_FALSE(selector.hasNoTags()); + auto tags = selector.getTags(); + ASSERT_EQ(1, tags.size()); + EXPECT_EQ(1, tags.count(ServerTag("some-tag"))); + EXPECT_FALSE(selector.hasMultipleTags()); +} + +// Check that multiple servers can be selected. +TEST(ServerSelectorTest, multiple) { + ServerSelector selector = ServerSelector::MULTIPLE({ "tag1", "tag2", "tag3" }); + EXPECT_EQ(ServerSelector::Type::SUBSET, selector.getType()); + EXPECT_FALSE(selector.amUnassigned()); + + EXPECT_FALSE(selector.hasNoTags()); + auto tags = selector.getTags(); + ASSERT_EQ(3, tags.size()); + EXPECT_EQ(1, tags.count(ServerTag("tag1"))); + EXPECT_EQ(1, tags.count(ServerTag("tag2"))); + EXPECT_EQ(1, tags.count(ServerTag("tag3"))); + EXPECT_TRUE(selector.hasMultipleTags()); +} + +// Check that server selector can be set to ANY. +TEST(ServerSelectorTest, any) { + ServerSelector selector = ServerSelector::ANY(); + EXPECT_EQ(ServerSelector::Type::ANY, selector.getType()); + EXPECT_FALSE(selector.amUnassigned()); + + EXPECT_TRUE(selector.hasNoTags()); +} + +} diff --git a/src/lib/database/tests/server_unittest.cc b/src/lib/database/tests/server_unittest.cc new file mode 100644 index 0000000..63e189b --- /dev/null +++ b/src/lib/database/tests/server_unittest.cc @@ -0,0 +1,85 @@ +// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <database/server_collection.h> +#include <testutils/test_to_element.h> +#include <exceptions/exceptions.h> +#include <gtest/gtest.h> +#include <string> + +using namespace isc; +using namespace isc::data; +using namespace isc::db; + +namespace { + +// Tests the constructor of the Server. +TEST(ServerTest, constructor) { + ServerPtr server; + + ASSERT_NO_THROW( + server = Server::create(ServerTag("xyz"), "my first server") + ); + ASSERT_TRUE(server); + EXPECT_EQ("xyz", server->getServerTagAsText()); + EXPECT_EQ("my first server", server->getDescription()); +} + +// Tests that too long description is rejected. +TEST(ServerTest, tooLongDescription) { + EXPECT_THROW(Server::create(ServerTag("xyz"), std::string(65537, 'c')), + BadValue); +} + +// Tests that toElement method returns expected JSON map. +TEST(ServerTest, toElement) { + ServerPtr server1 = Server::create(ServerTag("foo"), "a server"); + std::string expected1 = "{" + "\"server-tag\": \"foo\"," + "\"description\": \"a server\"" + " }"; + isc::test::runToElementTest<Server>(expected1, *server1); + + ServerPtr server2 = Server::create(ServerTag("bar")); + std::string expected2= "{" + "\"server-tag\": \"bar\"," + "\"description\": \"\"" + " }"; + isc::test::runToElementTest<Server>(expected2, *server2); +} + +// Tests that it is possible to fetch server by tag from the collection. +TEST(ServerFetcherTest, getByTag) { + ServerCollection servers; + + EXPECT_TRUE(servers.insert(Server::create(ServerTag("alpha"), "alpha description")).second); + EXPECT_TRUE(servers.insert(Server::create(ServerTag("beta"), "beta description")).second); + EXPECT_TRUE(servers.insert(Server::create(ServerTag("gamma"), "gamma description")).second); + + // Inserting an element with duplicated server tag should be unsuccessful. + EXPECT_FALSE(servers.insert(Server::create(ServerTag("gamma"), "gamma 2 description")).second); + + auto alpha = ServerFetcher::get(servers, ServerTag("alpha")); + ASSERT_TRUE(alpha); + EXPECT_EQ("alpha", alpha->getServerTagAsText()); + EXPECT_EQ("alpha description", alpha->getDescription()); + + auto beta = ServerFetcher::get(servers, ServerTag("beta")); + ASSERT_TRUE(beta); + EXPECT_EQ("beta", beta->getServerTagAsText()); + EXPECT_EQ("beta description", beta->getDescription()); + + auto gamma = ServerFetcher::get(servers, ServerTag("gamma")); + ASSERT_TRUE(gamma); + EXPECT_EQ("gamma", gamma->getServerTagAsText()); + EXPECT_EQ("gamma description", gamma->getDescription()); + + // Null pointer should be returned when a given server does not exist. + EXPECT_FALSE(ServerFetcher::get(servers, ServerTag("non-existent"))); +} + +} diff --git a/src/lib/database/testutils/Makefile.am b/src/lib/database/testutils/Makefile.am new file mode 100644 index 0000000..3cf82e0 --- /dev/null +++ b/src/lib/database/testutils/Makefile.am @@ -0,0 +1,21 @@ +SUBDIRS = . + +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib +TEST_CA_DIR = $(abs_srcdir)/../../asiolink/testutils/ca +AM_CPPFLAGS += -DTEST_CA_DIR=\"$(TEST_CA_DIR)\" + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +CLEANFILES = *.gcno *.gcda + +if HAVE_GTEST + +noinst_LTLIBRARIES = libdatabasetest.la + +libdatabasetest_la_SOURCES = schema.cc schema.h + +libdatabasetest_la_CXXFLAGS = $(AM_CXXFLAGS) +libdatabasetest_la_CPPFLAGS = $(AM_CPPFLAGS) +libdatabasetest_la_LDFLAGS = $(AM_LDFLAGS) + +endif diff --git a/src/lib/database/testutils/Makefile.in b/src/lib/database/testutils/Makefile.in new file mode 100644 index 0000000..51277d9 --- /dev/null +++ b/src/lib/database/testutils/Makefile.in @@ -0,0 +1,860 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/lib/database/testutils +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \ + $(top_srcdir)/m4macros/ax_cpp11.m4 \ + $(top_srcdir)/m4macros/ax_cpp20.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_netconf.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libdatabasetest_la_LIBADD = +am__libdatabasetest_la_SOURCES_DIST = schema.cc schema.h +@HAVE_GTEST_TRUE@am_libdatabasetest_la_OBJECTS = \ +@HAVE_GTEST_TRUE@ libdatabasetest_la-schema.lo +libdatabasetest_la_OBJECTS = $(am_libdatabasetest_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libdatabasetest_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(libdatabasetest_la_CXXFLAGS) $(CXXFLAGS) \ + $(libdatabasetest_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libdatabasetest_la_rpath = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libdatabasetest_la-schema.Plo +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libdatabasetest_la_SOURCES) +DIST_SOURCES = $(am__libdatabasetest_la_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@ +DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@ +DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_NETCONF = @HAVE_NETCONF@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@ +LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@ +LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@ +LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@ +LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@ +LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@ +LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@ +LIBYANG_LIBS = @LIBYANG_LIBS@ +LIBYANG_PREFIX = @LIBYANG_PREFIX@ +LIBYANG_VERSION = @LIBYANG_VERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_PLUGINS_PATH = @SR_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@ +SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@ +SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@ +SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_PREFIX = @SYSREPO_PREFIX@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . +AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \ + -DTEST_CA_DIR=\"$(TEST_CA_DIR)\" +TEST_CA_DIR = $(abs_srcdir)/../../asiolink/testutils/ca +AM_CXXFLAGS = $(KEA_CXXFLAGS) +CLEANFILES = *.gcno *.gcda +@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libdatabasetest.la +@HAVE_GTEST_TRUE@libdatabasetest_la_SOURCES = schema.cc schema.h +@HAVE_GTEST_TRUE@libdatabasetest_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libdatabasetest_la_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_GTEST_TRUE@libdatabasetest_la_LDFLAGS = $(AM_LDFLAGS) +all: all-recursive + +.SUFFIXES: +.SUFFIXES: .cc .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/lib/database/testutils/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib/database/testutils/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdatabasetest.la: $(libdatabasetest_la_OBJECTS) $(libdatabasetest_la_DEPENDENCIES) $(EXTRA_libdatabasetest_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libdatabasetest_la_LINK) $(am_libdatabasetest_la_rpath) $(libdatabasetest_la_OBJECTS) $(libdatabasetest_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdatabasetest_la-schema.Plo@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +libdatabasetest_la-schema.lo: schema.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabasetest_la_CPPFLAGS) $(CPPFLAGS) $(libdatabasetest_la_CXXFLAGS) $(CXXFLAGS) -MT libdatabasetest_la-schema.lo -MD -MP -MF $(DEPDIR)/libdatabasetest_la-schema.Tpo -c -o libdatabasetest_la-schema.lo `test -f 'schema.cc' || echo '$(srcdir)/'`schema.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdatabasetest_la-schema.Tpo $(DEPDIR)/libdatabasetest_la-schema.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='schema.cc' object='libdatabasetest_la-schema.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libdatabasetest_la_CPPFLAGS) $(CPPFLAGS) $(libdatabasetest_la_CXXFLAGS) $(CXXFLAGS) -c -o libdatabasetest_la-schema.lo `test -f 'schema.cc' || echo '$(srcdir)/'`schema.cc + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libdatabasetest_la-schema.Plo + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/libdatabasetest_la-schema.Plo + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-am clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/lib/database/testutils/schema.cc b/src/lib/database/testutils/schema.cc new file mode 100644 index 0000000..b396b9e --- /dev/null +++ b/src/lib/database/testutils/schema.cc @@ -0,0 +1,153 @@ +// Copyright (C) 2016-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> +#include <string> + +using namespace std; + +namespace isc { +namespace db { +namespace test { + +// Connection strings. +// Database: keatest +// Host: localhost +// Username: keatest +// Password: keatest + +const char* INVALID_TYPE = "type=unknown"; +const char* VALID_NAME = "name=keatest"; +const char* INVALID_NAME = "name=invalidname"; +const char* VALID_HOST = "host=localhost"; +const char* VALID_HOST_TCP = "host=127.0.0.1"; +const char* INVALID_HOST = "host=invalidhost"; +const char* INVALID_PORT_1 = "port=65536"; +const char* VALID_USER = "user=keatest"; +const char* VALID_READONLY_USER = "user=keatest_readonly"; +const char* VALID_SECURE_USER = "user=keatest_secure"; +const char* INVALID_USER = "user=invaliduser"; +const char* VALID_PASSWORD = "password=keatest"; +const char* INVALID_PASSWORD = "password=invalid"; +const char* VALID_TIMEOUT = "connect-timeout=10"; +const char* INVALID_TIMEOUT_1 = "connect-timeout=foo"; +const char* INVALID_TIMEOUT_2 = "connect-timeout=-17"; +const char* INVALID_TIMEOUT_3 = "connect-timeout=0"; +const char* VALID_READ_TIMEOUT = "read-timeout=11"; +const char* VALID_READ_TIMEOUT_ZERO = "read-timeout=0"; +const char* INVALID_READ_TIMEOUT_1 = "read-timeout=bar"; +const char* VALID_WRITE_TIMEOUT = "write-timeout=12"; +const char* VALID_WRITE_TIMEOUT_ZERO = "write-timeout=0"; +const char* INVALID_WRITE_TIMEOUT_1 = "write-timeout=baz"; +const char* VALID_TCP_USER_TIMEOUT = "tcp-user-timeout=8"; +const char* VALID_TCP_USER_TIMEOUT_ZERO = "tcp-user-timeout=0"; +const char* INVALID_TCP_USER_TIMEOUT_1 = "-7"; +const char* VALID_READONLY_DB = "readonly=true"; +const char* INVALID_READONLY_DB = "readonly=5"; +const char* VALID_CERT = "cert-file=" TEST_CA_DIR "/kea-client.crt"; +const char* VALID_KEY = "key-file=" TEST_CA_DIR "/kea-client.key"; +const char* INVALID_KEY = "key-file=" TEST_CA_DIR "/kea-other.key"; +const char* VALID_CA = "trust-anchor=" TEST_CA_DIR "/kea-ca.crt"; +const char* VALID_CIPHER = "cipher-list=AES"; + +string connectionString(const char* type, const char* name, const char* host, + const char* user, const char* password, + const char* timeout, const char* readonly_db, + const char* cert_file, const char* key_file, + const char* trust_anchor, const char* cipher) { + const string space = " "; + string result = ""; + + if (type) { + result += string(type); + } + + if (name) { + if (! result.empty()) { + result += space; + } + result += string(name); + } + + if (host) { + if (! result.empty()) { + result += space; + } + result += string(host); + } + + if (user) { + if (! result.empty()) { + result += space; + } + result += string(user); + } + + if (password) { + if (! result.empty()) { + result += space; + } + result += string(password); + } + + if (timeout) { + if (! result.empty()) { + result += space; + } + result += string(timeout); + } + + if (readonly_db) { + if (! result.empty()) { + result += space; + } + result += string(readonly_db); + } + + if (cert_file) { + if (! result.empty()) { + result += space; + } + result += string(cert_file); + } + + if (key_file) { + if (! result.empty()) { + result += space; + } + result += string(key_file); + } + + if (trust_anchor) { + if (! result.empty()) { + result += space; + } + result += string(trust_anchor); + } + + if (cipher) { + if (! result.empty()) { + result += space; + } + result += string(cipher); + } + + return (result); +} + +bool +softWipeEnabled() { + const char* const wipe_only = getenv("KEA_TEST_DB_WIPE_DATA_ONLY"); + if (wipe_only && (std::string(wipe_only) == std::string("false"))) { + return (false); + } + + return (true); +} + +} +} +} diff --git a/src/lib/database/testutils/schema.h b/src/lib/database/testutils/schema.h new file mode 100644 index 0000000..c20c47d --- /dev/null +++ b/src/lib/database/testutils/schema.h @@ -0,0 +1,85 @@ +// Copyright (C) 2016-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef SCHEMA_H +#define SCHEMA_H + +#include <config.h> +#include <cstdlib> +#include <string> + +namespace isc { +namespace db { +namespace test { + +extern const char* INVALID_TYPE; +extern const char* VALID_NAME; +extern const char* INVALID_NAME; +extern const char* VALID_HOST; +extern const char* VALID_HOST_TCP; +extern const char* INVALID_HOST; +extern const char* INVALID_PORT_1; +extern const char* VALID_USER; +extern const char* VALID_READONLY_USER; +extern const char* VALID_SECURE_USER; +extern const char* INVALID_USER; +extern const char* VALID_PASSWORD; +extern const char* INVALID_PASSWORD; +extern const char* VALID_TIMEOUT; +extern const char* INVALID_TIMEOUT_1; +extern const char* INVALID_TIMEOUT_2; +extern const char* INVALID_TIMEOUT_3; +extern const char* VALID_READ_TIMEOUT; +extern const char* VALID_READ_TIMEOUT_ZERO; +extern const char* INVALID_READ_TIMEOUT_1; +extern const char* VALID_WRITE_TIMEOUT; +extern const char* VALID_WRITE_TIMEOUT_ZERO; +extern const char* INVALID_WRITE_TIMEOUT_1; +extern const char* VALID_TCP_USER_TIMEOUT; +extern const char* VALID_TCP_USER_TIMEOUT_ZERO; +extern const char* INVALID_TCP_USER_TIMEOUT_1; +extern const char* VALID_READONLY_DB; +extern const char* INVALID_READONLY_DB; +extern const char* VALID_CERT; +extern const char* VALID_KEY; +extern const char* INVALID_KEY; +extern const char* VALID_CA; +extern const char* VALID_CIPHER; + +/// @brief Given a combination of strings above, produce a connection string. +/// +/// @param type type of the database +/// @param name name of the database to connect to +/// @param host hostname +/// @param user username used to authenticate during connection attempt +/// @param password password used to authenticate during connection attempt +/// @param timeout timeout used during connection attempt +/// @param readonly_db specifies if database is read only +/// @param cert_file specifies the client certificate file +/// @param key_file specifies the private key file +/// @param trust_anchor specifies the trust anchor aka cert authority +/// @param cipher specifies the cipher list +/// @return string containing all specified parameters +std::string connectionString(const char* type, const char* name = 0, + const char* host = 0, const char* user = 0, + const char* password = 0, const char* timeout = 0, + const char* readonly_db = 0, + const char* cert_file = 0, + const char* key_file = 0, + const char* trust_anchor = 0, + const char* cipher = 0); + +/// @brief Determines if wiping only the data between tests is enabled +/// +/// @return true if the environment variable, KEA_TEST_DB_WIPE_DATA_ONLY is +/// defined as "true" or if it is not present. +bool softWipeEnabled(); + +} +} +} + +#endif |