diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:15:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:15:43 +0000 |
commit | f5f56e1a1c4d9e9496fcb9d81131066a964ccd23 (patch) | |
tree | 49e44c6f87febed37efb953ab5485aa49f6481a7 /src/bin/admin | |
parent | Initial commit. (diff) | |
download | isc-kea-upstream.tar.xz isc-kea-upstream.zip |
Adding upstream version 2.4.1.upstream/2.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/bin/admin')
-rw-r--r-- | src/bin/admin/Makefile.am | 9 | ||||
-rw-r--r-- | src/bin/admin/Makefile.in | 834 | ||||
-rw-r--r-- | src/bin/admin/admin-utils.sh.in | 224 | ||||
-rw-r--r-- | src/bin/admin/kea-admin.in | 1090 | ||||
-rw-r--r-- | src/bin/admin/tests/Makefile.am | 32 | ||||
-rw-r--r-- | src/bin/admin/tests/Makefile.in | 880 | ||||
-rw-r--r-- | src/bin/admin/tests/admin_tests.sh.in | 147 | ||||
-rw-r--r-- | src/bin/admin/tests/data/Makefile.am | 3 | ||||
-rw-r--r-- | src/bin/admin/tests/data/Makefile.in | 551 | ||||
-rw-r--r-- | src/bin/admin/tests/data/lease4_dump_test.reference.csv | 4 | ||||
-rw-r--r-- | src/bin/admin/tests/data/lease6_dump_test.reference.csv | 4 | ||||
-rw-r--r-- | src/bin/admin/tests/dhcpdb_create_1.0.mysql | 131 | ||||
-rw-r--r-- | src/bin/admin/tests/dhcpdb_create_1.0.pgsql | 122 | ||||
-rw-r--r-- | src/bin/admin/tests/memfile_tests.sh.in | 239 | ||||
-rw-r--r-- | src/bin/admin/tests/mysql_tests.sh.in | 2892 | ||||
-rw-r--r-- | src/bin/admin/tests/pgsql_tests.sh.in | 2068 |
16 files changed, 9230 insertions, 0 deletions
diff --git a/src/bin/admin/Makefile.am b/src/bin/admin/Makefile.am new file mode 100644 index 0000000..94b88dd --- /dev/null +++ b/src/bin/admin/Makefile.am @@ -0,0 +1,9 @@ +SUBDIRS = . tests + +# Install kea-admin in sbin. +sbin_SCRIPTS = kea-admin + +DISTCLEANFILES = $(sbin_SCRIPTS) + +adminscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts +adminscripts_DATA = admin-utils.sh diff --git a/src/bin/admin/Makefile.in b/src/bin/admin/Makefile.in new file mode 100644 index 0000000..7503e6e --- /dev/null +++ b/src/bin/admin/Makefile.in @@ -0,0 +1,834 @@ +# 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/bin/admin +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 = admin-utils.sh kea-admin +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)$(sbindir)" \ + "$(DESTDIR)$(adminscriptsdir)" +SCRIPTS = $(sbin_SCRIPTS) +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 = +SOURCES = +DIST_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 +DATA = $(adminscripts_DATA) +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 $(srcdir)/admin-utils.sh.in \ + $(srcdir)/kea-admin.in +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 = . tests + +# Install kea-admin in sbin. +sbin_SCRIPTS = kea-admin +DISTCLEANFILES = $(sbin_SCRIPTS) +adminscriptsdir = ${datarootdir}/${PACKAGE_NAME}/scripts +adminscripts_DATA = admin-utils.sh +all: all-recursive + +.SUFFIXES: +$(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/bin/admin/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/bin/admin/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): +admin-utils.sh: $(top_builddir)/config.status $(srcdir)/admin-utils.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +kea-admin: $(top_builddir)/config.status $(srcdir)/kea-admin.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-sbinSCRIPTS: $(sbin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-adminscriptsDATA: $(adminscripts_DATA) + @$(NORMAL_INSTALL) + @list='$(adminscripts_DATA)'; test -n "$(adminscriptsdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(adminscriptsdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(adminscriptsdir)" || 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_DATA) $$files '$(DESTDIR)$(adminscriptsdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(adminscriptsdir)" || exit $$?; \ + done + +uninstall-adminscriptsDATA: + @$(NORMAL_UNINSTALL) + @list='$(adminscripts_DATA)'; test -n "$(adminscriptsdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(adminscriptsdir)'; $(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 $(SCRIPTS) $(DATA) +installdirs: installdirs-recursive +installdirs-am: + for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(adminscriptsdir)"; 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: + +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) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +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 mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: install-adminscriptsDATA + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: install-sbinSCRIPTS + +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 Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: uninstall-adminscriptsDATA uninstall-sbinSCRIPTS + +.MAKE: $(am__recursive_targets) install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-adminscriptsDATA 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-sbinSCRIPTS install-strip \ + installcheck installcheck-am installdirs installdirs-am \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-adminscriptsDATA uninstall-am \ + uninstall-sbinSCRIPTS + +.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/bin/admin/admin-utils.sh.in b/src/bin/admin/admin-utils.sh.in new file mode 100644 index 0000000..62fdc6f --- /dev/null +++ b/src/bin/admin/admin-utils.sh.in @@ -0,0 +1,224 @@ +#!/bin/sh + +# Copyright (C) 2014-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/. + +# This is an utility script that is being included by other scripts. + +# shellcheck disable=SC2086 +# SC2086: Double quote to prevent globbing and word splitting. +# Reason for disable: explicitly don't quote db_port_full_parameter so it +# doesn't expand to empty string if it is not set and explicitly don't quote +# extra_arguments so it is considered multiple arguments instead of one. + +# shellcheck disable=SC2154 +# SC2154: ... is referenced but not assigned. +# Some variables are assigned in kea-admin. + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# These are the default parameters. They will likely not work in any +# specific deployment. Also used in unit tests. +db_host='localhost' +db_user='keatest' +db_password='keatest' +db_name='keatest' +extra_arguments= + +# Runs all the given arguments as a single command. Maintains quoting. Places +# output in ${OUTPUT} and exit code in ${EXIT_CODE}. Does not support pipes and +# redirections. Support for them could be added through eval and single +# parameter assignment, but eval is not recommended. +run_command() { + if test -n "${DEBUG+x}"; then + printf '%s\n' "${*}" >&2 + fi + set +e + OUTPUT=$("${@}") + EXIT_CODE=${?} + set -e +} + +mysql_sanity_checks() { + # https://bugs.mysql.com/bug.php?id=55796#c321360 + # https://dev.mysql.com/doc/refman/8.0/en/connecting.html + # On Unix, MySQL programs treat the host name localhost specially, in a way + # that is likely different from what you expect compared to other + # network-based programs: the client connects using a Unix socket file. + if test -n "${db_port+x}" && \ + { test "${db_host}" = 'localhost' || \ + test "${db_host}" = '127.0.0.1'; }; then + printf 'Warning: the MySQL client uses the default unix socket ' >&2 + printf 'instead of TCP port %s ' "${db_port}" >&2 + printf 'when connecting to localhost. Continuing...\n' >&2 + fi +} + +# Executes a given MySQL statement. +# mysql_execute SQL_QUERY PARAM1 PARAM2 .. PARAMN - Additional parameters +# may be specified. They are passed directly to mysql. +# +# It returns the mysql command exit status to the caller as $? +mysql_execute() { + QUERY=$1 + shift + + mysql_sanity_checks + + if [ $# -gt 0 ]; then + mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \ + --database="${db_name}" --user="${db_user}" \ + --password="${db_password}" ${extra_arguments} \ + --execute "${QUERY}" "${@}" + else + mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \ + --database="${db_name}" --user="${db_user}" \ + --password="${db_password}" ${extra_arguments} \ + --execute "${QUERY}" + fi +} + +# Submits SQL in a given file to MySQL. +# pgsql_execute SQL_FILE PARAM1 PARAM2 .. PARAMN - Additional parameters +# may be specified. They are passed directly to pgsql. +# +# It returns the mysql command exit status to the caller as $? +mysql_execute_script() { + file=$1 + shift + + mysql_sanity_checks + + if [ $# -gt 0 ]; then + mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \ + --database="${db_name}" --user="${db_user}" \ + --password="${db_password}" ${extra_arguments} "${@}" < "${file}" + else + mysql -N -B --host="${db_host}" ${db_port_full_parameter-} \ + --database="${db_name}" --user="${db_user}" \ + --password="${db_password}" ${extra_arguments} < "${file}" + fi +} + +mysql_version() { + mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version" "$@" +} + +checked_mysql_version() { + run_command \ + mysql_execute "SELECT CONCAT_WS('.', version, minor) FROM schema_version" "$@" + + if [ "${EXIT_CODE}" -ne 0 ] + then + printf "Failed to get schema version, mysql status %s\n" "${EXIT_CODE}" + fi + + printf '%s\n' "${OUTPUT}" + return "${EXIT_CODE}" +} + +# Submits given SQL text to PostgreSQL +# pgsql_execute SQL_QUERY PARAM1 PARAM2 .. PARAMN - Additional parameters +# may be specified. They are passed directly to pgsql. +# +# It returns the pgsql command exit status to the caller as $? +pgsql_execute() { + QUERY=$1 + shift + + # Prioritize externally set PGPASSWORD. wipe_data.sh sets it for example. + if test -z "${PGPASSWORD-}"; then + PGPASSWORD="${db_password}" + fi + export PGPASSWORD + + if [ $# -gt 0 ]; then + printf '%s' "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t \ + -h "${db_host}" ${db_port_full_parameter-} -q -U "${db_user}" \ + -d "${db_name}" ${extra_arguments} "${@}" + else + printf '%s' "${QUERY}" | psql --set ON_ERROR_STOP=1 -A -t \ + -h "${db_host}" ${db_port_full_parameter-} -q -U "${db_user}" \ + -d "${db_name}" ${extra_arguments} + fi +} + +# Submits SQL in a given file to PostgreSQL +# pgsql_execute SQL_FILE PARAM1 PARAM2 .. PARAMN - Additional parameters +# may be specified. They are passed directly to pgsql. +# +# It returns the pgsql command exit status to the caller as $? +pgsql_execute_script() { + file=$1 + shift + + # Prioritize externally set PGPASSWORD. wipe_data.sh sets it for example. + if test -z "${PGPASSWORD-}"; then + PGPASSWORD="${db_password}" + fi + export PGPASSWORD + + if [ $# -gt 0 ]; then + psql --set ON_ERROR_STOP=1 -A -t -h "${db_host}" \ + ${db_port_full_parameter-} -q -U "${db_user}" -d "${db_name}" \ + ${extra_arguments} -f "${file}" "${@}" + else + psql --set ON_ERROR_STOP=1 -A -t -h "${db_host}" \ + ${db_port_full_parameter-} -q -U "${db_user}" -d "${db_name}" \ + ${extra_arguments} -f "${file}" + fi +} + +pgsql_version() { + pgsql_execute "SELECT version || '.' || minor FROM schema_version" "$@" +} + +checked_pgsql_version() { + run_command \ + pgsql_execute "SELECT version || '.' || minor FROM schema_version" "$@" + + if [ "${EXIT_CODE}" -ne 0 ] + then + printf "Failed to get schema version, pgsql status %s\n" "${EXIT_CODE}" + fi + + printf '%s\n' "${OUTPUT}" + return "${EXIT_CODE}" +} + +# recount IPv4 leases from scratch +_RECOUNT4_QUERY=\ +" +START TRANSACTION; \ +DELETE FROM lease4_stat; \ +INSERT INTO lease4_stat (subnet_id, state, leases) \ + SELECT subnet_id, state, COUNT(*) \ + FROM lease4 WHERE state = 0 OR state = 1 \ + GROUP BY subnet_id, state; \ +DELETE FROM lease4_pool_stat; \ +INSERT INTO lease4_pool_stat (subnet_id, pool_id, state, leases) \ + SELECT subnet_id, pool_id, state, count(*) FROM lease4 \ + WHERE state = 0 OR state = 1 GROUP BY subnet_id, pool_id, state; \ +COMMIT;" +export _RECOUNT4_QUERY + +# recount IPv6 leases from scratch +_RECOUNT6_QUERY=\ +" +START TRANSACTION; \ +DELETE FROM lease6_stat; \ +INSERT INTO lease6_stat (subnet_id, lease_type, state, leases) \ + SELECT subnet_id, lease_type, state, COUNT(*) \ + FROM lease6 WHERE state = 0 OR state = 1 \ + GROUP BY subnet_id, lease_type, state; \ +DELETE FROM lease6_pool_stat; \ +INSERT INTO lease6_pool_stat (subnet_id, pool_id, lease_type, state, leases) \ + SELECT subnet_id, pool_id, lease_type, state, count(*) FROM lease6 \ + WHERE state = 0 OR state = 1 GROUP BY subnet_id, pool_id, lease_type, state; \ +COMMIT;" +export _RECOUNT6_QUERY diff --git a/src/bin/admin/kea-admin.in b/src/bin/admin/kea-admin.in new file mode 100644 index 0000000..034a0ee --- /dev/null +++ b/src/bin/admin/kea-admin.in @@ -0,0 +1,1090 @@ +#!/bin/sh + +# Copyright (C) 2014-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/. + +# This is kea-admin script that conducts administrative tasks on the Kea +# installation. Currently supported operations are: +# +# - database init +# - database version check +# - database version upgrade +# - lease database dump +# - lease upload to the database +# - lease database recount + +# shellcheck disable=SC1091 +# SC1091: Not following: ... was not specified as input (see shellcheck -x). + +# shellcheck disable=SC2039 +# SC2039: In POSIX sh, 'local' is undefined. + +# shellcheck disable=SC2086 +# SC2086: Double quote to prevent globbing and word splitting. +# Reason for disable: explicitly don't quote extra_arguments so it is +# considered multiple arguments instead of one. + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# Shell ${variables} derived from autoconf @variables@. Some depend on others, so mind the order. +prefix="@prefix@" +export prefix +exec_prefix="@exec_prefix@" +export exec_prefix +SCRIPTS_DIR_DEFAULT="@datarootdir@/@PACKAGE@/scripts" +scripts_dir="${SCRIPTS_DIR_DEFAULT}" +VERSION="@PACKAGE_VERSION@" + +assume_yes=0 + +# lease dump parameters +dhcp_version=0 +dump_file="" +dump_qry="" + +# Include the installed admin-utils.sh if available. Fallback to sources otherwise. +if test -f "@datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh"; then + . "@datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh" +else + . "@abs_top_builddir@/src/bin/admin/admin-utils.sh" +fi + +# Find the installed kea-lfc if available. Fallback to sources otherwise. +if test -x "@sbindir@/kea-lfc"; then + kea_lfc="@sbindir@/kea-lfc" +else + kea_lfc="@abs_top_builddir@/src/bin/lfc/kea-lfc" +fi + +# Prints out usage version. +usage() { + printf \ +' +kea-admin %s + +This is a kea-admin script that conducts administrative tasks on +the Kea installation. + +Usage: %s COMMAND BACKEND [parameters] + +COMMAND: Currently supported operations are: + + - db-init: Initializes new database. Useful for first time installation. + - db-version: Checks version of the existing database schema. Useful + - for checking database version when preparing for an upgrade. + - db-upgrade: Upgrades your database schema. + - lease-dump: Dumps current leases to a memfile-ready CSV file. + - lease-upload: Uploads leases from a CSV file to the database. + - stats-recount: Recounts lease statistics. + +BACKEND - one of the supported backends: memfile|mysql|pgsql + +PARAMETERS: Parameters are optional in general, but may be required + for specific operations. + -h or --host hostname - specifies a hostname of a database to connect to + -P or --port port - specifies the TCP port to use for the database connection + -u or --user name - specifies username when connecting to a database + -p or --password [password] - specifies a password for the database connection; + if omitted from the command line, + then the user will be prompted for a password + -n or --name database - specifies a database name to connect to + -d or --directory - path to upgrade scripts (default: %s) + -v or --version - print kea-admin version and quit. + -x or --extra - specifies extra argument(s) to pass to the database command + + Parameters specific to lease-dump, lease-upload: + -4 to dump IPv4 leases to file + -6 to dump IPv6 leases to file + -i or --input to specify the name of file from which leases will be uploaded + -o or --output to specify the name of file to which leases will be dumped + -y or --yes - assume yes on overwriting temporary files +' "${VERSION}" "${0}" "${SCRIPTS_DIR_DEFAULT}" +} + +### Logging functions ### + +# Logs message at the error level. +# Takes one parameter that is printed as is. +log_error() { + printf "ERROR/kea-admin: %s\n" "${1}" +} + +# Logs message at the warning level. +# Takes one parameter that is printed as is. +log_warning() { + printf "WARNING/kea-admin: %s\n" "${1}" +} + +# Logs message at the info level. +# Takes one parameter that is printed as is. +log_info() { + printf "INFO/kea-admin: %s\n" "${1}" +} + +### Convenience functions ### + +# Checks if the value is in the list. An example usage of this function +# is to determine whether the kea-admin command belongs to the list of +# supported commands. +is_in_list() { + local member="${1-}" # Value to be checked + local list="${2-}" # Comma separated list of items + _inlist=0 # Return value: 0 if not in list, 1 otherwise. + if [ -z "${member}" ]; then + log_error "missing member (need to specify a string as first param)" + fi + # Iterate over all items on the list and compare with the member. + # If they match, return, otherwise log error and exit. + for item in ${list} + do + if [ "${item}" = "${member}" ]; then + _inlist=1 + return + fi + done +} + +### Functions that implement database initialization commands + +memfile_init() { + # Useless as Kea converts CSV versions at startup. + log_error "NOT IMPLEMENTED" + exit 1 +} + +# Validates that the MySQL db_users's permissions are sufficient to +# create the schema. +mysql_can_create() { + + # Let's grab the version for possible debugging issues. It also + # determines basic functional access to db. + run_command \ + mysql_execute "select @@global.version;" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql_can_create: get MySQL version failed, mysql status = ${EXIT_CODE}" + exit 1 + fi + + # shellcheck disable=SC2153 + # SC2153: Possible misspelling: ... may not be assigned, but ... is. + # Reason for disable: OUTPUT is assigned in run_command. + printf "MySQL Version is: %s\n" "${OUTPUT}" + + # SQL to drop our test table and trigger + cleanup_sql="DROP TABLE IF EXISTS kea_dummy_table; DROP PROCEDURE IF EXISTS kea_dummy_trigger;" + + # SQL to create our test table + table_sql="CREATE TABLE kea_dummy_table(dummy INT UNSIGNED PRIMARY KEY NOT NULL);" + + # SQL to create our test trigger + trigger_sql="\ +CREATE TRIGGER kea_dummy_trigger BEFORE insert ON kea_dummy_table FOR EACH ROW\n \ +BEGIN\n \ +END;" + + # Let's clean up just in case. + run_command \ + mysql_execute "$cleanup_sql" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql_can_create cannot run pre cleanup, mysql status = ${EXIT_CODE}" + exit 1; + fi + + # Now make the dummy table. + perms_ok=1 + run_command \ + mysql_execute "$table_sql" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql_can_create cannot create table, check user permissions, mysql status = ${EXIT_CODE}" + perms_ok=0; + else + # Now attempt to make trigger + run_command \ + mysql_execute "$trigger_sql" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql_can_create cannot trigger, check user permissions, mysql status = ${EXIT_CODE}" + perms_ok=0; + fi + fi + + # Try to cleanup no matter what happened above + run_command \ + mysql_execute "$cleanup_sql" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql_can_create cannot run post cleanup, mysql status = ${EXIT_CODE}" + exit 1; + fi + + if [ $perms_ok -ne 1 ] + then + log_error "Create failed, the user, $db_user, has insufficient privileges." + exit 1; + fi +} + +# Initializes a new, empty MySQL database. +# It essentially calls scripts/mysql/dhcpdb_create.mysql script, with +# some extra sanity checks. It will refuse to use it if there are any +# existing tables. It's better safe than sorry. +mysql_init() { + printf 'Checking if there is a database initialized already...\n' + + # Let's try to count the number of tables. Anything above 0 means that there + # is some database in place. If there is anything, we abort. Note that + # mysql may spit out connection or access errors to stderr, we ignore those. + # We should not hide them as they may give hints to user what is wrong with + # his setup. + run_command \ + mysql_execute "SHOW TABLES;" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql_init table query failed, mysql status = ${EXIT_CODE}" + exit 1 + fi + + count=$(printf '%s' "${OUTPUT}" | wc -w) + if [ "${count}" -gt 0 ]; then + # Let's start with a new line. mysql could have printed something out. + printf '\n' + log_error "Expected empty database ${db_name}. Aborting, the following tables are present: + ${OUTPUT}" + exit 1 + fi + + # Beginning with MySQL 8.0, the db user needs additional settings or SUPER + # privileges to create triggers and or functions. Call mysql_can_create to find + # out if we're good to go. If not, it will exit. + printf "Verifying create permissions for %s\n" "$db_user" + mysql_can_create + + printf "Initializing database using script %s\n" $scripts_dir/mysql/dhcpdb_create.mysql + mysql -B --host="${db_host}" --user="${db_user}" \ + --password="${db_password}" \ + "${db_name}" ${extra_arguments} < "${scripts_dir}/mysql/dhcpdb_create.mysql" + + printf "mysql returned status code %s\n" "${EXIT_CODE}" + + if [ "${EXIT_CODE}" -eq 0 ]; then + printf "Database version reported after initialization: " + checked_mysql_version + printf '\n' + fi + + exit "${EXIT_CODE}" +} + +pgsql_init() { + printf 'Checking if there is a database initialized already...\n' + + # Let's try to count the number of tables. Anything above 0 means that there + # is some database in place. If there is anything, we abort. + run_command \ + pgsql_execute "\d" + if [ "${EXIT_CODE}" -ne 0 ]; then + log_error "pgsql_init: table query failed, status code: ${EXIT_CODE}?" + exit 1 + fi + + count=$(printf '%s' "${OUTPUT}" | wc -w) + if [ "${count}" -gt 0 ]; then + # Let's start with a new line. pgsql could have printed something out. + printf '\n' + log_error "Expected empty database ${db_name}. Aborting, the following tables are present: + ${OUTPUT}" + exit 2 + fi + + init_script="$scripts_dir/pgsql/dhcpdb_create.pgsql" + printf "Initializing database using script %s\n" $init_script + run_command \ + pgsql_execute_script $init_script + if [ "${EXIT_CODE}" -ne 0 ]; then + log_error "Database initialization failed, status code: ${EXIT_CODE}?" + exit 1 + fi + + version=$(checked_pgsql_version) + printf "Database version reported after initialization: %s\n" "$version" + exit 0 +} + +### Functions that implement database version checking commands +memfile_version() { + # @todo Implement this? + log_error "NOT IMPLEMENTED" + exit 1 +} + +### Functions used for upgrade +memfile_upgrade() { + # Useless as Kea converts CSV versions at startup. + log_error "NOT IMPLEMENTED" + exit 1 +} + +# Upgrades existing MySQL database installation. The idea is that +# it will go over all upgrade scripts from (prefix)/share/kea/scripts/mysql +# and run them one by one. They will be named properly, so they will +# be run in order. +# +# This function prints version before and after upgrade. +mysql_upgrade() { + + printf "Database version reported before upgrade: " + checked_mysql_version + printf '\n' + + upgrade_scripts_dir=${scripts_dir}/mysql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find "${upgrade_scripts_dir}" -name 'upgrade*.sh' -type f | wc -l) + if [ "$num_files" -eq 0 ]; then + upgrade_scripts_dir=@abs_top_builddir@/src/share/database/scripts/mysql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find "${upgrade_scripts_dir}" -name 'upgrade*.sh' -type f | wc -l) + fi + + if [ "$num_files" -eq 0 ]; then + log_error "No scripts in ${upgrade_scripts_dir} or the directory is not readable or does not have any upgrade* scripts." + exit 1 + fi + + # Beginning with MySQL 8.0, the db user needs additional settings or SUPER + # privileges to create triggers and or functions. Call mysql_can_create to find + # out if we're good to go. If not, it will exit. + printf "Verifying upgrade permissions for %s\n" "$db_user" + mysql_can_create + + for script in "${upgrade_scripts_dir}"/upgrade*.sh + do + echo "Processing $script file..." + "${script}" --host="${db_host}" --user="${db_user}" \ + --password="${db_password}" "${db_name}" ${extra_arguments} + done + + printf "Database version reported after upgrade: " + checked_mysql_version + printf '\n' +} + +pgsql_upgrade() { + version=$(checked_pgsql_version) + printf "Database version reported before upgrade: %s\n" "$version" + + upgrade_scripts_dir=${scripts_dir}/pgsql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find "${upgrade_scripts_dir}" -name 'upgrade*.sh' -type f | wc -l) + if [ "$num_files" -eq 0 ]; then + upgrade_scripts_dir=@abs_top_builddir@/src/share/database/scripts/pgsql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find "${upgrade_scripts_dir}" -name 'upgrade*.sh' -type f | wc -l) + fi + + if [ "$num_files" -eq 0 ]; then + log_error "No scripts in ${upgrade_scripts_dir} or the directory is not readable or does not have any upgrade* scripts." + exit 1 + fi + + # Postgres psql does not accept pw on command line, but can do it + # thru an env + export PGPASSWORD=$db_password + + for script in "${upgrade_scripts_dir}"/upgrade*.sh + do + echo "Processing $script file..." + "${script}" -U "${db_user}" -h "${db_host}" \ + -d "${db_name}" ${extra_arguments} + done + + version=$(checked_pgsql_version) + printf "Database version reported after upgrade: %s\n" "$version" + exit 0 +} + +# Remove a file if it exists +remove_file () { + local file="${1}" + if [ -e "${file}" ] + then + log_info "Removing file ${file}..." + rm -f "${file}" + fi +} + +# Utility function which tests if the given file exists and +# if so notifies the user and provides them the opportunity +# to abort the current command. +check_file_overwrite () { + local file="${1}" + if [ $assume_yes -eq 1 ] + then + remove_file "${file}" + elif [ -e "${file}" ] + then + echo "Output file, $file, exists and will be overwritten." + echo "Do you wish to continue? (y/N)" + + # Ask for an answer only on an interactive shell to prevent blocking in + # automated or non-interactive scenarios where the answer defaults to no. + if test -t 0; then + read -r ans + else + log_warning 'Non-interactive tty detected. Assuming no.' + ans='N' + fi + + if [ "${ans}" != "y" ] + then + echo "$command aborted by user." + exit 1 + fi + fi +} + +### Functions used for dump + +# Sets the global variable, dump_qry, to the schema-version specific +# SQL text needed to dump the lease data for the current backend +# and protocol +get_dump_query() { + local version="${1}" + + case ${backend} in + mysql) + invoke="call" + ;; + pgsql) + invoke="select * from" + ;; + *) + log_error "unsupported backend ${backend}" + usage + exit 1 + ;; + esac + + dump_qry="${invoke} lease${dhcp_version}DumpHeader();${invoke} lease${dhcp_version}DumpData();"; +} + +memfile_dump() { + log_error "lease-dump is not supported for memfile" + exit 1 +} + +mysql_dump() { + + # Check the lease type was given + if [ ${dhcp_version} -eq 0 ]; then + log_error "lease-dump: lease type ( -4 or -6 ) needs to be specified" + usage + exit 1 + fi + + # get the correct dump query + run_command \ + mysql_version + version="${OUTPUT}" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "lease-dump: mysql_version failed, exit code ${EXIT_CODE}" + exit 1 + fi + + # Fetch the correct SQL text. Note this function will exit + # if it fails. + get_dump_query "$version" + + # Make sure they specified a file + if [ "$dump_file" = "" ]; then + log_error "you must specify an output file for lease-dump" + usage + exit 1 + + fi + + # If output file exists, notify user, allow them a chance to bail + check_file_overwrite "$dump_file" + + # Check the temp file too + tmp_file="/tmp/$(basename "${dump_file}").tmp" + check_file_overwrite $tmp_file + + # Run the sql to output tab-delimited lease data to a temp file. + # By using a temp file we can check for MySQL errors before using + # 'tr' to translate tabs to commas. We do not use MySQL's output + # to file as that requires linux superuser privileges to execute + # the select. + if ! mysql_execute "${dump_qry}" > $tmp_file; then + log_error "lease-dump: mysql_execute failed, exit code ${EXIT_CODE}" + exit 1 + fi + + # Now translate tabs to commas. + if ! tr '\t' ',' < "${tmp_file}" > "${dump_file}"; then + log_error "lease-dump: reformatting failed"; + exit 1 + fi + + # Clean up the temporary file. + rm -f "${tmp_file}" + log_info "Removed temporary file ${tmp_file}." + + log_info "Successfully dumped lease${dhcp_version} to ${dump_file}." + exit 0 +} + +### Functions used for dump +pgsql_dump() { + # Check the lease type was given + if [ ${dhcp_version} -eq 0 ]; then + log_error "lease-dump: lease type ( -4 or -6 ) needs to be specified" + usage + exit 1 + fi + + version=$(pgsql_version) + get_dump_query "$version" + + # Make sure they specified a file + if [ "$dump_file" = "" ]; then + log_error "you must specify an output file for lease-dump" + usage + exit 1 + + fi + + # If output file exists, notify user, allow them a chance to bail + check_file_overwrite "$dump_file" + + # psql does not accept password as a parameter but will look in the environment + export PGPASSWORD=$db_password + + # Call psql and redirect output to the dump file. We don't use psql "to csv" + # as it can only be run as db superuser. Check for errors. + if ! ( + echo "${dump_qry}" | \ + psql --set ON_ERROR_STOP=1 -t -h "${db_host}" -q --user="${db_user}" \ + --dbname="${db_name}" -w --no-align --field-separator=',' \ + ${extra_arguments} > "${dump_file}" + ); then + log_error "lease-dump: psql call failed, exit code: ${?}" + exit 1 + fi + + echo lease${dhcp_version} successfully dumped to "${dump_file}" + exit 0 +} + +######################## functions used in lease-upload ######################## + +# Finds the position of a column by name, starting with 1. +get_column_position() { + local column_name="${1}"; shift + + # Look only in the header, count the number of commas up to the column. + position=$(head -n 1 "${input_file}" | grep -Eo ",|${column_name}" | uniq -c | \ + head -n 1 | grep ',' | tr -s ' ' | cut -d ' ' -f 2) + + if test -z "${position}"; then + # If no commas, that means position 1. + printf '1' + else + # Else increment, so that it starts at 1. + printf '%s' "$((position + 1))" + fi +} + +# Adds quotes around values at given positions starting with 1 in a CSV line. +stringify_positions_in_line() { + local positions="${1}"; shift + local line="${1}"; shift + + i=1 + output= + for p in ${positions}; do + # Get everything up to position p. + if test "${i}" -lt "${p}"; then + up_until_p=$(printf '%s' "${line}" | cut -d ',' -f "${i}-$((p - 1))") + else + up_until_p='' + fi + + # Get value at position p. + p_word=$(printf '%s' "${line}" | cut -d ',' -f "${p}") + + # Add comma unless we're doing the first append. + if test "${i}" != 1; then + output="${output}," + fi + + # Append everything up to position p. + output="${output}${up_until_p}" + + # Add comma if we're not stringifying position 1 and if there is + # anything up to position p. In the second case, the comma was already + # added at a previous step. + if test "${p}" != 1 && test -n "${up_until_p}"; then + output="${output}," + fi + + # Add value at position p. + output="${output}'${p_word}'" + + # Skip position p when getting the values between positions next time. + i=$((p + 1)) + done + + # The last position might be somewhere in the middle, so add everything + # until the end. + the_rest=$(printf '%s' "${line}" | cut -d ',' -f ${i}-) + + # Add comma unless we're adding the whole line or nothing. + if test "${i}" != 1 && test -n "${the_rest}"; then + output="${output}," + fi + + # Append. + output="${output}${the_rest}" + + # Print back to the caller. + printf '%s' "${output}" +} + +# Entry point for the lease-upload command. +lease_upload() { + # Check the lease type was given + if [ ${dhcp_version} -eq 0 ]; then + log_error "lease-upload: lease type ( -4 or -6 ) needs to be specified" + usage + exit 1 + fi + + # Check that an input file was specified. + if test -z "${input_file-}"; then + log_error 'you must specify an input file with -i or --input for lease-upload' + usage + exit 1 + fi + + # Check that the input file has at least one row of values. + input_file_line_length=$(wc -l < "${input_file}") + if test "${input_file_line_length}" -le 1; then + log_error 'CSV file has no leases' + exit 1 + fi + + # Invoke LFC on the input file. + log_info "Looking at ${input_file_line_length} lines of CSV in ${input_file}..." + cleaned_up_csv="/tmp/$(basename "${input_file}").tmp" + check_file_overwrite "${cleaned_up_csv}" + cp "${input_file}" "${cleaned_up_csv}" + "${kea_lfc}" "-${dhcp_version}" -x "${cleaned_up_csv}" \ + -i "${cleaned_up_csv}.1" -o "${cleaned_up_csv}.output" \ + -f "${cleaned_up_csv}.completed" -p "${cleaned_up_csv}.pid" \ + -cignored-path + cleaned_up_csv_line_length=$(wc -l < "${cleaned_up_csv}") + log_info "Reduced to ${cleaned_up_csv_line_length} lines in ${cleaned_up_csv}." + + # Determine the columns whose values need to be stringified to avoid syntax + # errors in the MySQL client. These are columns which are VARCHARs or need + # to be further processed by a procedure. + if test "${dhcp_version}" = '4'; then + string_columns='address hwaddr client_id hostname user_context' + else + string_columns='address duid hostname hwaddr user_context' + fi + + # Get positions of string columns. + string_positions= + for i in ${string_columns}; do + string_positions="${string_positions} $(get_column_position "${i}")" + done + + # Construct the SQL insert statements. + header_parsed=false + sql_statement='START TRANSACTION;' + while read -r line; do + if "${header_parsed}"; then + line=$(stringify_positions_in_line "${string_positions}" "${line}") + if test "${backend}" = 'mysql'; then + sql_statement="${sql_statement} CALL lease${dhcp_version}Upload(${line}); " + elif test "${backend}" = 'pgsql'; then + sql_statement="${sql_statement} SELECT lease${dhcp_version}Upload(${line}); " + else + log_error "lease-upload not implemented for ${backend}" + exit 1 + fi + else + header_parsed=true + fi + done < "${cleaned_up_csv}" + sql_statement="${sql_statement} COMMIT;" + + # Execute the SQL insert statements. + if test "${backend}" = 'mysql'; then + output="$(mysql_execute "${sql_statement}")" + elif test "${backend}" = 'pgsql'; then + output="$(pgsql_execute "${sql_statement}")" + else + log_error "lease-upload not implemented for ${backend}" + exit 1 + fi + + # Clean up the temporary CSV. + rm -f "${cleaned_up_csv}" + log_info "Removed temporary file ${cleaned_up_csv}." + + # Print a confirmation message. + log_info "Successfully updated table lease${dhcp_version}." +} + +### Functions used for recounting statistics +mysql_recount() { + printf "Recount lease statistics from database\n" + + run_command \ + mysql_execute "$_RECOUNT4_QUERY" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql failed to recount IPv4 leases, mysql status = ${EXIT_CODE}" + exit 1 + fi + + run_command \ + mysql_execute "$_RECOUNT6_QUERY" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "mysql failed to recount IPv6 leases, mysql status = ${EXIT_CODE}" + exit 1 + fi +} + +pgsql_recount() { + printf "Recount lease statistics from database\n" + + run_command \ + pgsql_execute "$_RECOUNT4_QUERY" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "pgsql failed to recount IPv4 leases, pgsql status = ${EXIT_CODE}" + exit 1 + fi + + run_command \ + pgsql_execute "$_RECOUNT6_QUERY" + if [ "${EXIT_CODE}" -ne 0 ] + then + log_error "pgsql failed to recount IPv6 leases, pgsql status = ${EXIT_CODE}" + exit 1 + fi +} + +### Script starts here ### + +# First, find what the command is +command=${1-} +if [ -z "${command}" ]; then + log_error "missing command" + usage + exit 1 +fi + +# Check if this is a simple question about version. +if test "${command}" = "-v" || test "${command}" = "--version" ; then + echo "${VERSION}" + exit 0 +fi + +is_in_list "${command}" "db-init db-version db-upgrade lease-dump lease-upload stats-recount" +if [ "${_inlist}" -eq 0 ]; then + log_error "invalid command: ${command}" + usage + exit 1 +fi +shift + +# Second, check what's the backend +backend=${1-} +if [ -z "${backend}" ]; then + log_error "missing backend" + usage + exit 1 +fi +is_in_list "${backend}" "memfile mysql pgsql" +if [ "${_inlist}" -eq 0 ]; then + log_error "invalid backend: ${backend}" + exit 1 +fi +shift + +# Ok, let's process parameters (if there are any) +while test "${#}" -gt 0 +do + option=${1} + case ${option} in + # Specify database host + -h|--host) + shift + db_host=${1-} + if [ -z "${db_host}" ]; then + log_error "-h or --host requires a parameter" + usage + exit 1 + fi + ;; + # Specify database port + -P|--port) + shift + if test -z "${1+x}"; then + log_error '-P or --port requires a parameter' + usage + exit 1 + fi + db_port=${1} + export db_port_full_parameter="--port=${db_port}" + ;; + # Specify database user + -u|--user) + shift + db_user=${1-} + if [ -z "${db_user}" ]; then + log_error "-u or --user requires a parameter" + usage + exit 1 + fi + ;; + # Specify database password + -p|--password) + password_parameter_passed=true + # If there is at least one more parameter following... + if test "${#}" -gt 1; then + # Then take it as password. + shift + db_password=${1} + else + # If it's an interactive shell... + if test -t 0; then + # Read from standard input while hiding feedback to the terminal. + printf 'Password: ' + stty -echo + read -r db_password + stty echo + printf '\n' + else + log_warning 'Non-interactive tty detected. Assuming empty password.' + db_password='' + fi + fi + ;; + # Specify database name + -n|--name) + shift + db_name=${1-} + if [ -z "${db_name}" ]; then + log_error "-n or --name requires a parameter" + usage + exit 1 + fi + ;; + -d|--directory) + shift + scripts_dir=${1-} + if [ -z "${scripts_dir}" ]; then + log_error "-d or --directory requires a parameter" + usage + exit 1 + fi + ;; + # specify DHCPv4 lease type + -4) + if [ ${dhcp_version} -eq 6 ]; then + log_error "you may not specify both -4 and -6" + usage + exit 1 + fi + dhcp_version=4 + ;; + # specify DHCPv6 lease type + -6) + if [ ${dhcp_version} -eq 4 ]; then + log_error "you may not specify both -4 and -6" + usage + exit 1 + fi + dhcp_version=6 + ;; + # specify input file, used by lease-upload + -i|--input) + shift + input_file=${1-} + if [ -z "${input_file}" ]; then + log_error '-i or --input requires a parameter' + usage + exit 1 + fi + ;; + # specify output file, currently only used by lease dump + -o|--output) + shift + dump_file=${1-} + if [ -z "${dump_file}" ]; then + log_error "-o or --output requires a parameter" + usage + exit 1 + fi + ;; + # specify extra arguments to pass to the database command + -x|--extra) + shift + if [ -z "${1-}" ]; then + log_error "-x or --extra requires a parameter" + usage + exit 1 + fi + if [ -z "${extra_arguments}" ]; then + extra_arguments=${1} + else + extra_arguments="${extra_arguments} ${1}" + fi + ;; + -y|--yes) + assume_yes=1 + ;; + *) + log_error "invalid option: ${option}" + usage + exit 1 + esac + shift +done + +# After all the parameters have been parsed, check environment variables. +if test -z "${password_parameter_passed+x}"; then + if test -n "${KEA_ADMIN_DB_PASSWORD+x}"; then + printf 'Using the value of KEA_ADMIN_DB_PASSWORD for authentication...\n' + db_password="${KEA_ADMIN_DB_PASSWORD}" + fi +fi + +case ${command} in + # Initialize the database + db-init) + case ${backend} in + memfile) + memfile_init + ;; + mysql) + mysql_init + ;; + pgsql) + pgsql_init + ;; + esac + ;; + db-version) + case ${backend} in + memfile) + memfile_version + ;; + mysql) + checked_mysql_version + printf '\n' + ;; + pgsql) + checked_pgsql_version + ;; + esac + ;; + db-upgrade) + case ${backend} in + memfile) + memfile_upgrade + ;; + mysql) + mysql_upgrade + ;; + pgsql) + pgsql_upgrade + ;; + esac + ;; + lease-dump) + case ${backend} in + memfile) + memfile_dump + ;; + mysql) + mysql_dump + ;; + pgsql) + pgsql_dump + ;; + esac + ;; + lease-upload) + case ${backend} in + memfile) + log_error 'lease-upload is not supported for memfile' + exit 1 + ;; + mysql) + lease_upload + ;; + pgsql) + lease_upload + ;; + esac + ;; + stats-recount) + case ${backend} in + memfile) + log_info "memfile does not keep lease statistics" + ;; + mysql) + mysql_recount + ;; + pgsql) + pgsql_recount + ;; + esac + ;; +esac + +exit 0 diff --git a/src/bin/admin/tests/Makefile.am b/src/bin/admin/tests/Makefile.am new file mode 100644 index 0000000..9e0b2c2 --- /dev/null +++ b/src/bin/admin/tests/Makefile.am @@ -0,0 +1,32 @@ +SUBDIRS = data . + +# Add to the tarball: +EXTRA_DIST = +EXTRA_DIST += dhcpdb_create_1.0.mysql +EXTRA_DIST += dhcpdb_create_1.0.pgsql + +# Shell tests +SHTESTS = +SHTESTS += admin_tests.sh +SHTESTS += memfile_tests.sh +if HAVE_MYSQL +SHTESTS += mysql_tests.sh +endif +if HAVE_PGSQL +SHTESTS += pgsql_tests.sh +endif + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) + +if HAVE_GTEST + +# Run tests on "make check". +check_SCRIPTS = $(SHTESTS) +TESTS = $(SHTESTS) + +endif + +# Don't install shell tests. +noinst_SCRIPTS = $(SHTESTS) diff --git a/src/bin/admin/tests/Makefile.in b/src/bin/admin/tests/Makefile.in new file mode 100644 index 0000000..b48b20c --- /dev/null +++ b/src/bin/admin/tests/Makefile.in @@ -0,0 +1,880 @@ +# 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@ +@HAVE_MYSQL_TRUE@am__append_1 = mysql_tests.sh +@HAVE_PGSQL_TRUE@am__append_2 = pgsql_tests.sh +subdir = src/bin/admin/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 = admin_tests.sh memfile_tests.sh mysql_tests.sh \ + pgsql_tests.sh +CONFIG_CLEAN_VPATH_FILES = +SCRIPTS = $(noinst_SCRIPTS) +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 = +SOURCES = +DIST_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 +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 $(srcdir)/admin_tests.sh.in \ + $(srcdir)/memfile_tests.sh.in $(srcdir)/mysql_tests.sh.in \ + $(srcdir)/pgsql_tests.sh.in +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 = data . + +# Add to the tarball: +EXTRA_DIST = dhcpdb_create_1.0.mysql dhcpdb_create_1.0.pgsql + +# Shell tests +SHTESTS = admin_tests.sh memfile_tests.sh $(am__append_1) \ + $(am__append_2) + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) + +# Run tests on "make check". +@HAVE_GTEST_TRUE@check_SCRIPTS = $(SHTESTS) +@HAVE_GTEST_TRUE@TESTS = $(SHTESTS) + +# Don't install shell tests. +noinst_SCRIPTS = $(SHTESTS) +all: all-recursive + +.SUFFIXES: +$(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/bin/admin/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/bin/admin/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): +admin_tests.sh: $(top_builddir)/config.status $(srcdir)/admin_tests.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +memfile_tests.sh: $(top_builddir)/config.status $(srcdir)/memfile_tests.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +mysql_tests.sh: $(top_builddir)/config.status $(srcdir)/mysql_tests.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +pgsql_tests.sh: $(top_builddir)/config.status $(srcdir)/pgsql_tests.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +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_SCRIPTS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(SCRIPTS) +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: + +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) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +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 mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am 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 Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: 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 check \ + check-TESTS check-am clean clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean 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-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/bin/admin/tests/admin_tests.sh.in b/src/bin/admin/tests/admin_tests.sh.in new file mode 100644 index 0000000..c6b8a26 --- /dev/null +++ b/src/bin/admin/tests/admin_tests.sh.in @@ -0,0 +1,147 @@ +#!/bin/sh + +# Copyright (C) 2021-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/. + +# shellcheck disable=SC1091 +# SC1091: Not following: ... was not specified as input (see shellcheck -x). + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# Include common test library. +. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh" + +# Include admin utilities +. "@abs_top_builddir@/src/bin/admin/admin-utils.sh" + +# Set location of the kea-admin. +kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin" + +kea_admin_error_reporting_tests() { + test_start 'kea_admin_error_reporting_test.db_init' + run_command \ + "${kea_admin}" db-init + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: missing backend' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.u' + run_command \ + "${kea_admin}" db-init mysql -u + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -u or --user requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.user' + run_command \ + "${kea_admin}" db-init mysql --user + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -u or --user requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.h' + run_command \ + "${kea_admin}" db-init mysql -h + assert_str_eq 'ERROR/kea-admin: -h or --host requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + assert_eq 1 "${EXIT_CODE}" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.host' + run_command \ + "${kea_admin}" db-init mysql --host + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -h or --host requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.n' + run_command \ + "${kea_admin}" db-init mysql -n + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -n or --name requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.name' + run_command \ + "${kea_admin}" db-init mysql --name + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -n or --name requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.d' + run_command \ + "${kea_admin}" db-init mysql -d + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -d or --directory requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_init.mysql.directory' + run_command \ + "${kea_admin}" db-init mysql --directory + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -d or --directory requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.lease_upload.mysql.i' + run_command \ + "${kea_admin}" lease-upload mysql -i + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -i or --input requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.lease_upload.mysql.input' + run_command \ + "${kea_admin}" lease-upload mysql --input + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -i or --input requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.lease_dump.mysql.o' + run_command \ + "${kea_admin}" lease-dump mysql -o + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -o or --output requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.lease_dump.mysql.output' + run_command \ + "${kea_admin}" lease-dump mysql --output + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -o or --output requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.lease_dump.mysql.lease_type_missing' + run_command \ + "${kea_admin}" lease-dump mysql + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: lease-dump: lease type ( -4 or -6 ) needs to be specified' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.lease_upload.mysql.lease_type_missing' + run_command \ + "${kea_admin}" lease-upload mysql + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: lease-upload: lease type ( -4 or -6 ) needs to be specified' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_version.mysql.x' + run_command \ + "${kea_admin}" db-version mysql -x + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -x or --extra requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} + + test_start 'kea_admin_error_reporting_test.db_version.mysql.extra' + run_command \ + "${kea_admin}" db-version mysql --extra + assert_eq 1 "${EXIT_CODE}" + assert_str_eq 'ERROR/kea-admin: -x or --extra requires a parameter' "$(printf '%s\n' "${OUTPUT}" | head -n 1)" + test_finish ${?} +} + +# Run tests. +kea_admin_error_reporting_tests diff --git a/src/bin/admin/tests/data/Makefile.am b/src/bin/admin/tests/data/Makefile.am new file mode 100644 index 0000000..7ce6289 --- /dev/null +++ b/src/bin/admin/tests/data/Makefile.am @@ -0,0 +1,3 @@ +EXTRA_DIST = \ + lease4_dump_test.reference.csv \ + lease6_dump_test.reference.csv diff --git a/src/bin/admin/tests/data/Makefile.in b/src/bin/admin/tests/data/Makefile.in new file mode 100644 index 0000000..43b751f --- /dev/null +++ b/src/bin/admin/tests/data/Makefile.in @@ -0,0 +1,551 @@ +# 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/bin/admin/tests/data +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 = +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 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +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@ +EXTRA_DIST = \ + lease4_dump_test.reference.csv \ + lease6_dump_test.reference.csv + +all: all-am + +.SUFFIXES: +$(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/bin/admin/tests/data/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/bin/admin/tests/data/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): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +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 +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +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: + +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-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool 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 \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + 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/bin/admin/tests/data/lease4_dump_test.reference.csv b/src/bin/admin/tests/data/lease4_dump_test.reference.csv new file mode 100644 index 0000000..05eb50c --- /dev/null +++ b/src/bin/admin/tests/data/lease4_dump_test.reference.csv @@ -0,0 +1,4 @@ +address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id +0.0.0.10,32:30,33:30,40,1642000000,50,1,1,one.example.com,0,,0 +0.0.0.11,,31:32:33,40,1643210000,50,1,1,,1,{ },0 +0.0.0.12,32:32,,40,1643212345,50,1,1,threeˎxampleˌom,2,{ "a": 1, "b": "c" },0 diff --git a/src/bin/admin/tests/data/lease6_dump_test.reference.csv b/src/bin/admin/tests/data/lease6_dump_test.reference.csv new file mode 100644 index 0000000..3693537 --- /dev/null +++ b/src/bin/admin/tests/data/lease6_dump_test.reference.csv @@ -0,0 +1,4 @@ +address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,state,user_context,hwtype,hwaddr_source,pool_id +::10,32:30:33,30,1642000000,40,50,1,60,128,1,1,one.example.com,38:30,0,,90,16,0 +::11,32:31:33,30,1643210000,40,50,1,60,128,1,1,,38:30,1,{ },90,1,0 +::12,32:32:33,30,1643212345,40,50,1,60,128,1,1,threeˎxampleˌom,38:30,2,{ "a": 1, "b": "c" },90,4,0 diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.mysql b/src/bin/admin/tests/dhcpdb_create_1.0.mysql new file mode 100644 index 0000000..181d536 --- /dev/null +++ b/src/bin/admin/tests/dhcpdb_create_1.0.mysql @@ -0,0 +1,131 @@ +# Copyright (C) 2012-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/. + +# This is the Kea schema 1.0 specification for MySQL. +# Note: this is outdated version on purpose and it used to test upgrade +# process. Do not update this file to 2.0 or any later. +# +# The schema is reasonably portable (with the exception of the engine +# specification, which is MySQL-specific). Minor changes might be needed for +# other databases. + +# To create the schema, either type the command: +# +# mysql -u <user> -p <password> <database> < dhcpdb_create.mysql +# +# ... at the command prompt, or log in to the MySQL database and at the "mysql>" +# prompt, issue the command: +# +# source dhcpdb_create.mysql + + +# Holds the IPv4 leases. +CREATE TABLE lease4 ( + address INT UNSIGNED PRIMARY KEY NOT NULL, # IPv4 address + hwaddr VARBINARY(20), # Hardware address + client_id VARBINARY(128), # Client ID + valid_lifetime INT UNSIGNED, # Length of the lease (seconds) + expire TIMESTAMP, # Expiration time of the lease + subnet_id INT UNSIGNED, # Subnet identification + fqdn_fwd BOOL, # Has forward DNS update been performed by a server + fqdn_rev BOOL, # Has reverse DNS update been performed by a server + hostname VARCHAR(255) # The FQDN of the client + ) ENGINE = INNODB; + + +# Create search indexes for lease4 table. +# index by hwaddr and subnet_id +CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id); + +# index by client_id and subnet_id +CREATE INDEX lease4_by_client_id_subnet_id ON lease4 (client_id, subnet_id); + +# Holds the IPv6 leases. +# N.B. The use of a VARCHAR for the address is temporary for development: +# it will eventually be replaced by BINARY(16). +CREATE TABLE lease6 ( + address VARCHAR(39) PRIMARY KEY NOT NULL, # IPv6 address + duid VARBINARY(128), # DUID + valid_lifetime INT UNSIGNED, # Length of the lease (seconds) + expire TIMESTAMP, # Expiration time of the lease + subnet_id INT UNSIGNED, # Subnet identification + pref_lifetime INT UNSIGNED, # Preferred lifetime + lease_type TINYINT, # Lease type (see lease6_types + # table for possible values) + iaid INT UNSIGNED, # See Section 12 of RFC 8415 + prefix_len TINYINT UNSIGNED, # For IA_PD only + fqdn_fwd BOOL, # Has forward DNS update been performed by a server + fqdn_rev BOOL, # Has reverse DNS update been performed by a server + hostname VARCHAR(255) # The FQDN of the client + + ) ENGINE = INNODB; + +# Create search indexes for lease4 table. +# index by iaid, subnet_id, and duid +CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid); + +# ... and a definition of lease6 types. This table is a convenience for +# users of the database - if they want to view the lease table and use the +# type names, they can join this table with the lease6 table. +# Make sure those values match Lease6::LeaseType enum (see src/bin/dhcpsrv/ +# lease_mgr.h) +CREATE TABLE lease6_types ( + lease_type TINYINT PRIMARY KEY NOT NULL, # Lease type code. + name VARCHAR(5) # Name of the lease type + ) ENGINE = INNODB; +START TRANSACTION; +INSERT INTO lease6_types VALUES (0, 'IA_NA'); # Non-temporary v6 addresses +INSERT INTO lease6_types VALUES (1, 'IA_TA'); # Temporary v6 addresses +INSERT INTO lease6_types VALUES (2, 'IA_PD'); # Prefix delegations +COMMIT; + +# Finally, the version of the schema. We start at 0.1 during development. +# This table is only modified during schema upgrades. For historical reasons +# (related to the names of the columns in the BIND 10 DNS database file), the +# first column is called 'version' and not 'major'. +# Note: This MUST be kept in step with src/share/database/scripts/mysql/dhcpdb_create.mysql, +# which defines the schema for the unit tests. +CREATE TABLE schema_version ( + version INT PRIMARY KEY NOT NULL, # Major version number + minor INT # Minor version number + ) ENGINE = INNODB; +START TRANSACTION; +INSERT INTO schema_version VALUES (1, 0); +COMMIT; + +# Notes: +# +# Indexes +# ======= +# It is likely that additional indexes will be needed. However, the +# increase in lookup performance from these will come at the expense +# of a decrease in performance during insert operations due to the need +# to update the indexes. For this reason, the need for additional indexes +# will be determined by experiment during performance tests. +# +# The most likely additional indexes will cover the following columns: +# +# expire +# To speed up the deletion of expired leases from the database. +# +# hwaddr and client_id +# For lease stability: if a client requests a new lease, try to find an +# existing or recently expired lease for it so that it can keep using the +# same IP address. +# +# Field Sizes +# =========== +# If any of the VARxxx field sizes are altered, the lengths in the MySQL +# backend source file (mysql_lease_mgr.cc) must be correspondingly changed. +# +# Portability +# =========== +# The "ENGINE = INNODB" on some tables is not portable to another database +# and will need to be removed. +# +# Some columns contain binary data so are stored as VARBINARY instead of +# VARCHAR. This may be non-portable between databases: in this case, the +# definition should be changed to VARCHAR. diff --git a/src/bin/admin/tests/dhcpdb_create_1.0.pgsql b/src/bin/admin/tests/dhcpdb_create_1.0.pgsql new file mode 100644 index 0000000..cbe9ff9 --- /dev/null +++ b/src/bin/admin/tests/dhcpdb_create_1.0.pgsql @@ -0,0 +1,122 @@ +-- Copyright (C) 2012-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/. + +-- This is the Kea DHCP schema specification for PostgreSQL schema 1.0. +-- It is used to create a 1.0 schema database for testing kea-admin's +-- ability to upgrade Postgres databases. + +-- The schema is reasonably portable (with the exception of some field types +-- specification, which are PostgreSQL-specific). Minor changes might be needed +-- for other databases. + +-- To create the schema, either type the command: + +-- psql -U <user> -W <password> <database> < dhcpdb_create.pgsql + +-- ... at the command prompt, or log in to the PostgreSQL database and at the "postgres=#" +-- prompt, issue the command: + +-- @dhcpdb_create.pgsql + + +-- Holds the IPv4 leases. +CREATE TABLE lease4 ( + address BIGINT PRIMARY KEY NOT NULL, -- IPv4 address + hwaddr BYTEA, -- Hardware address + client_id BYTEA, -- Client ID + valid_lifetime BIGINT, -- Length of the lease (seconds) + expire TIMESTAMP WITH TIME ZONE, -- Expiration time of the lease + subnet_id BIGINT, -- Subnet identification + fqdn_fwd BOOLEAN, -- Has forward DNS update been performed by a server + fqdn_rev BOOLEAN, -- Has reverse DNS update been performed by a server + hostname VARCHAR(255) -- The FQDN of the client + ); + + +-- Create search indexes for lease4 table +-- index by hwaddr and subnet_id +CREATE INDEX lease4_by_hwaddr_subnet_id ON lease4 (hwaddr, subnet_id); + +-- index by client_id and subnet_id +CREATE INDEX lease4_by_client_id_subnet_id ON lease4 (client_id, subnet_id); + +-- Holds the IPv6 leases. +-- N.B. The use of a VARCHAR for the address is temporary for development: +-- it will eventually be replaced by BINARY(16). +CREATE TABLE lease6 ( + address VARCHAR(39) PRIMARY KEY NOT NULL, -- IPv6 address + duid BYTEA, -- DUID + valid_lifetime BIGINT, -- Length of the lease (seconds) + expire TIMESTAMP WITH TIME ZONE, -- Expiration time of the lease + subnet_id BIGINT, -- Subnet identification + pref_lifetime BIGINT, -- Preferred lifetime + lease_type SMALLINT, -- Lease type (see lease6_types + -- table for possible values) + iaid INT, -- See Section 12 of RFC 8415 + prefix_len SMALLINT, -- For IA_PD only + fqdn_fwd BOOLEAN, -- Has forward DNS update been performed by a server + fqdn_rev BOOLEAN, -- Has reverse DNS update been performed by a server + hostname VARCHAR(255) -- The FQDN of the client + ); + +-- Create search indexes for lease4 table +-- index by iaid, subnet_id, and duid +CREATE INDEX lease6_by_iaid_subnet_id_duid ON lease6 (iaid, subnet_id, duid); + +-- ... and a definition of lease6 types. This table is a convenience for +-- users of the database - if they want to view the lease table and use the +-- type names, they can join this table with the lease6 table +CREATE TABLE lease6_types ( + lease_type SMALLINT PRIMARY KEY NOT NULL, -- Lease type code. + name VARCHAR(5) -- Name of the lease type + ); +START TRANSACTION; +INSERT INTO lease6_types VALUES (0, 'IA_NA'); -- Non-temporary v6 addresses +INSERT INTO lease6_types VALUES (1, 'IA_TA'); -- Temporary v6 addresses +INSERT INTO lease6_types VALUES (2, 'IA_PD'); -- Prefix delegations +COMMIT; + +-- Finally, the version of the schema. We start at 1.0 during development. +-- This table is only modified during schema upgrades. For historical reasons +-- (related to the names of the columns in the BIND 10 DNS database file), the +-- first column is called "version" and not "major". +-- Note: This MUST be kept in step with src/share/database/scripts/pgsql/dhcpdb_create.pgsql, +-- which defines the schema for the unit tests. +CREATE TABLE schema_version ( + version INT PRIMARY KEY NOT NULL, -- Major version number + minor INT -- Minor version number + ); +START TRANSACTION; +INSERT INTO schema_version VALUES (1, 0); +COMMIT; + +-- Notes: + +-- Indexes +-- ======= +-- It is likely that additional indexes will be needed. However, the +-- increase in lookup performance from these will come at the expense +-- of a decrease in performance during insert operations due to the need +-- to update the indexes. For this reason, the need for additional indexes +-- will be determined by experiment during performance tests. + +-- The most likely additional indexes will cover the following columns: + +-- hwaddr and client_id +-- For lease stability: if a client requests a new lease, try to find an +-- existing or recently expired lease for it so that it can keep using the +-- same IP address. + +-- Field Sizes +-- =========== +-- If any of the VARxxx field sizes are altered, the lengths in the PgSQL +-- backend source file (pgsql_lease_mgr.cc) must be correspondingly changed. + +-- Portability +-- =========== +-- Some columns contain binary data so are stored as BYTEA instead of +-- VARCHAR. This may be non-portable between databases: in this case, the +-- definition should be changed to VARCHAR. diff --git a/src/bin/admin/tests/memfile_tests.sh.in b/src/bin/admin/tests/memfile_tests.sh.in new file mode 100644 index 0000000..9a71b87 --- /dev/null +++ b/src/bin/admin/tests/memfile_tests.sh.in @@ -0,0 +1,239 @@ +#!/bin/sh + +# Copyright (C) 2014-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/. + +# shellcheck disable=SC1091 +# SC1091: Not following: ... was not specified as input (see shellcheck -x). + +# shellcheck disable=SC2039 +# SC2039: In POSIX sh, 'local' is undefined. + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# Include common test library. +. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh" + +# Locations of memfile tools +kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin" +kea_lfc="@abs_top_builddir@/src/bin/lfc/kea-lfc" + +# Clean up any files used in testing. +clean_up() { + remove_if_exists \ + "${config_file-}" \ + "${csv-}" \ + "${csv_2-}" \ + "@abs_top_builddir@/src/bin/admin/tests/kea.log" \ + "@abs_top_builddir@/src/bin/admin/tests/kea.log.lock" +} + +# Print location of CSV file. Accepts 4 or 6 as parameter. +csv_file() { + local v="${1}" + printf '%s' "@abs_top_builddir@/src/bin/admin/tests/kea-dhcp${v}.csv" +} + +# Print location of kea-dhcp[46] binaries. Accepts 4 or 6 as parameter. +kea_dhcp() { + local v="${1}" + printf '%s' "@abs_top_builddir@/src/bin/dhcp${v}/kea-dhcp${v}" +} + +# Print the minimum allowed number of header columns for v4. +incomplete_memfile_header_v4() { + printf 'address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname' +} + +# Print the minimum allowed number of header columns for v6. +incomplete_memfile_header_v6() { + printf 'address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname' +} + +# Print the entire header for v4. +memfile_header_v4() { + printf '%s,state,user_context,pool_id' "$(incomplete_memfile_header_v4)" +} + +# Print the entire header for v6. +memfile_header_v6() { + printf '%s,hwaddr,state,user_context,hwtype,hwaddr_source,pool_id' "$(incomplete_memfile_header_v6)" +} + +# Print data copied from mysql_upgrade_12_to_13_test and pgsql_upgrade_7_0_to_8_0. +# @{ +memfile_data_v4() { + printf '0.0.0.10,32:30,33:30,40,1678900000,50,1,1,oneˎxampleˌom,0,{"a":1,"b":2},0' +} +memfile_data_v6() { + printf '::10,32:30:33,30,1678900000,40,50,1,60,70,1,1,oneˎxampleˌom,38:30,0,{"a":1,"b":2},90,16,0' +} +# @} + +# Print "server-id" configuration. Not available for v4. +server_id_v4() { + : +} + +# Print "server-id" configuration. Not available for v4. +server_id_v6() { + printf ', + "server-id": { + "persist": false, + "type": "EN" + } +' +} + +# Starts Kea and sets PID. It logs to stdout and stderr if DEBUG is enabled. +# Accepts 4 or 6 as parameter. +start_kea_dhcp() { + local v="${1}" + "$(kea_dhcp "${v}")" -c "${config_file}" >> "@abs_top_builddir@/src/bin/admin/tests/kea.log" 2>&1 & + PID=${!} + sleep 1 +} + +# Test that Kea creates a correctly populated CSV file if configured with +# persisting memfile. +memfile_init_test() { + test_start 'memfile.init' + + for v in 4 6; do + for i in no-file some-data; do + if test -n "${DEBUG+x}"; then + printf 'TRACE %s, %s\n' "${v}" "${i}" + fi + config=$(printf '%s%s%s' ' +{ + "Dhcpx": { + "lease-database": { + "name": "@abs_top_builddir@/src/bin/admin/tests/kea-dhcpx.csv", + "persist": true, + "type": "memfile" + }, + "loggers": [ + { + "debuglevel": 99, + "name": "kea-dhcpx", + "output_options": [ + { + "output": "@abs_top_builddir@/src/bin/admin/tests/kea.log" + } + ], + "severity": "DEBUG" + } + ]' \ + "$(server_id_v${v})" \ + ' + } +} +') + config_file="@abs_top_builddir@/src/bin/admin/tests/kea-dhcp${v}.conf" + csv=$(csv_file "${v}") + + if test "${i}" = 'some-data'; then + # Test that Kea accepts the output of lease-dump in memfile. + { + "memfile_header_v${v}" + printf '\n' + "memfile_data_v${v}" + printf '\n' + } > "${csv}" + else + # Make sure there is no CSV as the test requires. + rm -f "${csv}" + fi + + # Set DHCP version in config. + printf '%s\n' "${config}" | \ + sed "s#Dhcpx#Dhcp${v}#g; + s#dhcpx#dhcp${v}#g" \ + > "${config_file}" + + start_kea_dhcp "${v}" + # This assumes that the CSV creation + writing to CSV is atomic. Not + # sure if it is, but if this ever fails on the comparison further below, + # consider waiting here for line DHCPSRV_MEMFILE_LFC_SETUP in logs, even + # though it doesn't clearly signal end of CSV writing. + if ! wait_for_file "${csv}"; then + clean_up + clean_exit 1 + fi + kill "${PID}" || true # process may have exited early due to errors + if ! wait_for_process_to_stop "${PID}"; then + clean_up + clean_exit 2 + fi + content=$(head -n 1 "${csv}") + expected=$(memfile_header_v${v}) + if test "${content}" != "${expected}"; then + printf 'ERROR: %s does not contain expected header.\n< %s\n> %s\n' \ + "${csv}" "${content}" "${expected}" >&2 + clean_up + clean_exit 3 + fi + + if grep -Fi ERROR "@abs_top_builddir@/src/bin/admin/tests/kea.log"; then + printf 'ERROR: loading memfile failed. See error above.\n' >&2 + cat "${csv}" + clean_up + clean_exit 4 + fi + + clean_up + done + done + + test_finish 0 +} + +# Test that kea-lfc is able to upgrade a CSV file with incomplete header. +memfile_upgrade_test() { + test_start 'memfile.upgrade' + + for v in 4 6; do + csv=$(csv_file "${v}") + "incomplete_memfile_header_v${v}" > "${csv}" + printf '\n' >> "${csv}" + csv_2="${csv}.2" + + "${kea_lfc}" "-${v}" \ + -c 'ignored-path' \ + -f "${csv}.completed" \ + -i "${csv}" \ + -o "${csv}.output" \ + -p "${csv}.pid" \ + -x "${csv_2}" + + content=$(cat "${csv_2}") + expected=$(memfile_header_v${v}) + if test "${content}" != "${expected}"; then + printf 'ERROR: %s does not contain expected header.\n< %s\n> %s\n' \ + "${csv}" "${content}" "${expected}" >&2 + clean_up + clean_exit 1 + fi + + clean_up + done + + test_finish 0 +} + +# shellcheck disable=SC2034 +# SC2034: ... appears unused. Verify use (or export if used externally). +# reason: bin and bin_path are used in version_test +{ +bin=$(basename "${kea_admin}") +bin_path=$(dirname "${kea_admin}") +version_test 'memfile.version' 'long_version_too_please' +} + +memfile_init_test +memfile_upgrade_test diff --git a/src/bin/admin/tests/mysql_tests.sh.in b/src/bin/admin/tests/mysql_tests.sh.in new file mode 100644 index 0000000..889ce52 --- /dev/null +++ b/src/bin/admin/tests/mysql_tests.sh.in @@ -0,0 +1,2892 @@ +#!/bin/sh + +# Copyright (C) 2014-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/. + +# shellcheck disable=SC1091 +# SC1091: Not following: ... was not specified as input (see shellcheck -x). + +# shellcheck disable=SC2154 +# SC2154: ... is referenced but not assigned. +# Reason: some variables are sourced. + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# Include common test library. +. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh" + +# Include admin utilities +. "@abs_top_builddir@/src/bin/admin/admin-utils.sh" + +# Set path to the production schema scripts +db_scripts_dir="@abs_top_srcdir@/src/share/database/scripts" + +# Set location of the kea-admin. +kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin" + +# Convenience function for running an SQL statement +# param hdr - text message to prepend to any error +# param qry - SQL statement to run +# param exp_value - optional expected value. This can be used IF the SQL statement +# generates a single value, such as a SELECT which returns one column for one row. +# Examples: +# +# qry="insert into lease6 (address, lease_type, subnet_id, state) values ($addr,$ltype,1,0)" +# run_statement "#2" "$qry" +# +# qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" +# run_statement "#3" "$qry" 1 +run_statement() { + hdr="$1";shift + qry="$1";shift + exp_value="${1-}" # Optional value. If not given, replace with empty string. + + # Execute the statement + run_command \ + mysql_execute "${qry}" + # shellcheck disable=SC2153 + # SC2153: Possible misspelling: ... may not be assigned, but ... is. + # Reason for disable: OUTPUT is assigned in run_command. + value="${OUTPUT}" + + # Execution should succeed + assert_eq 0 "${EXIT_CODE}" "$hdr: SQL=[$qry] failed: (expected status code %d, returned %d)" + + # If there's an expected value, test it + if [ "x$exp_value" != "x" ] + then + assert_str_eq "$exp_value" "$value" "$hdr: SQL=[$qry] wrong: (expected value %s, returned %s)" + fi +} + +# Wipe all tables from the DB: +mysql_wipe() { + printf "Wiping whole database %s...\n" "${db_name}" + + run_command \ + mysql_execute_script "${db_scripts_dir}/mysql/dhcpdb_drop.mysql" + assert_eq 0 "${EXIT_CODE}" "mysql-wipe: drop table sql failed, expected %d, returned %d" +} + +mysql_db_init_test() { + test_start "mysql.db-init" + + # Let's wipe the whole database + mysql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init mysql failed, expected %d, returned non-zero status code %d" + + # Ok, now let's check if the tables are indeed there. + # First table: schema_version. Should have 2 columns: version and minor. + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT version, minor FROM schema_version' + assert_eq 0 "${EXIT_CODE}" "schema_version table is missing or broken. (expected status code %d, returned %d)" + + # Second table: lease4 + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT address, hwaddr, client_id, valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname FROM lease4' + assert_eq 0 "${EXIT_CODE}" "lease4 table is missing or broken. (expected status code %d, returned %d)" + + # Third table: lease6 + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT address, duid, valid_lifetime, expire, subnet_id, pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, hwaddr, hwtype, hwaddr_source FROM lease6' + assert_eq 0 "${EXIT_CODE}" "lease6 table is missing or broken. (expected status code %d, returned %d)" + + # Fourth table: lease6_types + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT lease_type, name FROM lease6_types' + assert_eq 0 "${EXIT_CODE}" "lease6_types table is missing or broken. (expected status code %d, returned %d)" + + # Fifth table: lease_hwaddr_source + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT hwaddr_source, name FROM lease_hwaddr_source' + assert_eq 0 "${EXIT_CODE}" "lease_hwaddr_source table is missing or broken. (expected status code %d, returned %d)" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +mysql_db_version_test() { + test_start "mysql.db-version" + + # Let's wipe the whole database + mysql_wipe + + # Do not create any table so db-version will raise an error + printf 'Checking db-version error case...\n' + run_command \ + "${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" + assert_eq 1 "${EXIT_CODE}" "schema_version table still exists. (expected %d, exit code %d)" + + # Ok, now let's create a version 1.7 + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ +'CREATE TABLE schema_version ( + version INT PRIMARY KEY NOT NULL, + minor INT +); +INSERT INTO schema_version VALUES (1, 7)' + assert_eq 0 "${EXIT_CODE}" "schema_version table cannot be created. (expected %d, exit code %d)" + + run_command \ + "${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" + version="${OUTPUT}" + assert_str_eq "1.7" "${version}" "Expected kea-admin to return %s, returned value was %s" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +mysql_db_version_with_extra_test() { + test_start "mysql.db-version_with_extra" + + # Let's wipe the whole database + mysql_wipe + + # Do not create any table so db-version will raise an error + printf 'Checking db-version error case...\n' + run_command \ + "${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" + assert_eq 1 "${EXIT_CODE}" "schema_version table still exists. (expected %d, exit code %d)" + + # Ok, now let's create a version 1.7 + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ +'CREATE TABLE schema_version ( + version INT PRIMARY KEY NOT NULL, + minor INT +); +INSERT INTO schema_version VALUES (1, 7)' + assert_eq 0 "${EXIT_CODE}" "schema_version table cannot be created. (expected %d, exit code %d)" + + # Single -x. + run_command \ + "${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -x --protocol=TCP + version="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "kea-admin -x failed. (expected %d, exit code %d)" + assert_str_eq "1.7" "${version}" "Expected kea-admin to return %s, returned value was %s" + + # Multiple -x. + run_command \ + "${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" \ + -x --protocol=TCP -x --hello 2> "@abs_top_builddir@/src/bin/admin/test-data" + assert_eq 2 "${EXIT_CODE}" "kea-admin -x -x succeeded. (expected %d, exit code %d)" + if ! grep -F "unknown option '--hello'" "@abs_top_builddir@/src/bin/admin/test-data"; then + printf 'second parameter --hello was not passed to mysql with -x\n' + test_finish 1 + fi + rm -f "@abs_top_builddir@/src/bin/admin/test-data" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +mysql_host_reservation_init_test() { + test_start "mysql.host_reservation-init" + + # Let's wipe the whole database + mysql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init mysql failed, expected %d, returned non-zero status code %d" + + # Ok, now let's check if the tables are indeed there. + # First table: schema_version. Should have 2 columns: version and minor. + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT version, minor FROM schema_version' + assert_eq 0 "${EXIT_CODE}" "schema_version table is missing or broken. (expected status code %d, returned %d)" + + # Second table: hosts + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT host_id, dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, dhcp4_client_classes, dhcp6_client_classes, dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key FROM hosts' + assert_eq 0 "${EXIT_CODE}" "hosts table is missing or broken. (expected status code %d, returned %d)" + + # Third table: ipv6_reservations + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT reservation_id, address, prefix_len, type, dhcp6_iaid, host_id FROM ipv6_reservations' + assert_eq 0 "${EXIT_CODE}" "ipv6_reservations table is missing or broken. (expected status code %d, returned %d)" + + # Fourth table: dhcp4_options + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id FROM dhcp4_options' + assert_eq 0 "${EXIT_CODE}" "dhcp4_options table is missing or broken. (expected status code %d, returned %d)" + + # Fifth table: dhcp6_options + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp6_subnet_id, host_id, scope_id FROM dhcp6_options' + assert_eq 0 "${EXIT_CODE}" "dhcp6_options table is missing or broken. (expected status code %d, returned %d)" + + # Sixth table: host_identifier_type + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT type, name FROM host_identifier_type' + assert_eq 0 "${EXIT_CODE}" "host_identifier_type table is missing or broken. (expected status code %d, returned %d)" + + # Seventh table: dhcp_option_scope + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT scope_id, scope_name FROM dhcp_option_scope' + assert_eq 0 "${EXIT_CODE}" "dhcp_option_scope table is missing or broken. (expected status code %d, returned %d)" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Upgrades an existing schema to a target newer version +# param target_version - desired schema version as "major.minor" +mysql_upgrade_schema_to_version() { + target_version=$1 + + upgrade_scripts_dir=${db_scripts_dir}/mysql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find ${upgrade_scripts_dir} -name 'upgrade*.sh' -type f | wc -l) + if [ "${num_files}" -eq 0 ]; then + upgrade_scripts_dir=@abs_top_builddir@/src/share/database/scripts/mysql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find "${upgrade_scripts_dir}" -name 'upgrade*.sh' -type f | wc -l) + fi + + if [ "${num_files}" -eq 0 ]; then + log_error "No scripts in ${upgrade_scripts_dir}?" + exit 1 + fi + + for script in "${upgrade_scripts_dir}"/upgrade*.sh + do + version=$(mysql_version) + if [ "${version}" = "${target_version}" ] + then + break + fi + + echo "Processing $script file..." + "${script}" --user="${db_user}" --password="${db_password}" "${db_name}" + done + + echo "Schema upgraded to $version" +} + +mysql_upgrade_12_to_13_test() { + # Check the output of colonSeparatedHex(). + run_command \ + mysql_execute 'SELECT colonSeparatedHex(HEX(0xF123456789))' + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq 'f1:23:45:67:89' "${OUTPUT}" + + run_command \ + mysql_execute 'SELECT colonSeparatedHex("")' + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + run_command \ + mysql_execute 'SELECT colonSeparatedHex(HEX(0xF))' + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq '0f' "${OUTPUT}" + + run_command \ + mysql_execute 'SELECT colonSeparatedHex(HEX(0xF1))' + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq 'f1' "${OUTPUT}" + + run_command \ + mysql_execute 'SELECT colonSeparatedHex(HEX(0xF12))' + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq '0f:12' "${OUTPUT}" + + run_command \ + mysql_execute 'SELECT colonSeparatedHex(HEX(458753))' + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq '07:00:01' "${OUTPUT}" + + # Check lease4Dump*(). + run_command \ + mysql_execute "INSERT INTO lease4 VALUES(10,20,30,40,(SELECT FROM_UNIXTIME(1678900000)),50,1,1,'one,example,com',0,'{ \"a\": 1, \"b\": 2 }',NULL,NULL,0)" + assert_eq 0 "${EXIT_CODE}" 'INSERT INTO lease4 failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + run_command \ + mysql_execute "CALL lease4DumpHeader()" + assert_eq 0 "${EXIT_CODE}" 'lease4DumpHeader() failed, expected exit code %d, actual %d' + assert_str_eq 'address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id' "${OUTPUT}" + + run_command \ + mysql_execute "CALL lease4DumpData()" + assert_eq 0 "${EXIT_CODE}" 'lease4DumpData() failed, expected exit code %d, actual %d' + output=$(printf '%s' "${OUTPUT}" | sed 's/\t/,/g') # turn tabs into commas + assert_str_eq '0.0.0.10,32:30,33:30,40,1678900000,50,1,1,oneˎxampleˌom,0,{ "a": 1, "b": 2 },0' "${output}" + + # Check lease6Dump*(). + run_command \ + mysql_execute "INSERT INTO lease6 VALUES(inet6_aton('::10'),20,30,(SELECT FROM_UNIXTIME(1678900000)),40,50,1,60,70,1,1,'one,example,com',80,90,16,0,'{ \"a\": 1, \"b\": 2 }',0)" + assert_eq 0 "${EXIT_CODE}" 'INSERT INTO lease6 failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + run_command \ + mysql_execute "CALL lease6DumpHeader()" + assert_eq 0 "${EXIT_CODE}" 'lease6DumpHeader() failed, expected exit code %d, actual %d' + assert_str_eq 'address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,state,user_context,hwtype,hwaddr_source,pool_id' "${OUTPUT}" + + run_command \ + mysql_execute "CALL lease6DumpData()" + assert_eq 0 "${EXIT_CODE}" 'lease6DumpData() failed, expected exit code %d, actual %d' + output=$(printf '%s' "${OUTPUT}" | sed 's/\t/,/g') # turn tabs into commas + assert_str_eq '::10,32:30,30,1678900000,40,50,1,60,70,1,1,oneˎxampleˌom,38:30,0,{ "a": 1, "b": 2 },90,16,0' "${output}" + + # Check lease4Upload(). + run_command \ + mysql_execute "CALL lease4Upload('192.0.0.0','ff0102030405','01ff0102030405',7200,1234567890,1,0,0,'',0,'',0)" + assert_eq 0 "${EXIT_CODE}" 'lease4Upload() failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + # Check lease6Upload(). + run_command \ + mysql_execute "CALL lease6Upload('2001:db8::','000100012955cb80ff0102030407',7200,1234567890,1,3600,0,1,128,0,0,'','ff0102030407',0,'',90,16,0)" + assert_eq 0 "${EXIT_CODE}" 'lease6Upload() failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" +} + +mysql_upgrade_13_to_14_test() { + # Check function source code + run_command \ + mysql_execute "select action_statement from information_schema.TRIGGERS where trigger_schema = '${db_name}' and trigger_name = 'dhcp4_shared_network_BDEL'" + assert_eq 0 "${EXIT_CODE}" "function func_dhcp4_shared_network_BDEL() broken or missing. (expected status code %d, returned %d)" + + count=$(echo "${OUTPUT}" | grep -Eci 'UPDATE dhcp4_subnet SET shared_network_name = NULL') || true + assert_eq 1 "${count}" "function func_dhcp4_shared_network_BDEL() is missing changed line. (expected count %d, returned %d)" + + # Check function source code + run_command \ + mysql_execute "select action_statement from information_schema.TRIGGERS where trigger_schema = '${db_name}' and trigger_name = 'dhcp6_shared_network_BDEL'" + assert_eq 0 "${EXIT_CODE}" "function func_dhcp6_shared_network_BDEL() broken or missing. (expected status code %d, returned %d)" + + count=$(echo "${OUTPUT}" | grep -Eci 'UPDATE dhcp6_subnet SET shared_network_name = NULL') || true + assert_eq 1 "${count}" "function func_dhcp6_shared_network_BDEL() is missing changed line. (expected count %d, returned %d)" + + # user_context should have been added to dhcp4_client_class + qry="select user_context from dhcp4_client_class limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # user_context should have been added to dhcp6_client_class + qry="select user_context from dhcp6_client_class limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # -- lease counting tests -- + + # Check that @json_supported is NULL by default. + query="SELECT @json_supported" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "NULL" "${OUTPUT}" "${query}: expected output %s, returned %s" + + # Clean up. + query="DELETE FROM lease4; DELETE FROM lease6" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "" "${OUTPUT}" "${query}: expected output %s, returned %s" + + # Populate the lease tables. Also check that @json_supported is NULL at + # first, and then it is set after inserting leases. + run_command \ + mysql_execute " + SELECT @json_supported; + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (100,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + SELECT @json_supported; + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (101,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (102,1,2, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (103,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (104,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (105,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (100,0,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (101,0,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (102,0,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (103,0,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (104,2,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (105,2,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (106,2,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (107,2,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + SELECT @json_supported; + " + assert_eq 0 "${EXIT_CODE}" 'INSERT INTO leases when upgrading from 13 to 14 failed. expected %d, returned %d' + one_line=$(printf '%s' "${OUTPUT}" | tr '\n' ' ') + json_supported=$(printf '%s' "${one_line}" | grep -Eo '[0-1]$') || true + if test "${json_supported}" != 0 && test "${json_supported}" != 1; then + assert_str_eq '[01]' "${json_supported}" "INSERT INTO leases when upgrading from 13 to 14 does not set @json_supported. expected '[01]', returned '${json_supported}'" + fi + if ! printf '%s' "${one_line}" | grep -E "NULL ${json_supported} ${json_supported}" > /dev/null; then + assert_str_eq 'NULL [01] [01]' "${one_line}" "INSERT INTO leases when upgrading from 13 to 14 does not set @json_supported. expected 'NULL [01] [01]', returned '${one_line}'" + fi + + for v in 4 6; do + # Check that client classes were counted correctly. + query="SELECT leases FROM lease${v}_stat_by_client_class WHERE client_class = 'foo' LIMIT 1" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + if test "${json_supported}" = 1; then + assert_str_eq 2 "${OUTPUT}" "${query}: expected output %s, returned %s" + else + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + # -- Verify some calls to checkLeaseXLimits(). -- + + query="SELECT checkLease${v}Limits('')" + run_command \ + mysql_execute "${query}" + # Should fail with ERROR 4037 (HY000): Unexpected end of JSON text in argument 1 to function 'json_extract' + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{}')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 1 } ] } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 1 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"subnet\": { \"id\": 1, \"address-limit\": 1 } } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 1 for subnet ID 1, current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 2 } ] } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"subnet\": { \"id\": 1, \"address-limit\": 2 } } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for subnet ID 1, current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 4 } ] } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"subnet\": { \"id\": 1, \"address-limit\": 4 } } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 1 }, { \"name\": \"bar\", \"address-limit\": 1 } ], \"subnet\": { \"id\": 1, \"address-limit\": 1 } } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 1 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 2 }, { \"name\": \"bar\", \"address-limit\": 4 } ], \"subnet\": { \"id\": 1, \"address-limit\": 4 } } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 4 }, { \"name\": \"bar\", \"address-limit\": 4 } ], \"subnet\": { \"id\": 1, \"address-limit\": 2 } } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for subnet ID 1, current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 4 }, { \"name\": \"bar\", \"address-limit\": 4 } ], \"subnet\": { \"id\": 1, \"address-limit\": 4 } } } }')" + run_command \ + mysql_execute "${query}" + if test "${json_supported}" = 1; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR 1305 (42000) at line 1: FUNCTION keatest.JSON_EXTRACT does not exist + assert_eq 1 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + done + + # Check that leases counters cannot go negative. + for v in 4 6; do + query="SELECT leases FROM lease${v}_stat WHERE subnet_id = 1 AND state = 0 LIMIT 1" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '2' "${OUTPUT}" "${query}: expected output %s, returned %s" + + # Artificially change the subnet counter from 2 down to 1. + query="UPDATE lease${v}_stat SET leases = 1 WHERE subnet_id = 1 AND state = 0" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + if test "${json_supported}" = 1; then + query="SELECT leases FROM lease${v}_stat_by_client_class WHERE client_class = 'foo' LIMIT 1" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '2' "${OUTPUT}" "${query}: expected output %s, returned %s" + + # Artificially change the client class counter from 2 down to 1. + query="UPDATE lease${v}_stat_by_client_class SET leases = 1 WHERE client_class = 'foo'" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + # Clean up. + query="DELETE FROM lease${v}" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + # SELECT should finish successfully and the subnet counter should be 0. + query="SELECT leases FROM lease${v}_stat WHERE subnet_id = 1 AND state = 0 LIMIT 1" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '0' "${OUTPUT}" "${query}: expected output %s, returned %s" + + if test "${json_supported}" = 1; then + # SELECT should finish successfully and the client class counter should be 0. + query="SELECT leases FROM lease${v}_stat_by_client_class WHERE client_class = 'foo' LIMIT 1" + run_command \ + mysql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '0' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + done +} + +mysql_upgrade_14_to_15_test() { + # table: dhcp4_options new cancelled column. + qry="select cancelled from dhcp4_options" + run_statement "dhcp4_options" "$qry" + + # table: dhcp6_options new cancelled column. + qry="select cancelled from dhcp6_options" + run_statement "dhcp6_options" "$qry" + + # Check if offer_lifetime was added to dhcp4_shared_network table. + qry="SELECT offer_lifetime from dhcp4_shared_network limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if offer_lifetime was added to dhcp4_subnet table. + qry="SELECT offer_lifetime from dhcp4_subnet limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if offer_lifetime was added to dhcp4_client_class table. + qry="SELECT offer_lifetime from dhcp4_client_class limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" +} + +mysql_upgrade_16_to_17_test() { + # Check if allocator was added to dhcp4_shared_network table. + qry="SELECT allocator from dhcp4_shared_network limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if allocator was added to dhcp6_shared_network table. + qry="SELECT allocator from dhcp6_shared_network limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if pd_allocator was added to dhcp6_shared_network table. + qry="SELECT pd_allocator from dhcp6_shared_network limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if allocator was added to dhcp4_subnet table. + qry="SELECT allocator from dhcp4_subnet limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if allocator was added to dhcp6_subnet table. + qry="SELECT allocator from dhcp6_subnet limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if pd_allocator was added to dhcp6_subnet table. + qry="SELECT pd_allocator from dhcp6_subnet limit 1" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" +} + +mysql_upgrade_17_to_18_test() { + # lease4 client_id should support 255 long strings. + qry="insert into lease4 (address, client_id, subnet_id) values (1, '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345', 1)" + run_statement "lease4_255_long_client_id" "$qry" + + # lease4 relay_id should support 255 long strings. + qry="insert into lease4 (address, remote_id, subnet_id) values (2, '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345', 1)" + run_statement "lease4_255_long_relay_id" "$qry" + + # lease4 remote_id should support 255 long strings. + qry="insert into lease4 (address, remote_id, subnet_id) values (3, '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345', 1)" + run_statement "lease4_255_long_remote_id" "$qry" + + # hosts dhcp_identifier should support 255 long strings. + qry="insert into hosts (dhcp_identifier, dhcp_identifier_type) values ('123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345', 0)" + run_statement "hosts_255_long_dhcp_identifier" "$qry" + + #lease6 duid should support 130 long strings. + qry="insert into lease6 values(inet6_aton('::10'),12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890,30,(SELECT FROM_UNIXTIME(1642000000)),40,50,1,60,70,1,1,'one.example.com',80,90,16,0,NULL,0)" + run_statement "lease6_130_long_duid" "$qry" + + #lease4_pool_stat new table. + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT subnet_id, pool_id, state, leases FROM lease4_pool_stat' + assert_eq 0 "${EXIT_CODE}" "lease4_pool_stat table is missing or broken. (expected status code %d, returned %d)" + + #lease6_pool_stat new table. + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT subnet_id, pool_id, lease_type, state, leases FROM lease6_pool_stat' + assert_eq 0 "${EXIT_CODE}" "lease6_pool_stat table is missing or broken. (expected status code %d, returned %d)" + + #lease6_relay_id new table. + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT extended_info_id, relay_id, lease_addr FROM lease6_relay_id' + assert_eq 0 "${EXIT_CODE}" "lease6_relay_id table is missing or broken. (expected status code %d, returned %d)" + + #lease6_remote_id new table. + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT extended_info_id, remote_id, lease_addr FROM lease6_remote_id' + assert_eq 0 "${EXIT_CODE}" "lease6_remote_id table is missing or broken. (expected status code %d, returned %d)" +} + +mysql_upgrade_18_to_19_test() { + # Verify that lease6 address is binary. This is sort of overkill as many of + # the prior upgrade tests manipulate lease6 records. + qry="insert into lease6 values(inet6_aton('3001::99'),'18219',30,(SELECT FROM_UNIXTIME(1642000000)),40,50,1,60,70,1,1,'one.example.com',80,90,16,0,NULL,0)" + run_statement "lease6_insert" "$qry" + + qry="select inet6_ntoa(address) from lease6 where duid = '18219';" + run_statement "lease6_insert" "$qry" "3001::99" + + # Verify that ipv6_reservations address is binary. + qry="\ + insert into hosts(host_id, dhcp_identifier, dhcp_identifier_type) values (18219, '18219', 1); \ + insert into ipv6_reservations (address, prefix_len, type, dhcp6_iaid, host_id) \ + values (inet6_aton('3001::99'), 128, 1, 123, 18219); \ + select inet6_ntoa(address) from ipv6_reservations where host_id = 18219;" + + run_statement "ipv6_reservations_insert" "$qry" "3001::99" +} + +mysql_upgrade_test() { + + test_start "mysql.upgrade" + + # Let's wipe the whole database + mysql_wipe + + # Initialize database to schema 1.0. + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + + # Sanity check - verify that it reports version 1.0. + version=$("${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}") + assert_str_eq "1.0" "${version}" "Expected kea-admin to return %s, returned value was %s" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-upgrade mysql failed, expected %d, returned non-zero status code %d\n" + + # Verify that the upgraded schema reports the latest version. + version=$("${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}") + assert_str_eq "19.0" "${version}" "Expected kea-admin to return %s, returned value was %s" + + # Let's check that the new tables are indeed there. + + #table: lease6 (upgrade 1.0 -> 2.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT hwaddr, hwtype, hwaddr_source FROM lease6' + assert_eq 0 "${EXIT_CODE}" "lease6 table not upgraded to 2.0 (expected status code %d, returned %d)" + + #table: lease_hwaddr_source (upgrade 1.0 -> 2.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT hwaddr_source, name FROM lease_hwaddr_source' + assert_eq 0 "${EXIT_CODE}" "lease_hwaddr_source table is missing or broken. (expected status code %d, returned %d)" + + #table: hosts (upgrade 2.0 -> 3.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT host_id, dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, dhcp4_client_classes, dhcp6_client_classes FROM hosts' + assert_eq 0 "${EXIT_CODE}" "hosts table is missing or broken. (expected status code %d, returned %d)" + + #table: ipv6_reservations (upgrade 2.0 -> 3.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT reservation_id, address, prefix_len, type, dhcp6_iaid, host_id FROM ipv6_reservations' + assert_eq 0 "${EXIT_CODE}" "ipv6_reservations table is missing or broken. (expected status code %d, returned %d)" + + #table: dhcp4_options (upgrade 2.0 -> 3.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp4_subnet_id, host_id FROM dhcp4_options' + assert_eq 0 "${EXIT_CODE}" "dhcp4_options table is missing or broken. (expected status code %d, returned %d)" + + #table: dhcp6_options (upgrade 2.0 -> 3.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp6_subnet_id, host_id FROM dhcp6_options' + assert_eq 0 "${EXIT_CODE}" "dhcp6_options table is missing or broken. (expected status code %d, returned %d)" + + #table: lease_state table added (upgrade 3.0 -> 4.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT state,name from lease_state' + assert_eq 0 "${EXIT_CODE}" "dhcp6_options table is missing or broken. (expected status code %d, returned %d)" + + #table: state column added to lease4 (upgrade 3.0 -> 4.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT state from lease4' + assert_eq 0 "${EXIT_CODE}" "lease4 is missing state column. (expected status code %d, returned %d)" + + #table: state column added to lease6 (upgrade 3.0 -> 4.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT state from lease6' + assert_eq 0 "${EXIT_CODE}" "lease6 is missing state column. (expected status code %d, returned %d)" + + #table: stored procedures for lease dumps added (upgrade 3.0 -> 4.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'call lease4DumpHeader(); call lease4DumpData(); call lease6DumpHeader(); call lease6DumpHeader()' + assert_eq 0 "${EXIT_CODE}" "lease dump stored procedures are missing or broken. (expected status code %d, returned %d)" + + #lease_hardware_source should have row for source = 0 (upgrade 4.0 -> 4.1) + qry="select count(hwaddr_source) from lease_hwaddr_source where hwaddr_source = 0 and name='HWADDR_SOURCE_UNKNOWN'" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select from lease_hwaddr_source failed. (expected status code %d, returned %d)" + assert_eq 1 "${count}" "lease_hwaddr_source does not contain entry for HWADDR_SOURCE_UNKNOWN. (record count %d, expected %d)" + + # table: stored procedures for lease data dumps were modified (upgrade 4.0 -> 4.1) + # verify lease4DumpData has order by lease address + qry="show create procedure lease4DumpData" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "procedure text fetch for lease4DumpData failed. (returned status code %d, expected %d)" + count=$(echo "${OUTPUT}" | grep -Eci 'order by [a-z]*[\.]?address') || true + assert_eq 1 "${count}" "lease4DumpData is missing order by clause. (expected count %d, returned %d)" + + # verify lease6DumpData has order by lease address + qry="show create procedure lease6DumpData" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "procedure text fetch for lease6DumpData failed. (returned status code %d, expected %d)" + count=$(echo "${OUTPUT}" | grep -Eci 'order by [a-z]*[\.]?address') || true + assert_eq 1 "${count}" "lease6DumpData doesn't have order by clause. (returned count %d, expected %d)" + + #table: host_identifier_type (upgrade 4.1 -> 5.0) + # verify that host_identifier_type table exists. + qry="select count(*) from host_identifier_type" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select from host_identifier_type failed. (expected status code %d, returned %d)" + assert_eq 5 "${count}" "host_identifier_type does not contain correct number of entries. (expected count %d, returned %d)" + + # verify that foreign key fk_host_identifier_type exists + qry="show create table hosts" + run_command \ + mysql_execute "${qry}" + count=$(echo "${OUTPUT}" | grep -Fci -m 1 'fk_host_identifier_type') || true + assert_eq 0 "${EXIT_CODE}" "show create table hosts failed. (expected status code %d, returned %d)" + assert_eq 1 "${count}" "show create table hosts did not return correct number of fk_host_identifier_type instances. (expected %d, returned %d)" + + #table: dhcp_option_scope (upgrade 4.1 -> 5.0) + # verify that dhcp_option_scope table exists. + qry="select count(*) from dhcp_option_scope" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select from dhcp_option_scope failed. (expected status code %d, returned %d)" + # verify that dhcp_option_scope table contains correct number of entries. + assert_eq 7 "${count}" "dhcp_option_scope does not contain correct number of entries. (expected %d, returned %d)" + + #table: scope_id columns to dhcp4_options (upgrade 4.1 -> 5.0) + # verify that dhcp4_options table includes scope_id + qry="select scope_id from dhcp4_options" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select scope_id from dhcp4_options failed. (expected status code %d, returned %d)" + + #table: scope_id columns to dhcp6_options (upgrade 4.1 -> 5.0) + # verify that dhcp6_options table includes scope_id + qry="select scope_id from dhcp6_options" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select scope_id from dhcp6_options failed. (expected status code %d, returned %d)" + + #table: DHCPv4 fixed field columns (upgrade 4.1 -> 5.0) + # verify that hosts table has columns holding values for DHCPv4 fixed fields + qry="select dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key from hosts" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key failed. (expected status code %d, returned %d)" + + # verify that dhcp4_subnet_id is unsigned + qry="show columns from hosts like 'dhcp4_subnet_id'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show columns from hosts like 'dhcp4_subnet_id' failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci unsigned) || true + assert_eq 1 "${count}" "dhcp4_subnet_id is not of unsigned type. (returned count %d, expected %d)" + + # verify that dhcp6_subnet_id is unsigned + qry="show columns from hosts like 'dhcp6_subnet_id'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show columns from hosts like 'dhcp6_subnet_id' failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci unsigned) || true + assert_eq 1 "${count}" "dhcp6_subnet_id is not of unsigned type. (expected count %d, returned %d)" + + #host_identifier_type should have rows for types 3 and 4 (upgrade 5.0 -> 5.1) + qry="select count(*) from host_identifier_type" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select from host_identifier_type failed. (expected status code %d, returned %d)" + assert_eq 5 "${count}" "host_identifier_type does not contain correct number of entries. (expected count %d, returned %d)" + + #table: user_context columns to hosts, dhcp4_options and dhcp6_options (upgrade 5.2 -> 6.0) + # verify that hosts table includes user_context + qry="select user_context from hosts" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select user_context from hosts failed. (expected status code %d, returned %d)" + + # verify that dhcp4_options table includes user_context + qry="select user_context from dhcp4_options" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select user_context from dhcp4_options failed. (expected status code %d, returned %d)" + + # verify that dhcp6_options table includes user_context + qry="select user_context from dhcp6_options" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select user_context from dhcp6_options failed. (expected status code %d, returned %d)" + + # lease4/6_stats changes are tested separately + + #table: user_context to lease4 and lease6 (upgrade 6.0 -> 7.0) + # verify that lease4 table includes user_context + qry="select user_context from lease4" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select user_context from lease4 failed. (expected status code %d, returned %d)" + + # verify that lease6 table includes user_context + qry="select user_context from lease6" + run_command \ + mysql_execute "${qry}" + count="${OUTPUT}" + assert_eq 0 "${EXIT_CODE}" "select user_context from lease6 failed. (expected status code %d, returned %d)" + + #table: logs (upgrade 6.0 -> 7.0) + run_command \ + mysql -u"${db_user}" -p"${db_password}" "${db_name}" -e \ + 'SELECT timestamp, address, log FROM logs' + assert_eq 0 "${EXIT_CODE}" "logs table is missing or broken. (expected status code %d, returned %d)" + + # table: modification (upgrade 6.0 -> 7.0) + qry="select id, modification_type from modification" + run_statement "modification" "$qry" + + # table: modification table should have 3 entries (upgrade 6.0 -> 7.0) + qry="select count(*) from modification" + run_statement "modification count" "$qry" 3 + + # table: dhcp4_server + qry="select id, tag, description, modification_ts from dhcp4_server" + run_statement "dhcp4_server" "$qry" + + # table: dhcp4_server - check if it contains default entry + qry="select count(*) from dhcp4_server" + run_statement "dhcp4_server" "$qry" 1 + + # table: dhcp4_audit + qry="select id, object_type, object_id, modification_type from dhcp4_audit" + run_statement "dhcp4_audit" "$qry" + + # table: dhcp4_global_parameter + qry="select id, name, value, parameter_type, modification_ts from dhcp4_global_parameter" + run_statement "dhcp4_global_parameter" "$qry" + + # table: dhcp4_global_parameter_server + qry="select parameter_id, server_id, modification_ts from dhcp4_global_parameter_server" + run_statement "dhcp4_global_parameter_server" "$qry" + + # table: dhcp4_option_def + qry="select id, code, name, space, type, modification_ts, is_array, encapsulate, record_types, user_context from dhcp4_option_def" + run_statement "dhcp4_option_def" "$qry" + + # table: dhcp4_option_def_server + qry="select option_def_id, server_id, modification_ts from dhcp4_option_def_server" + run_statement "dhcp4_option_def_server" "$qry" + + # table: dhcp4_shared_network + qry="select id, name, client_class, interface, match_client_id, modification_ts, rebind_timer, relay, renew_timer, require_client_classes, user_context, valid_lifetime, authoritative, calculate_tee_times, t1_percent, t2_percent, boot_file_name, next_server, server_hostname from dhcp4_shared_network" + run_statement "dhcp4_shared_network" "$qry" + + # table: dhcp4_shared_network_server + qry="select shared_network_id, server_id, modification_ts from dhcp4_shared_network_server" + run_statement "dhcp4_shared_network_server" "$qry" + + # table: dhcp4_subnet + qry="select subnet_prefix, 4o6_interface, 4o6_interface_id, 4o6_subnet, boot_file_name, client_class, interface, match_client_id, modification_ts, next_server, rebind_timer, relay, renew_timer, require_client_classes, server_hostname, shared_network_name, subnet_id, user_context, valid_lifetime, authoritative, calculate_tee_times, t1_percent, t2_percent from dhcp4_subnet" + run_statement "dhcp4_subnet" "$qry" + + # table: dhcp4_pool + qry="select id, start_address, end_address, subnet_id, modification_ts from dhcp4_pool" + run_statement "dhcp4_pool" "$qry" + + # table: dhcp4_subnet_server + qry="select subnet_id, server_id, modification_ts from dhcp4_subnet_server" + run_statement "dhcp4_subnet_server" "$qry" + + # table: dhcp4_options (should include three new columns) + qry="select shared_network_name, pool_id, modification_ts from dhcp4_options" + run_statement "dhcp4_options" "$qry" + + # table: dhcp4_options_server + qry="select option_id, server_id, modification_ts from dhcp4_options_server" + run_statement "dhcp4_options_server" "$qry" + + # table: dhcp6_server + qry="select id, tag, description, modification_ts from dhcp6_server" + run_statement "dhcp6_server" "$qry" + + # table: dhcp6_server - check if it contains default entry + qry="select count(*) from dhcp6_server" + run_statement "dhcp6_server" "$qry" 1 + + # table: dhcp6_audit + qry="select id, object_type, object_id, modification_type from dhcp6_audit" + run_statement "dhcp6_audit" "$qry" + + # table: dhcp6_global_parameter + qry="select id, name, value, parameter_type, modification_ts from dhcp6_global_parameter" + run_statement "dhcp6_global_parameter" "$qry" + + # table: dhcp6_global_parameter_server + qry="select parameter_id, server_id, modification_ts from dhcp6_global_parameter_server" + run_statement "dhcp6_global_parameter_server" "$qry" + + # table: dhcp6_option_def + qry="select id, code, name, space, type, modification_ts, is_array, encapsulate, record_types, user_context from dhcp6_option_def" + run_statement "dhcp6_option_def" "$qry" + + # table: dhcp6_option_def_server + qry="select option_def_id, server_id, modification_ts from dhcp6_option_def_server" + run_statement "dhcp6_option_def_server" "$qry" + + # table: dhcp6_shared_network + qry="select id, name, client_class, interface, modification_ts, preferred_lifetime, rapid_commit, rebind_timer, relay, renew_timer, require_client_classes, user_context, valid_lifetime, calculate_tee_times, t1_percent, t2_percent, interface_id from dhcp6_shared_network" + run_statement "dhcp6_shared_network" "$qry" + + # table: dhcp6_shared_network_server + qry="select shared_network_id, server_id, modification_ts from dhcp6_shared_network_server" + run_statement "dhcp6_shared_network" "$qry" + + # table: dhcp6_subnet + qry="select subnet_prefix, client_class, interface, modification_ts, preferred_lifetime, rapid_commit, rebind_timer, relay, renew_timer, require_client_classes, shared_network_name, subnet_id, user_context, valid_lifetime, calculate_tee_times, t1_percent, t2_percent, interface_id from dhcp6_subnet" + run_statement "dhcp6_subnet" "$qry" + + # table: dhcp6_subnet_server + qry="select subnet_id, server_id, modification_ts from dhcp6_subnet_server" + run_statement "dhcp6_subnet_server" "$qry" + + # table: dhcp6_pd_pool + qry="select id, prefix_length, delegated_prefix_length, subnet_id, modification_ts from dhcp6_pd_pool" + run_statement "dhcp6_pd_pool" "$qry" + + # table: dhcp6_pool + qry="select id, start_address, end_address, subnet_id, modification_ts from dhcp6_pool" + run_statement "dhcp6_pool" "$qry" + + # table: dhcp6_options (should include four new columns) + qry="select shared_network_name, pool_id, pd_pool_id, modification_ts from dhcp6_options" + run_statement "dhcp6_options" "$qry" + + # table: dhcp6_options_server + qry="select option_id, server_id, modification_ts from dhcp6_options_server" + run_statement "dhcp6_options_server" "$qry" + + # Schema upgrade from 7.0 to 8.0 + + # Test that createAuditRevisionDHCP4 exists and creates entry in + # the dhcp4_audit_revision table. + qry="CALL createAuditRevisionDHCP4('2019-01-28 23:59:11', 'all', 'some log message', 0)" + run_statement "createAuditRevisionDHCP4" "$qry" + + qry="SELECT COUNT(*) from dhcp4_audit_revision" + run_statement "dhcp4_audit_revision count" "$qry" 1 + + qry="SELECT id, modification_ts, server_id, log_message FROM dhcp4_audit_revision" + run_statement "dhcp4_audit_revision" "$qry" + + # Test that createAuditEntryDHCP4 exists and creates entry in + # the dhcp4_audit table. + qry="SET @audit_revision_id = (SELECT id FROM dhcp4_audit_revision LIMIT 1); CALL createAuditEntryDHCP4('dhcp4_subnet', 1, 'create')" + run_statement "createAuditEntryDHCP4" "$qry" + + qry="SELECT COUNT(*) FROM dhcp4_audit" + run_statement "dhcp4_audit count" "$qry" 1 + + qry="SELECT id, object_type, object_id, modification_type, revision_id FROM dhcp4_audit" + run_statement "dhcp4_audit" "$qry" + + # Test that createOptionAuditDHCP4 exists can create an audit + # entry. + + # First set the cascade_transaction session variable to check that + # the procedure won't create the audit entry for the option when + # this flag is set. + qry="SET @audit_revision_id = (SELECT id FROM dhcp4_audit_revision LIMIT 1); SET @cascade_transaction = 1; CALL createOptionAuditDHCP4('create', 0, 1024, NULL, NULL, NULL, NULL, now())" + run_statement "createOptionAuditDHCP4 cascade update" "$qry" + + # The number of rows matching the audit entry should be 0. + qry="SELECT COUNT(*) FROM dhcp4_audit WHERE object_type = 'dhcp4_options' AND object_id = 1024" + run_statement "createOptionAuditDHCP4 cascade update, entry not inserted" "$qry" 0; + + # This time set the cascade_update to 0 and expect that the + # audit entry will be created for the option. + qry="SET @audit_revision_id = (SELECT id FROM dhcp4_audit_revision LIMIT 1); SET @cascade_transaction = 0; CALL createOptionAuditDHCP4('create', 0, 1024, NULL, NULL, NULL, NULL, now())" + run_statement "createOptionAuditDHCP4 cascade update" "$qry" + + qry="SELECT COUNT(*) FROM dhcp4_audit WHERE object_type = 'dhcp4_options' AND object_id = 1024" + run_statement "createOptionAuditDHCP4 cascade update, entry not inserted" "$qry" 1; + + # Test that createAuditRevisionDHCP6 exists and creates entry in + # the dhcp6_audit_revision table. + qry="CALL createAuditRevisionDHCP6('2019-01-28 23:59:11', 'all', 'some log message', 0)" + run_statement "createAuditRevisionDHCP6" "$qry" + + qry="SELECT COUNT(*) from dhcp6_audit_revision" + run_statement "dhcp6_audit_revision count" "$qry" 1 + + qry="SELECT id, modification_ts, server_id, log_message FROM dhcp6_audit_revision" + run_statement "dhcp6_audit_revision" "$qry" + + # Test that createAuditEntryDHCP6 exists and creates entry in + # the dhcp6_audit table. + qry="SET @audit_revision_id = (SELECT id FROM dhcp6_audit_revision LIMIT 1); CALL createAuditEntryDHCP6('dhcp6_subnet', 1, 'create')" + run_statement "createAuditEntryDHCP6" "$qry" + + qry="SELECT COUNT(*) FROM dhcp6_audit" + run_statement "dhcp6_audit count" "$qry" 1 + + qry="SELECT id, object_type, object_id, modification_type, revision_id FROM dhcp6_audit" + run_statement "dhcp6_audit" "$qry" + + # Test that createOptionAuditDHCP6 exists can create an audit + # entry. + + # First set the cascade_transaction session variable to check that + # the procedure won't create the audit entry for the option when + # this flag is set. + qry="SET @audit_revision_id = (SELECT id FROM dhcp6_audit_revision LIMIT 1); SET @cascade_transaction = 1; CALL createOptionAuditDHCP6('create', 0, 1024, NULL, NULL, NULL, NULL, NULL, now())" + run_statement "createOptionAuditDHCP6 cascade update" "$qry" + + # The number of rows matching the audit entry should be 0. + qry="SELECT COUNT(*) FROM dhcp6_audit WHERE object_type = 'dhcp6_options' AND object_id = 1024" + run_statement "createOptionAuditDHCP6 cascade update, entry not inserted" "$qry" 0; + + # This time set the cascade_update to 0 and expect that the + # audit entry will be created for the option. + qry="SET @audit_revision_id = (SELECT id FROM dhcp6_audit_revision LIMIT 1); SET @cascade_transaction = 0; CALL createOptionAuditDHCP6('create', 0, 1024, NULL, NULL, NULL, NULL, NULL,now())" + run_statement "createOptionAuditDHCP6 cascade update" "$qry" + + qry="SELECT COUNT(*) FROM dhcp6_audit WHERE object_type = 'dhcp6_options' AND object_id = 1024" + run_statement "createOptionAuditDHCP6 cascade update, entry not inserted" "$qry" 1; + + # New triggers aren't tested here because the extensive tests are + # provided with the backend implementations. + + # parameter_data_type must exist and must have 4 rows. + qry="SELECT COUNT(*) FROM parameter_data_type" + run_statement "parameter_data_type count" "$qry" 4; + + # Schema upgrade from 8.0 to 8.2 + + # New lifetime bounds. + + # table: dhcp4_shared_network + qry="select id, name, client_class, interface, match_client_id, modification_ts, rebind_timer, relay, renew_timer, require_client_classes, user_context, valid_lifetime, min_valid_lifetime, max_valid_lifetime, authoritative, calculate_tee_times, t1_percent, t2_percent, boot_file_name, next_server, server_hostname from dhcp4_shared_network" + run_statement "dhcp4_shared_network" "$qry" + + # table: dhcp4_subnet + qry="select subnet_prefix, 4o6_interface, 4o6_interface_id, 4o6_subnet, boot_file_name, client_class, interface, match_client_id, modification_ts, next_server, rebind_timer, relay, renew_timer, require_client_classes, server_hostname, shared_network_name, subnet_id, user_context, valid_lifetime, min_valid_lifetime, max_valid_lifetime, authoritative, calculate_tee_times, t1_percent, t2_percent from dhcp4_subnet" + run_statement "dhcp4_subnet" "$qry" + + # table: dhcp6_shared_network + qry="select id, name, client_class, interface, modification_ts, preferred_lifetime, min_preferred_lifetime, max_preferred_lifetime,rapid_commit, rebind_timer, relay, renew_timer, require_client_classes, user_context, valid_lifetime, min_valid_lifetime, max_valid_lifetime, calculate_tee_times, t1_percent, t2_percent from dhcp6_shared_network" + run_statement "dhcp6_shared_network" "$qry" + + # table: dhcp6_subnet + qry="select subnet_prefix, client_class, interface, modification_ts, preferred_lifetime, min_preferred_lifetime, max_preferred_lifetime, rapid_commit, rebind_timer, relay, renew_timer, require_client_classes, shared_network_name, subnet_id, user_context, valid_lifetime, min_valid_lifetime, max_valid_lifetime, calculate_tee_times, t1_percent, t2_percent from dhcp6_subnet" + run_statement "dhcp6_subnet" "$qry" + + # table: dhcp4_pool (should include three new columns) + qry="select client_class, require_client_classes, user_context from dhcp4_pool" + run_statement "dhcp4_pool" "$qry" + + # table: dhcp6_pd_pool (should include five new columns) + qry="select excluded_prefix, excluded_prefix_length, client_class, require_client_classes, user_context from dhcp6_pd_pool" + run_statement "dhcp6_pd_pool" "$qry" + + # table: dhcp6_pool (should include three new columns) + qry="select client_class, require_client_classes, user_context from dhcp6_pool" + run_statement "dhcp6_pool" "$qry" + + # Verify that dhcp4_option_def column name is is_array + qry="select is_array from dhcp4_option_def" + run_statement "dhcp4_option_def verify is_array column" "$qry" + + # Verify that dhcp6_option_def column name is is_array + qry="select is_array from dhcp6_option_def" + run_statement "dhcp6_option_def verify is_array column" "$qry" + + # Schema upgrade from 8.2 to 9.3 + + # New DDNS columns. + + # table: dhcp4_shared_network (should include six new columns) + qry="select ddns_send_updates, ddns_override_no_update, ddns_override_client_update, ddns_replace_client_name, ddns_generated_prefix, ddns_qualifying_suffix from dhcp4_shared_network" + run_statement "dhcp4_shared_network" "$qry" + + # table: dhcp6_shared_network (should include six new columns) + qry="select ddns_send_updates, ddns_override_no_update, ddns_override_client_update, ddns_replace_client_name, ddns_generated_prefix, ddns_qualifying_suffix from dhcp6_shared_network" + run_statement "dhcp6_shared_network" "$qry" + + # table: dhcp4_subnet (should include six new columns) + qry="select ddns_send_updates, ddns_override_no_update, ddns_override_client_update, ddns_replace_client_name, ddns_generated_prefix, ddns_qualifying_suffix from dhcp4_subnet" + run_statement "dhcp4_subnet" "$qry" + + # table: dhcp6_subnet (should include six new columns) + qry="select ddns_send_updates, ddns_override_no_update, ddns_override_client_update, ddns_replace_client_name, ddns_generated_prefix, ddns_qualifying_suffix from dhcp6_subnet" + run_statement "dhcp6_subnet" "$qry" + + # Schema upgrade from 9.3 to 9.4. + + # Non unique indexes on hosts allowing multiple reservation for the same IP. + + insert_sql="\ +insert into hosts(dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, ipv4_address) values (hex('010101010101'), 0, 1, inet_aton('192.0.2.0'));\ +insert into hosts(dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, ipv4_address) values (hex('010101010102'), 0, 1, inet_aton('192.0.2.0'))" + run_command \ + mysql_execute "$insert_sql" + assert_eq 0 "${EXIT_CODE}" "insert into hosts failed, expected exit code %d, actual %d" + + # Schema upgrade from 9.4 to 9.5. + + # table: dhcp4_shared_network (reservation_mode replaced by reservations flags) + qry="select reservations_global, reservations_in_subnet, reservations_out_of_pool from dhcp4_shared_network" + run_statement "dhcp4_shared_network" "$qry" + + qry="show columns from dhcp4_shared_network like 'reservation_mode'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show columns from dhcp4_shared_network like 'reservation_mode' failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci reservation) || true + assert_eq 0 "${count}" "dhcp4_shared_network has still reservation_mode column. (returned count %d, expected %d)" + + # table: dhcp4_subnet (reservation_mode replaced by reservations flags) + qry="select reservations_global, reservations_in_subnet, reservations_out_of_pool from dhcp4_subnet" + run_statement "dhcp4_subnet" "$qry" + + qry="show columns from dhcp4_subnet like 'reservation_mode'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show columns from dhcp4_subnet like 'reservation_mode' failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci reservation) || true + assert_eq 0 "${count}" "dhcp4_subnet has still reservation_mode column. (returned count %d, expected %d)" + + # table: dhcp6_shared_network (reservation_mode replaced by reservations flags) + qry="select reservations_global, reservations_in_subnet, reservations_out_of_pool from dhcp6_shared_network" + run_statement "dhcp6_shared_network" "$qry" + + qry="show columns from dhcp6_shared_network like 'reservation_mode'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show columns from dhcp6_shared_network like 'reservation_mode' failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci reservation) || true + assert_eq 0 "${count}" "dhcp6_shared_network has still reservation_mode column. (returned count %d, expected %d)" + + # table: dhcp6_subnet (reservation_mode replaced by reservations flags) + qry="select reservations_global, reservations_in_subnet, reservations_out_of_pool from dhcp6_subnet" + run_statement "dhcp6_subnet" "$qry" + + qry="show columns from dhcp6_subnet like 'reservation_mode'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show columns from dhcp6_subnet like 'reservation_mode' failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci reservation) || true + assert_eq 0 "${count}" "dhcp6_subnet has still reservation_mode column. (returned count %d, expected %d)" + + # Schema upgrade from 9.5 to 9.6. + + # table: dhcp4_shared_network new cache_threshold and cache_max_age columns + qry="select cache_threshold, cache_max_age from dhcp4_shared_network" + run_statement "dhcp4_shared_network" "$qry" + + # table: dhcp4_subnet new cache_threshold and cache_max_age columns + qry="select cache_threshold, cache_max_age from dhcp4_subnet" + run_statement "dhcp4_shared_network" "$qry" + + # table: dhcp6_shared_network new cache_threshold and cache_max_age columns + qry="select cache_threshold, cache_max_age from dhcp6_shared_network" + run_statement "dhcp6_shared_network" "$qry" + + # table: dhcp6_subnet new cache_threshold and cache_max_age columns + qry="select cache_threshold, cache_max_age from dhcp6_subnet" + run_statement "dhcp6_shared_network" "$qry" + + qry='SELECT id FROM logs' + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry} failed: expected status code %d, returned %d" + + # Check upgrade from 10.0 to 11.0. + qry="show indexes from lease4 where key_name = 'lease4_by_expire_state'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show indexes from lease4 failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci lease4_by_expire_state) + assert_eq 2 "${count}" "lease4_by_expire_state wrong or missing. (expected count %d, actual %d)" + + qry="show indexes from lease6 where key_name = 'lease6_by_expire_state'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show indexes from lease6 failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci lease6_by_expire_state) + assert_eq 2 "${count}" "lease6_by_expire_state wrong or missing. (expected count %d, actual %d)" + + # Verify preferred lifetime columns exist. + qry="select preferred_lifetime,min_preferred_lifetime,max_preferred_lifetime from dhcp6_client_class where name=''" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "$qry failed. dhcp6_client_classes preferred lifetime columns missing?" + + # Check upgrade from 11.0 to 12.0. + + # Add classes with associated options. + qry="\ +SET @disable_audit = 1;\ +INSERT INTO dhcp4_client_class(name, modification_ts) VALUES ('foo', now());\ +INSERT INTO dhcp4_options(code, scope_id, dhcp_client_class, modification_ts) VALUES (222, 2, 'foo', now());\ +INSERT INTO dhcp6_client_class(name, modification_ts) VALUES ('foo', now());\ +INSERT INTO dhcp6_options(code, scope_id, dhcp_client_class, modification_ts) VALUES (222, 2, 'foo', now());\ +SET @disable_audit = 0" + run_command \ + mysql_execute "$qry" + assert_eq 0 "${EXIT_CODE}" "inserting classes and options failed, expected exit code %d, actual %d" + + # Delete the classes. + qry="\ +SET @disable_audit = 1;\ +DELETE FROM dhcp4_client_class;\ +DELETE FROM dhcp6_client_class;\ +SET @disable_audit = 0" + run_command \ + mysql_execute "$qry" + assert_eq 0 "${EXIT_CODE}" "deleting classes failed, expected exit code %d, actual %d" + + # Ensure that the DHCPv4 option was deleted. + qry="SELECT COUNT(*) from dhcp4_options" + run_statement "dhcp4_options count" "$qry" 0 + + # Ensure that the DHCPv6 option was deleted. + qry="SELECT COUNT(*) from dhcp6_options" + run_statement "dhcp6_options count" "$qry" 0 + + # Check upgrade from 12.0 to 13.0. + mysql_upgrade_12_to_13_test + + # Check upgrade from 13.0 to 14.0. + mysql_upgrade_13_to_14_test + + # Check upgrade from 14.0 to 15.0. + mysql_upgrade_14_to_15_test + + # Check upgrade from 15.0 to 16.0. + + # table: lease4 new relay_id remote_id column and index. + qry="select relay_id from lease4" + run_statement "lease4" "$qry" + + qry="show indexes from lease4 where key_name = 'lease4_by_relay_id'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show indexes from lease4 failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci lease4_by_relay_id) + assert_eq 1 "${count}" "lease4_by_relay_id wrong or missing. (expected count %d, actual %d)" + + # table: lease4 new remote_id column and index. + qry="select remote_id from lease4" + run_statement "lease4" "$qry" + + qry="show indexes from lease4 where key_name = 'lease4_by_remote_id'" + run_command \ + mysql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "show indexes from lease4 failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci lease4_by_remote_id) + assert_eq 1 "${count}" "lease4_by_remote_id wrong or missing. (expected count %d, actual %d)" + + # Check upgrade from 16.0 to 17.0. + mysql_upgrade_16_to_17_test + + # Check upgrade from 17.0 to 18.0. + mysql_upgrade_17_to_18_test + + # Check upgrade from 18.0 to 19.0. + mysql_upgrade_18_to_19_test + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# May accept additional parameters to be passed to lease-dump. +mysql_lease4_dump_test() { + test_start "mysql.lease4_dump_test${1-}" + + test_dir="@abs_top_srcdir@/src/bin/admin/tests" + output_dir="@abs_top_builddir@/src/bin/admin/tests" + + output_file="$output_dir/data/mysql.lease4_dump_test.output.csv" + ref_file="$test_dir/data/lease4_dump_test.reference.csv" + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + for i in "${output_file}" \ + "${output_file}.tmp" \ + "/tmp/$(basename "${output_file}").tmp" \ + ; do + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${i}" + else + rm -f "${i}" + fi + done + + # Let's wipe the whole database + mysql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" \ + -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" + + # Insert the reference record + insert_sql="\ +insert into lease4 values(10,20,30,40,(SELECT FROM_UNIXTIME(1642000000)),50,1,1,'one.example.com',0,NULL,NULL,NULL,0); +insert into lease4 values(11,NULL,123,40,(SELECT FROM_UNIXTIME(1643210000)),50,1,1,'',1,'{ }',NULL,NULL,0);\ +insert into lease4 values(12,22,NULL,40,(SELECT FROM_UNIXTIME(1643212345)),50,1,1,'three,example,com',2,'{ \"a\": 1, \"b\": \"c\" }',NULL,NULL,0)" + + run_command \ + mysql_execute "$insert_sql" + assert_eq 0 "${EXIT_CODE}" "insert into lease4 failed, expected exit code %d, actual %d" + + # Dump lease4 to output_file + run_command \ + "${kea_admin}" lease-dump mysql -4 -u "${db_user}" -p "${db_password}" -n "${db_name}" \ + -d "${db_scripts_dir}" -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -4 failed, expected exit code %d, actual %d" + + # Compare the dump output to reference file, they should be identical + run_command \ + cmp -s "${output_file}" "${ref_file}" + assert_eq 0 "${EXIT_CODE}" "dump file does not match reference file, expected exit code %d, actual %d, diff:\n$(diff ${ref_file} ${output_file})" + + # Remove the files. + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# May accept additional parameters to be passed to lease-dump. +mysql_lease6_dump_test() { + test_start "mysql.lease6_dump_test${1-}" + + test_dir="@abs_top_srcdir@/src/bin/admin/tests" + output_dir="@abs_top_builddir@/src/bin/admin/tests" + + output_file="$output_dir/data/mysql.lease6_dump_test.output.csv" + ref_file="$test_dir/data/lease6_dump_test.reference.csv" + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + for i in "${output_file}" \ + "${output_file}.tmp" \ + "/tmp/$(basename "${output_file}").tmp" \ + ; do + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${i}" + else + rm -f "${i}" + fi + done + + # Let's wipe the whole database + mysql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" + + # Insert the reference record + insert_sql="\ +insert into lease6 values(inet6_aton('::10'),203,30,(SELECT FROM_UNIXTIME(1642000000)),40,50,1,60,128,1,1,'one.example.com',80,90,16,0,NULL,0);\ +insert into lease6 values(inet6_aton('::11'),213,30,(SELECT FROM_UNIXTIME(1643210000)),40,50,1,60,128,1,1,'',80,90,1,1,'{ }',0);\ +insert into lease6 values(inet6_aton('::12'),223,30,(SELECT FROM_UNIXTIME(1643212345)),40,50,1,60,128,1,1,'three,example,com',80,90,4,2,'{ \"a\": 1, \"b\": \"c\" }',0)" + + run_command \ + mysql_execute "$insert_sql" + assert_eq 0 "${EXIT_CODE}" "insert into lease6 failed, expected exit code %d, actual %d" + + # Dump lease4 to output_file + run_command \ + "${kea_admin}" lease-dump mysql -6 -u "${db_user}" -p "${db_password}" -n "${db_name}" \ + -d "${db_scripts_dir}" -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -6 failed, expected exit code %d, actual %d" + + # Compare the dump output to reference file, they should be identical + run_command \ + cmp -s "${output_file}" "${ref_file}" + assert_eq 0 "${EXIT_CODE}" "dump file does not match reference file, expected exit code %d, actual %d, diff:\n$(diff ${ref_file} ${output_file})" + + # Remove the files. + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# May accept additional parameters to be passed to lease-dump or to lease-upload. +mysql_lease4_upload_test() { + test_start "mysql.lease4_upload_test${1-}" + + input_file="@abs_top_srcdir@/src/bin/admin/tests/data/lease4_dump_test.reference.csv" + input_file_cp="@abs_top_builddir@/src/bin/admin/tests/data/lease4_dump_test.reference.csv" + output_file="@abs_top_builddir@/src/bin/admin/tests/data/lease4_dump_test.output.csv" + + if [ "${input_file}" != "${input_file_cp}" ]; then + cp -f ${input_file} ${input_file_cp} + input_file=${input_file_cp} + input_file_cp="" + fi + + # Wipe the whole database. + mysql_wipe + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + for i in "${input_file}.tmp" \ + "${output_file}" \ + "${output_file}.tmp" \ + "/tmp/$(basename "${input_file}").tmp" \ + ; do + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${i}" + else + rm -f "${i}" + fi + done + + # Initialize the database. + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" \ + -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" + + # Upload leases. + run_command \ + "${kea_admin}" lease-upload mysql -4 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -i "${input_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-upload -4 failed, expected exit code %d, actual %d" + + # Dump leases. + run_command \ + "${kea_admin}" lease-dump mysql -4 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -4 failed, expected exit code %d, actual %d" + + # Compare the initial file used for upload to the file retrieved from dump, they should be identical. + run_command \ + cmp -s "${input_file}" "${output_file}" + assert_eq 0 "${EXIT_CODE}" "file resulted from dump after upload does not match file used for upload, expected exit code %d, actual %d, diff:\n$(diff ${input_file} ${output_file})" + + # Remove the files. + if [ "${input_file}" != "${input_file_cp}" ]; then + rm -f "${input_file}" + fi + rm -f "${input_file}.tmp" + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Wipe the whole database. + mysql_wipe + + test_finish 0 +} + +# May accept additional parameters to be passed to lease-dump or to lease-upload. +mysql_lease6_upload_test() { + test_start "mysql.lease6_upload_test${1-}" + + input_file="@abs_top_srcdir@/src/bin/admin/tests/data/lease6_dump_test.reference.csv" + input_file_cp="@abs_top_builddir@/src/bin/admin/tests/data/lease6_dump_test.reference.csv" + output_file="@abs_top_builddir@/src/bin/admin/tests/data/lease6_dump_test.output.csv" + + if [ "${input_file}" != "${input_file_cp}" ]; then + cp -f ${input_file} ${input_file_cp} + input_file=${input_file_cp} + input_file_cp="" + fi + + # Wipe the whole database. + mysql_wipe + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + for i in "${input_file}.tmp" \ + "${output_file}" \ + "${output_file}.tmp" \ + "/tmp/$(basename "${input_file}").tmp" \ + ; do + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${i}" + else + rm -f "${i}" + fi + done + + # Initialize the database. + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" \ + -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" + + # Upload leases. + run_command \ + "${kea_admin}" lease-upload mysql -6 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -i "${input_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-upload -6 failed, expected exit code %d, actual %d" + + # Dump leases. + run_command \ + "${kea_admin}" lease-dump mysql -6 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -6 failed, expected exit code %d, actual %d" + + # Compare the initial file used for upload to the file retrieved from dump, they should be identical. + run_command \ + cmp -s "${input_file}" "${output_file}" + assert_eq 0 "${EXIT_CODE}" "file resulted from dump after upload does not match file used for upload, expected exit code %d, actual %d, diff:\n$(diff ${input_file} ${output_file})" + + # Remove the files. + if [ "${input_file}" != "${input_file_cp}" ]; then + rm -f "${input_file}" + fi + rm -f "${input_file}.tmp" + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Wipe the whole database. + mysql_wipe + + test_finish 0 +} + +# Verifies lease4_stat trigger operations on +# an new, empty database. It inserts, updates, and +# deletes various leases, checking lease4_stat +# values along the way. +mysql_lease4_stat_test() { + test_start "mysql.lease4_stat_test" + + # Let's wipe the whole database + mysql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init mysql failed, expected %d, returned non-zero status code %d" + + # Verify lease4 stat table is present + qry="select count(subnet_id) from lease4_stat" + run_statement "#1" "$qry" 0 + + # Insert lease4 + qry="insert into lease4 (address, subnet_id, state) values (111,1,0)" + run_statement "#2" "$qry" + + # Assigned state count should be 1 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#3" "$qry" 1 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 0" + run_statement "#4" "$qry" 1 + + # Set lease state to declined + qry="update lease4 set state = 1 where address = 111" + run_statement "#5" "$qry" + + # Leases state count for assigned should be 0 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#6" "$qry" 0 + + # Leases state count for assigned should be 0 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 0" + run_statement "#7" "$qry" 0 + + # Leases state count for declined should be 1 + qry="select leases from lease4_stat where subnet_id = 1 and state = 1" + run_statement "#8" "$qry" 1 + + # Leases state count for declined should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 1" + run_statement "#9" "$qry" 1 + + # Delete the lease + qry="delete from lease4 where address = 111" + run_statement "#10" "$qry" + + # Leases state count for declined should be 0 + qry="select leases from lease4_stat where subnet_id = 1 and state = 1" + run_statement "#11" "$qry" 0 + + # Leases state count for declined should be 0 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 1" + run_statement "#12" "$qry" 0 + + # Insert lease4 + qry="insert into lease4 (address, subnet_id, pool_id, state) values (112,1,1,0)" + run_statement "#13" "$qry" + + # Assigned state count should be 1 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#14" "$qry" 1 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 1 and state = 0" + run_statement "#15" "$qry" 1 + + # Insert lease4 + qry="insert into lease4 (address, subnet_id, pool_id, state) values (113,1,2,0)" + run_statement "#16" "$qry" + + # Assigned state count should be 2 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#17" "$qry" 2 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 1 and state = 0" + run_statement "#18" "$qry" 1 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 2 and state = 0" + run_statement "#19" "$qry" 1 + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Verifies that lease6_stat triggers operate correctly +# for using a given address and lease_type. It will +# insert a lease, update it, and delete checking the +# lease stat counts along the way. It assumes the +# database has been created but is empty. +# param addr - address to use to add to subnet 1 +# param ltype - type of lease to create +mysql_lease6_stat_per_type() { + addr=$1;shift + addr1=$1;shift + addr2=$1;shift + ltype=$1 + + # insert a lease6 for addr and ltype, state assigned + qry="insert into lease6 (address, lease_type, subnet_id, state) values (inet6_aton('$addr'),$ltype,1,0)" + run_statement "#2" "$qry" + + # assigned stat should be 1 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#3" "$qry" 1 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 0" + run_statement "#4" "$qry" 1 + + # update the lease, changing state to declined + qry="update lease6 set state = 1 where address = inet6_aton('$addr')" + run_statement "#5" "$qry" + + # leases stat for assigned state should be 0 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#6" "$qry" 0 + + # leases stat for assigned state should be 0 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 0" + run_statement "#7" "$qry" 0 + + # leases count for declined state should be 1 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 1" + run_statement "#8" "$qry" 1 + + # leases count for declined state should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 1" + run_statement "#9" "$qry" 1 + + # delete the lease + qry="delete from lease6 where address = inet6_aton('$addr')" + run_statement "#10" "$qry" + + # leases count for declined state should be 0 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#11" "$qry" 0 + + # leases count for declined state should be 0 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 0" + run_statement "#12" "$qry" 0 + + # insert a lease6 for addr and ltype, state assigned + qry="insert into lease6 (address, lease_type, subnet_id, pool_id, state) values (inet6_aton('$addr1'),$ltype,1,1,0)" + run_statement "#13" "$qry" + + # assigned stat should be 1 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#14" "$qry" 1 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 1 and state = 0" + run_statement "#15" "$qry" 1 + + # insert a lease6 for addr and ltype, state assigned + qry="insert into lease6 (address, lease_type, subnet_id, pool_id, state) values (inet6_aton('$addr2'),$ltype,1,2,0)" + run_statement "#16" "$qry" + + # assigned stat should be 2 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#17" "$qry" 2 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 1 and state = 0" + run_statement "#18" "$qry" 1 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 2 and state = 0" + run_statement "#19" "$qry" 1 +} + +# Verifies that lease6_stat triggers operation correctly +# for both NA and PD lease types, mysql_lease6_stat_per_type() +mysql_lease6_stat_test() { + + test_start "mysql.lease6_stat_test" + + # Let's wipe the whole database + mysql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init mysql failed, expected %d, returned non-zero status code %d" + + # verify lease6 stat table is present + qry="select count(subnet_id) from lease6_stat" + run_statement "#1" "$qry" + + # Test for address ::11, NA lease type + mysql_lease6_stat_per_type "::11" "::12" "::13" "0" + + # Test for address ::22, PD lease type + mysql_lease6_stat_per_type "::22" "::23" "::24" "1" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Verifies that you can upgrade from earlier version and +# lease<4/6>_stat tables will be populated based on existing +# leases and that the stat triggers work properly. +mysql_lease_stat_upgrade_test() { + test_start "mysql.lease_stat_upgrade_test" + + # Let's wipe the whole database + mysql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which prepopulate the lease stat + # tables. + # + # Initialize database to schema 1.0. + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + + # Now upgrade to schema 4.0, this has lease_state in it + mysql_upgrade_schema_to_version 4.0 + + # Now we need insert some leases to "migrate" for both v4 and v6 + qry=\ +"insert into lease4 (address, subnet_id, state) values (111,10,0);\ + insert into lease4 (address, subnet_id, state) values (222,10,0);\ + insert into lease4 (address, subnet_id, state) values (333,10,1);\ + insert into lease4 (address, subnet_id, state) values (444,10,2);\ + insert into lease4 (address, subnet_id, state) values (555,77,0)" + run_statement "insert v4 leases" "$qry" + + qry=\ +"insert into lease6 (address, lease_type, subnet_id, state) values (inet6_aton('::11'),0,40,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values (inet6_aton('::22'),0,40,1);\ + insert into lease6 (address, lease_type, subnet_id, state) values (inet6_aton('::33'),1,40,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values (inet6_aton('::44'),1,50,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values (inet6_aton('::55'),1,50,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values (inet6_aton('::66'),1,40,2)" + run_statement "insert v6 leases" "$qry" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + + # + # First we'll verify lease4_stats are correct after migration. + # + + # Assigned leases for subnet 10 should be 2 + qry="select leases from lease4_stat where subnet_id = 10 and state = 0" + run_statement "#4.1" "$qry" 2 + + # Assigned leases for subnet 10 should be 2 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 0" + run_statement "#4.2" "$qry" 2 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.3" "$qry" 1 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 0 and state = 0" + run_statement "#4.4" "$qry" 1 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_stat where state = 2" + run_statement "#4.5" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_pool_stat where state = 2" + run_statement "#4.6" "$qry" 0 + + # + # Now we'll verify v4 trigger operation for insert, update, and delete + # + + # Insert a new lease subnet 77 + qry="insert into lease4 (address, subnet_id, pool_id, state) values (777,77,1,0)" + run_statement "#4.7" "$qry" + + # Assigned count for subnet 77 should be 2 + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.8" "$qry" 2 + + # Assigned count for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 0 and state = 0" + run_statement "#4.9" "$qry" 1 + + # Assigned count for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 0" + run_statement "#4.10" "$qry" 1 + + # Update the state of the new lease to declined + qry="update lease4 set state = 1 where address = 777" + run_statement "#4.11" "$qry" + + # Assigned count for subnet 77 should be 1 again + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.12" "$qry" 1 + + # Assigned count for subnet 77 should be 1 again + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 0 and state = 0" + run_statement "#4.13" "$qry" 1 + + # Assigned count for subnet 77 should be 0 again + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 0" + run_statement "#4.14" "$qry" 0 + + # Declined count for subnet 77 should be 1 + qry="select leases from lease4_stat where subnet_id = 77 and state = 1" + run_statement "#4.15" "$qry" 1 + + # Declined count for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 1" + run_statement "#4.16" "$qry" 1 + + # Delete the lease. + qry="delete from lease4 where address = 777" + run_statement "#4.17" "$qry" + + # Declined count for subnet 77 should be 0 + qry="select leases from lease4_stat where subnet_id = 77 and state = 1" + run_statement "#4.18" "$qry" 0 + + # Declined count for subnet 77 should be 0 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 1" + run_statement "#4.19" "$qry" 0 + + # + # Next we'll verify lease6_stats are correct after migration. + # + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 0" + run_statement "#6.1" "$qry" 1 + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 0" + run_statement "#6.2" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 1 and state = 0" + run_statement "#6.3" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.4" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 1" + run_statement "#6.5" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 1" + run_statement "#6.6" "$qry" 1 + + # Assigned (PD) leases for subnet 50 should be 2 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.7" "$qry" 2 + + # Assigned (PD) leases for subnet 50 should be 2 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.8" "$qry" 2 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_stat where state = 2" + run_statement "#6.9" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_pool_stat where state = 2" + run_statement "#6.10" "$qry" 0 + + # + # Finally we'll verify v6 trigger operation for insert, update, and delete + # + + # Insert a new lease subnet 50 + qry="insert into lease6 (address, subnet_id, pool_id, lease_type, state) values (inet6_aton('::77'),50,1,1,0)" + run_statement "#6.11" "$qry" + + # Assigned count for subnet 50 should be 3 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.12" "$qry" 3 + + # Assigned count for subnet 50 should be 2 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.13" "$qry" 2 + + # Assigned count for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 1 and state = 0" + run_statement "#6.14" "$qry" 1 + + # Update the state of the new lease to expired + qry="update lease6 set state = 2 where address = inet6_aton('::77')" + run_statement "#6.15" "$qry" + + # Assigned count for subnet 50 should be 2 again + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.16" "$qry" 2 + + # Assigned count for subnet 50 should be 0 again + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 1 and state = 0" + run_statement "#6.17" "$qry" 0 + + # Delete another PD lease. + qry="delete from lease6 where address = inet6_aton('::55')" + run_statement "#6.18" "$qry" + + # Assigned leases for subnet 50 should be 1 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.19" "$qry" 1 + + # Assigned leases for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.20" "$qry" 1 + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +mysql_lease_stat_recount_test() { + test_start "mysql.lease_stat_recount_test" + + # Let's wipe the whole database + mysql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init mysql failed, expected %d, returned non-zero status code %d" + + # Now we need insert some leases to "recount" + qry=\ +"insert into lease4 (address, subnet_id, state) values (111,10,0);\ + insert into lease4 (address, subnet_id, pool_id, state) values (222,10,1,0);\ + insert into lease4 (address, subnet_id, state) values (333,10,1);\ + insert into lease4 (address, subnet_id, state) values (444,10,2);\ + insert into lease4 (address, subnet_id, pool_id, state) values (555,77,2,0)" + run_statement "insert v4 leases" "$qry" + + qry=\ +"insert into lease6 (address, lease_type, subnet_id, state) values ('::111',0,40,0);\ + insert into lease6 (address, lease_type, subnet_id, pool_id, state) values ('::222',0,40,1,1);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::333',1,40,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::444',1,50,0);\ + insert into lease6 (address, lease_type, subnet_id, pool_id, state) values ('::555',1,50,2,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::666',1,40,2)" + run_statement "insert v6 leases" "$qry" + + # Now we change some counters. + qry=\ +"insert into lease4_stat (subnet_id, state, leases) values (20,0,1);\ + update lease4_stat set leases = 5 where subnet_id = 10 and state = 0;\ + delete from lease4_stat where subnet_id = 10 and state = 2" + run_statement "change v4 stats" "$qry" + + qry=\ +"insert into lease4_pool_stat (subnet_id, pool_id, state, leases) values (20,3,0,1);\ + update lease4_pool_stat set leases = 5 where subnet_id = 10 and pool_id = 0 and state = 0;\ + delete from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 2" + run_statement "change v4 stats" "$qry" + + qry=\ +"insert into lease6_stat (subnet_id, lease_type, state, leases) values (20,1,0,1);\ + update lease6_stat set leases = 5 where subnet_id = 40 and lease_type = 0 and state = 0;\ + delete from lease6_stat where subnet_id = 40 and lease_type = 1 and state = 2" + run_statement "change v6 stats" "$qry" + + qry=\ +"insert into lease6_pool_stat (subnet_id, pool_id, lease_type, state, leases) values (20,3,1,0,1);\ + update lease6_pool_stat set leases = 5 where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 0;\ + delete from lease6_pool_stat where subnet_id = 40 and lease_type = 1 and pool_id = 0 and state = 2" + run_statement "change v6 stats" "$qry" + + # Recount all statistics from scratch. + run_command \ + "${kea_admin}" stats-recount mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" + assert_eq 0 "${EXIT_CODE}" "kea-admin stats-recount mysql failed, expected %d, returned non-zero status code %d" + + # + # First we'll verify lease4_stats are correct after recount. + # + + # Assigned leases for subnet 10 should be 2 + qry="select leases from lease4_stat where subnet_id = 10 and state = 0" + run_statement "#4.1" "$qry" 2 + + # Assigned leases for subnet 10 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 0" + run_statement "#4.2" "$qry" 1 + + # Assigned leases for subnet 10 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 1 and state = 0" + run_statement "#4.3" "$qry" 1 + + # Declined leases for subnet 10 should be 1 + qry="select leases from lease4_stat where subnet_id = 10 and state = 1" + run_statement "#4.4" "$qry" 1 + + # Assigned leases for subnet 10 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 1" + run_statement "#4.5" "$qry" 1 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.6" "$qry" 1 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 2 and state = 0" + run_statement "#4.7" "$qry" 1 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_stat where state = 2" + run_statement "#4.8" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_pool_stat where state = 2" + run_statement "#4.9" "$qry" 0 + + # + # Next we'll verify lease6_stats are correct after recount. + # + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 0" + run_statement "#6.1" "$qry" 1 + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 0" + run_statement "#6.2" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 1 and state = 0" + run_statement "#6.3" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.4" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 1" + run_statement "#6.5" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 1 and state = 1" + run_statement "#6.6" "$qry" 1 + + # Assigned (PD) leases for subnet 50 should be 2 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.7" "$qry" 2 + + # Assigned (PD) leases for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.8" "$qry" 1 + + # Assigned (PD) leases for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 2 and state = 0" + run_statement "#6.9" "$qry" 1 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_stat where state = 2" + run_statement "#6.10" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_pool_stat where state = 2" + run_statement "#6.11" "$qry" 0 + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Verifies that you can upgrade from an earlier version and +# that unused subnet ID values in hosts and options tables are +# converted to NULL. +mysql_unused_subnet_id_test() { + test_start "mysql.unused_subnet_id_test" + + # Let's wipe the whole database + mysql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which convert subnet id values + # + # Initialize database to schema 1.0. + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + + # Now upgrade to schema 6.0, this has lease_state in it + mysql_upgrade_schema_to_version 6.0 + + # Now we need insert some hosts to "migrate" for both v4 and v6 + qry=\ +"insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname)\ + values (0, '0123456', 0, 0, 'both'); \ + insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname)\ + values (0, '1123456', 4, 0, 'v4only'); + insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname)\ + values (0, '2123456', 0, 6, 'v6only');\ + insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname) \ + values (0, '3123456', 4, 6, 'neither')" + + run_statement "insert hosts" "$qry" + + # Now we need insert some options to "migrate" for both v4 and v6 + qry=\ +"insert into dhcp4_options (code, dhcp4_subnet_id, scope_id) values (1, 4, 0);\ + insert into dhcp4_options (code, dhcp4_subnet_id, scope_id) values (2, 0, 0);\ + insert into dhcp6_options (code, dhcp6_subnet_id, scope_id) values (1, 6, 0);\ + insert into dhcp6_options (code, dhcp6_subnet_id, scope_id) values (2, 0, 0)" + + run_statement "insert options" "$qry" + + # Ok, we have a 6.0 database with hosts and options. Let's upgrade it to 7.0 + # For versions higher than 7.0 some new constraints fail to be added + # with the not empty tables, for instance the 9.1 -> 9.2 upgrade script + # can raise a MySQL error 1452 for fk_dhcp4_options_subnet constraint. + mysql_upgrade_schema_to_version 7.0 + + # Version should be new 7.0 + version=$("${kea_admin}" db-version mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}") + assert_str_eq "7.0" "${version}" "Expected kea-admin to return %s, returned value was %s" + + # Two hosts should have null v4 subnet ids + qry="select count(host_id) from hosts where dhcp4_subnet_id is null" + run_statement "#hosts.1" "$qry" 2 + + # Two hosts should have v4 subnet ids = 4 + qry="select count(host_id) from hosts where dhcp4_subnet_id = 4" + run_statement "#hosts.2" "$qry" 2 + + # Two hosts should have null v6 subnet ids + qry="select count(host_id) from hosts where dhcp6_subnet_id is null" + run_statement "#hosts.3" "$qry" 2 + + # Two hosts should should have v6 subnet ids = 6 + qry="select count(host_id) from hosts where dhcp6_subnet_id = 6" + run_statement "#hosts.4" "$qry" 2 + + # One option should have null v4 subnet id + qry="select count(option_id) from dhcp4_options where dhcp4_subnet_id is null" + run_statement "#options.1" "$qry" 1 + + # One option should have v4 subnet id = 4 + qry="select count(option_id) from dhcp4_options where dhcp4_subnet_id = 4" + run_statement "#options.2" "$qry" 1 + + # One option should have null v6 subnet id + qry="select count(option_id) from dhcp6_options where dhcp6_subnet_id is null" + run_statement "#options.3" "$qry" 1 + + # One option should have v4 subnet id = 6 + qry="select count(option_id) from dhcp6_options where dhcp6_subnet_id = 6" + run_statement "#options.4" "$qry" 1 + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Verifies that you can upgrade from an earlier version and +# that reservation_mode values in subnet and shared network tables are +# converted to new reservations flags. +mysql_reservation_mode_upgrade_test() { + test_start "mysql.reservation_mode_upgrade_test" + + # Let's wipe the whole database + mysql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which convert reservation values + # + # Initialize database to schema 1.0. + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + + # Now upgrade to schema 9.4, the last version with reservation_mode + mysql_upgrade_schema_to_version 9.4 + + # Now we need insert some subnets and shared networks. + sql=\ +"set @disable_audit = 1; \ + insert into dhcp4_shared_network (name, modification_ts, reservation_mode)\ + values ('test0', current_timestamp, 0);\ + insert into dhcp4_shared_network (name, modification_ts, reservation_mode)\ + values ('test1', current_timestamp, 1);\ + insert into dhcp4_shared_network (name, modification_ts, reservation_mode)\ + values ('test2', current_timestamp, 2);\ + insert into dhcp4_shared_network (name, modification_ts, reservation_mode)\ + values ('test3', current_timestamp, 3);\ + insert into dhcp4_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (1234, '192.0.0.0/24', current_timestamp, 0);\ + insert into dhcp4_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (2345, '192.0.1.0/24', current_timestamp, 1);\ + insert into dhcp4_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (3456, '192.0.2.0/24', current_timestamp, 2);\ + insert into dhcp4_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (4567, '192.0.3.0/24', current_timestamp, 3);\ + insert into dhcp6_shared_network (name, modification_ts, reservation_mode)\ + values ('test0', current_timestamp, 0);\ + insert into dhcp6_shared_network (name, modification_ts, reservation_mode)\ + values ('test1', current_timestamp, 1);\ + insert into dhcp6_shared_network (name, modification_ts, reservation_mode)\ + values ('test2', current_timestamp, 2);\ + insert into dhcp6_shared_network (name, modification_ts, reservation_mode)\ + values ('test3', current_timestamp, 3);\ + insert into dhcp6_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (1234, '2001:db8::/64', current_timestamp, 0);\ + insert into dhcp6_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (2345, '2001:db8:1::/64', current_timestamp, 1);\ + insert into dhcp6_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (3456, '2001:db8:2::/64', current_timestamp, 2);\ + insert into dhcp6_subnet (subnet_id, subnet_prefix, modification_ts, reservation_mode)\ + values (4567, '2001:db8:3::/64', current_timestamp, 3);\ + set @disable_audit = 0" + + run_statement "insert reservation_mode" "$sql" + + qry="select count(*) from dhcp4_shared_network" + run_statement "#get 4_shared count before update" "$qry" 4 + + qry="select count(*) from dhcp4_subnet" + run_statement "#get 4_subnet count before update" "$qry" 4 + + qry="select count(*) from dhcp6_shared_network" + run_statement "#get 6_shared count before update" "$qry" 4 + + qry="select count(*) from dhcp6_subnet" + run_statement "#get 6_subnet count before update" "$qry" 4 + + # Upgrade to schema 9.5. + mysql_upgrade_schema_to_version 9.5 + + # Test DISABLED (0) -> false, false, null + qry="select count(id) from dhcp4_shared_network where reservations_global = false and reservations_in_subnet = false and reservations_out_of_pool is null and name = 'test0'" + run_statement "#4_shared_disabled" "$qry" 1 + + # Test OUT_OF_POOL (1) -> false, true, true + qry="select count(id) from dhcp4_shared_network where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = true and name = 'test1'" + run_statement "#4_shared_out_of_pool" "$qry" 1 + + # Test GLOBAL (2) -> true, false, null + qry="select count(id) from dhcp4_shared_network where reservations_global = true and reservations_in_subnet = false and reservations_out_of_pool is null and name = 'test2'" + run_statement "#4_shared_global" "$qry" 1 + + # Test ALL (3) -> false, true, false + qry="select count(id) from dhcp4_shared_network where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = false and name = 'test3'" + run_statement "#4_shared_all" "$qry" 1 + + # Test DISABLED (0) -> false, false, null + qry="select count(subnet_id) from dhcp4_subnet where reservations_global = false and reservations_in_subnet = false and reservations_out_of_pool is null and subnet_prefix = '192.0.0.0/24'" + run_statement "#4_subnet_disabled" "$qry" 1 + + # Test OUT_OF_POOL (1) -> false, true, true + qry="select count(subnet_id) from dhcp4_subnet where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = true and subnet_prefix = '192.0.1.0/24'" + run_statement "#4_subnet_out_of_pool" "$qry" 1 + + # Test GLOBAL (2) -> true, false, null + qry="select count(subnet_id) from dhcp4_subnet where reservations_global = true and reservations_in_subnet = false and reservations_out_of_pool is null and subnet_prefix = '192.0.2.0/24'" + run_statement "#4_subnet_global" "$qry" 1 + + # Test ALL (3) -> false, true, false + qry="select count(subnet_id) from dhcp4_subnet where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = false and subnet_prefix = '192.0.3.0/24'" + run_statement "#4_subnet_all" "$qry" 1 + + # Test DISABLED (0) -> false, false, null + qry="select count(id) from dhcp6_shared_network where reservations_global = false and reservations_in_subnet = false and reservations_out_of_pool is null and name = 'test0'" + run_statement "#6_shared_disabled" "$qry" 1 + + # Test OUT_OF_POOL (1) -> false, true, true + qry="select count(id) from dhcp6_shared_network where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = true and name = 'test1'" + run_statement "#6_shared_out_of_pool" "$qry" 1 + + # Test GLOBAL (2) -> true, false, null + qry="select count(id) from dhcp6_shared_network where reservations_global = true and reservations_in_subnet = false and reservations_out_of_pool is null and name = 'test2'" + run_statement "#6_shared_global" "$qry" 1 + + # Test ALL (3) -> false, true, false + qry="select count(id) from dhcp6_shared_network where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = false and name = 'test3'" + run_statement "#6_shared_all" "$qry" 1 + + # Test DISABLED (0) -> false, false, null + qry="select count(subnet_id) from dhcp6_subnet where reservations_global = false and reservations_in_subnet = false and reservations_out_of_pool is null and subnet_prefix = '2001:db8::/64'" + run_statement "#6_subnet_disabled" "$qry" 1 + + # Test OUT_OF_POOL (1) -> false, true, true + qry="select count(subnet_id) from dhcp6_subnet where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = true and subnet_prefix = '2001:db8:1::/64'" + run_statement "#6_subnet_out_of_pool" "$qry" 1 + + # Test GLOBAL (2) -> true, false, null + qry="select count(subnet_id) from dhcp6_subnet where reservations_global = true and reservations_in_subnet = false and reservations_out_of_pool is null and subnet_prefix = '2001:db8:2::/64'" + run_statement "#6_subnet_global" "$qry" 1 + + # Test ALL (3) -> false, true, false + qry="select count(subnet_id) from dhcp6_subnet where reservations_global = false and reservations_in_subnet = true and reservations_out_of_pool = false and subnet_prefix = '2001:db8:3::/64'" + run_statement "#6_subnet_all" "$qry" 1 + + qry="select count(*) from dhcp4_shared_network" + run_statement "#get 4_shared count before update" "$qry" 4 + + qry="select count(*) from dhcp4_subnet" + run_statement "#get 4_subnet count before update" "$qry" 4 + + qry="select count(*) from dhcp6_shared_network" + run_statement "#get 6_shared count before update" "$qry" 4 + + qry="select count(*) from dhcp6_subnet" + run_statement "#get 6_subnet count before update" "$qry" 4 + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Verifies that several tables for holding client classes are created +# and the triggers and stored procedures positioning the client classes +# and validating their dependencies behave correctly. +mysql_client_class_test() { + table_prefix="$1" + + test_start "mysql.client_classes_test.${table_prefix}" + + # Let's wipe the whole database + mysql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which validates client classes and + # dependencies behave correctly + # + # Initialize database to schema 1.0. + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + + # Now upgrade to schema 10.0 that can contain client classes. + mysql_upgrade_schema_to_version 10.0 + + # Insert a new server. + sql=\ +"SET @disable_audit = 1; \ + INSERT INTO ${table_prefix}_server (tag, modification_ts) VALUES ('server1', NOW()); \ + SET @disable_audit = 0" + + run_statement "insert servers" "$sql" + + # Insert client class foo at the top of the hierarchy. It has no dependencies. + sql=\ +"START TRANSACTION; \ + SET @disable_audit = 1; \ + INSERT INTO ${table_prefix}_client_class (name, modification_ts, follow_class_name, depend_on_known_directly) VALUES ('foo', NOW(), NULL, 1); \ + SET @last_id = LAST_INSERT_ID(); \ + INSERT INTO ${table_prefix}_client_class_server (class_id, server_id) \ + VALUES (@last_id, (SELECT id FROM ${table_prefix}_server WHERE tag = 'all')); \ + SET @disable_audit = 0; \ + COMMIT" + run_statement "insert client class foo" "$sql" + + # Insert client class foobar after the foo class. + sql=\ +"START TRANSACTION; \ + SET @disable_audit = 1; \ + INSERT INTO ${table_prefix}_client_class (name, modification_ts, follow_class_name) VALUES ('foobar', NOW(), NULL); \ + SET @last_id = LAST_INSERT_ID(); \ + INSERT INTO ${table_prefix}_client_class_server (class_id, server_id) \ + VALUES (@last_id, (SELECT id FROM ${table_prefix}_server WHERE tag = 'server1')); \ + SET @disable_audit = 0; \ + COMMIT" + run_statement "insert client class foobar" "$sql" + + # Insert the client class bar at the end. This class depends on the client + # class foo. + sql=\ +"START TRANSACTION; \ + SET @disable_audit = 1; \ + INSERT INTO ${table_prefix}_client_class (name, modification_ts, follow_class_name) VALUES ('bar', NOW(), 'foo'); \ + SET @last_id = LAST_INSERT_ID(); \ + INSERT INTO ${table_prefix}_client_class_server (class_id, server_id) \ + VALUES (@last_id, (SELECT id FROM ${table_prefix}_server WHERE tag = 'server1')); \ + INSERT INTO ${table_prefix}_client_class_dependency (class_id, dependency_id) \ + VALUES (@last_id, (SELECT id FROM ${table_prefix}_client_class WHERE name = 'foo')); \ + SET @disable_audit = 0; \ + COMMIT" + run_statement "insert client class bar" "$sql" + + # Ensure that all three classes have been added in the expected order. + sql="SELECT o.order_index FROM ${table_prefix}_client_class AS c \ + INNER JOIN ${table_prefix}_client_class_order AS o \ + ON c.id = o.class_id WHERE c.name = 'foo'" + run_statement "#get order index of class foo" "$sql" 1 + + sql="SELECT o.order_index FROM ${table_prefix}_client_class AS c \ + INNER JOIN ${table_prefix}_client_class_order AS o \ + ON c.id = o.class_id WHERE c.name = 'bar'" + run_statement "#get order index of class bar" "$sql" 2 + + sql="SELECT o.order_index FROM ${table_prefix}_client_class AS c \ + INNER JOIN ${table_prefix}_client_class_order AS o \ + ON c.id = o.class_id WHERE c.name = 'foobar'" + run_statement "#get order index of class foobar" "$sql" 3 + + # Update the class bar moving behind the foobar class. + sql=\ +"START TRANSACTION; \ + SET @disable_audit = 1; \ + UPDATE ${table_prefix}_client_class SET follow_class_name = 'foobar' WHERE name = 'bar'; \ + SET @disable_audit = 0; \ + COMMIT" + run_statement "update client class bar with re-positioning" "$sql" + + # Check that the order of the last two classes was changed. + sql="SELECT o.order_index FROM ${table_prefix}_client_class AS c \ + INNER JOIN ${table_prefix}_client_class_order AS o \ + ON c.id = o.class_id WHERE c.name = 'bar'" + run_statement "#get order index of class bar" "$sql" 4 + + sql="SELECT o.order_index FROM ${table_prefix}_client_class AS c \ + INNER JOIN ${table_prefix}_client_class_order AS o \ + ON c.id = o.class_id WHERE c.name = 'foobar'" + run_statement "#get order index of class foobar" "$sql" 3 + + # Check that the first class is still at the first position. + sql="SELECT o.order_index FROM ${table_prefix}_client_class AS c \ + INNER JOIN ${table_prefix}_client_class_order AS o \ + ON c.id = o.class_id WHERE c.name = 'foo'" + run_statement "#get order index of class foo" "$sql" 1 + + sql=\ +"SET @disable_audit = 1; \ + INSERT INTO ${table_prefix}_options(code, scope_id, dhcp_client_class, modification_ts) \ + VALUES (222, 0, '', now()); \ + SET @disable_audit = 0" + run_statement "add option with an empty dhcp_client class" "$sql" + + # Let's make sure that we can upgrade to version 12.0. This version + # introduces a foreign key between dhcpX_client_class and dhcpX_options + # table. The migration should set the dhcp_client_class to NULL. + mysql_upgrade_schema_to_version 12.0 + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Verifies that the migration 9.6 to 10.0 modifies the length of +# the tag column in the dhcp4_server and dhcp6_server tables. +mysql_shrink_server_tag_test() { + + test_start "mysql.shrink_server_tag_test" + + mysql_wipe + + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + # Now upgrade to schema 9.6. + mysql_upgrade_schema_to_version 9.6 + + # Unfortunately, this schema version already contains 64 character + # long server tags. Let's extend it back, but not to 256 characters + # because it is proven to cause errors in some configurations. + sql=\ +"ALTER TABLE dhcp4_server MODIFY COLUMN tag VARCHAR(128) NOT NULL" + run_statement "extend server DHCPv4 server tag column", "$sql" + + sql=\ +"ALTER TABLE dhcp6_server MODIFY COLUMN tag VARCHAR(128) NOT NULL" + run_statement "extend server DHCPv6 server tag column", "$sql" + + mysql_upgrade_schema_to_version 10.0 + + # Ensure that the migration corrected the lengths. + sql=\ +"SELECT CHARACTER_MAXIMUM_LENGTH \ + FROM INFORMATION_SCHEMA.COLUMNS \ + WHERE TABLE_SCHEMA='${db_name}' AND TABLE_NAME='dhcp4_server' AND COLUMN_NAME='tag'" + run_statement "get new tag column length" "$sql" 64 + + sql=\ +"SELECT CHARACTER_MAXIMUM_LENGTH \ + FROM INFORMATION_SCHEMA.COLUMNS \ + WHERE TABLE_SCHEMA='${db_name}' AND TABLE_NAME='dhcp6_server' AND COLUMN_NAME='tag'" + run_statement "get new tag column length" "$sql" 64 + + mysql_wipe + + test_finish 0 +} + +# Verifies that you can upgrade from earlier version and that initial EMPTY DUID +# (0x00) value in lease6 table is updated to proper value (0x000000). +mysql_update_empty_duid_test() { + test_start "mysql.update_empty_duid_test" + + # Let's wipe the whole database + mysql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which convert empty duid values + # + # Initialize database to schema 1.0. + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + + # Now upgrade to schema 16.0 + mysql_upgrade_schema_to_version 16.0 + + sql=\ +"insert into lease6 values('::10',203,30,(SELECT FROM_UNIXTIME(1642000000)),40,50,1,60,70,1,1,'one.example.com',80,90,16,0,NULL);\ + insert into lease6 values('::11',UNHEX('00'),30,(SELECT FROM_UNIXTIME(1643210000)),40,50,1,60,70,1,1,'',80,90,1,1,'{ }')" + + run_statement "insert v6 leases" "$sql" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + + # leases count for declined state should be 1 with DUID updated (0x000000) + qry="select count(*) from lease6 where address = inet6_aton('::11') and duid = 0x000000 and state = 1" + run_statement "#2" "$qry" 1 + + # leases count for non declined state should be 1 with DUID unchanged (0x323033) + qry="select count(*) from lease6 where address = inet6_aton('::10') and duid = 0x323033 and state = 0" + run_statement "#3" "$qry" 1 + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Verifies that converting from lease6.address to binary column works +# while preserving data. +mysql_update_v6_addresses_to_binary() { + test_start "mysql.update_lease6_address_to_binary" + + # Let's wipe the whole database + mysql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which convert empty duid values + # + # Initialize database to schema 1.0. + mysql -u"${db_user}" -p"${db_password}" "${db_name}" < "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.mysql" + + # Now upgrade to schema 18.0 + mysql_upgrade_schema_to_version 18.0 + + sql=\ +"insert into lease6 (address, lease_type, subnet_id) values('2601:19e:8100:1e10:b1b:51a8:f616:cf14', 1, 1); +insert into lease6 (address, lease_type, subnet_id) values('2601:19e:8100:1e10:b1b:51a8:f616:cf15', 1, 1);" + + run_statement "insert v6 leases" "$sql" + + # Insert ipv6_reservations address is binary. + sql=\ +"insert into hosts(host_id, dhcp_identifier, dhcp_identifier_type) values (18219, '18219', 1); \ +insert into ipv6_reservations (address, prefix_len, type, dhcp6_iaid, host_id) \ + values ('2601:19e:8100:1e10:b1b:51a8:f616:cf16', 128, 1, 123, 18219);" + + run_statement "insert an ipv6 reservation" "$sql" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade mysql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + + # leases count for declined state should be 1 with DUID updated (0x000000) + qry="select count(*) from lease6 where address = inet6_aton('2601:19e:8100:1e10:b1b:51a8:f616:cf14');" + run_statement "#2" "$qry" 1 + + # leases count for non declined state should be 1 with DUID unchanged (0x323033) + qry="select count(*) from lease6 where address = inet6_aton('2601:19e:8100:1e10:b1b:51a8:f616:cf15');" + run_statement "#3" "$qry" 1 + + # verify the reservation is intact + qry="select inet6_ntoa(address) from ipv6_reservations where host_id = 18219;" + run_statement "ipv6_reservations_insert" "$qry" "2601:19e:8100:1e10:b1b:51a8:f616:cf16" + + # Let's wipe the whole database + mysql_wipe + + test_finish 0 +} + +# Run tests. +mysql_db_init_test +mysql_host_reservation_init_test +mysql_db_version_test +mysql_db_version_with_extra_test +mysql_upgrade_test +mysql_lease4_dump_test +mysql_lease4_dump_test -y +mysql_lease6_dump_test +mysql_lease6_dump_test -y +mysql_lease4_upload_test +mysql_lease4_upload_test -y +mysql_lease6_upload_test +mysql_lease6_upload_test -y +mysql_lease4_stat_test +mysql_lease6_stat_test +mysql_lease_stat_upgrade_test +mysql_lease_stat_recount_test +mysql_unused_subnet_id_test +mysql_reservation_mode_upgrade_test +mysql_client_class_test dhcp4 +mysql_client_class_test dhcp6 +mysql_shrink_server_tag_test +mysql_update_empty_duid_test +mysql_update_v6_addresses_to_binary diff --git a/src/bin/admin/tests/pgsql_tests.sh.in b/src/bin/admin/tests/pgsql_tests.sh.in new file mode 100644 index 0000000..f9b839e --- /dev/null +++ b/src/bin/admin/tests/pgsql_tests.sh.in @@ -0,0 +1,2068 @@ +#!/bin/sh + +# 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/. + +# shellcheck disable=SC1091 +# SC1091: Not following: ... was not specified as input (see shellcheck -x). + +# shellcheck disable=SC2154 +# SC2154: ... is referenced but not assigned. +# Reason: some variables are sourced. + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# Include common test library. +. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh" + +# Include admin utilities +. "@abs_top_builddir@/src/bin/admin/admin-utils.sh" + +# Set path to the production schema scripts +db_scripts_dir="@abs_top_srcdir@/src/share/database/scripts" + +# Set location of the kea-admin. +kea_admin="@abs_top_builddir@/src/bin/admin/kea-admin" + +# Convenience function for running an SQL statement +# param hdr - text message to prepend to any error +# param qry - SQL statement to run +# param exp_value - optional expected value. This can be used IF the SQL statement +# generates a single value, such as a SELECT which returns one column for one row. +# Examples: +# +# qry="insert into lease6 (address, lease_type, subnet_id, state) values ($addr,$ltype,1,0)" +# run_statement "#2" "$qry" +# +# qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" +# run_statement "#3" "$qry" 1 +run_statement() { + hdr="$1";shift + qry="$1";shift + exp_value="${1-}" # Optional value. If not given, replace with empty string. + + # Execute the statement + run_command \ + pgsql_execute "${qry}" + value="${OUTPUT}" + + # Execution should succeed + assert_eq 0 "${EXIT_CODE}" "$hdr: SQL=[$qry] failed: (expected status code %d, returned %d)" + + # If there's an expected value, test it + if [ "x$exp_value" != "x" ] + then + assert_str_eq "$exp_value" "$value" "$hdr: SQL=[$qry] wrong: (expected value %s, returned %s)" + fi +} + +# Wipe all tables from the DB: +pgsql_wipe() { + printf "Wiping whole database %s...\n" "${db_name}" + export PGPASSWORD="${db_password}" + + run_command \ + psql --set ON_ERROR_STOP=1 -A -t -q -U keatest -d keatest -f "${db_scripts_dir}/pgsql/dhcpdb_drop.pgsql" + assert_eq 0 "${EXIT_CODE}" "pgsql_wipe drop failed, expected exit code: %d, actual: %d" +} + +pgsql_db_init_test() { + test_start "pgsql.db-init" + + # Let's wipe the whole database + pgsql_wipe + + # Create the database + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init pgsql failed, expected exit code: %d, actual: %d" + + # Verify that all the expected tables exist + + # Check schema_version table + run_command \ + pgsql_execute "SELECT version, minor FROM schema_version" + assert_eq 0 "${EXIT_CODE}" "schema_version table check failed, expected exit code: %d, actual: %d" + + # Check lease4 table + run_command \ + pgsql_execute "SELECT address, hwaddr, client_id, valid_lifetime, expire, subnet_id, fqdn_fwd, fqdn_rev, hostname, state, user_context FROM lease4" + assert_eq 0 "${EXIT_CODE}" "lease4 table check failed, expected exit code: %d, actual: %d" + + # Check lease6 table + run_command \ + pgsql_execute "SELECT address, duid, valid_lifetime, expire, subnet_id, pref_lifetime, lease_type, iaid, prefix_len, fqdn_fwd, fqdn_rev, hostname, state, user_context FROM lease6" + assert_eq 0 "${EXIT_CODE}" "lease6 table check failed, expected exit code: %d, actual: %d" + + # Check lease6_types table + run_command \ + pgsql_execute "SELECT lease_type, name FROM lease6_types" + assert_eq 0 "${EXIT_CODE}" "lease6_types table check failed, expected exit code: %d, actual: %d" + + # Check lease_state table + run_command \ + pgsql_execute "SELECT state, name FROM lease_state" + assert_eq 0 "${EXIT_CODE}" "lease_state table check failed, expected exit code: %d, actual: %d" + + # Trying to create it again should fail. This verifies the db present + # check + printf '\nDB created successfully, make sure we are not allowed to try it again:\n' + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 2 "${EXIT_CODE}" "kea-admin failed to deny db-init, expected exit code: %d, actual: %d" + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +pgsql_db_version_test() { + test_start "pgsql.db-version" + + # Wipe the whole database + pgsql_wipe + + # Do not create any table so db-version will raise an error + printf 'Checking db-version error case...\n' + run_command \ + "${kea_admin}" db-version pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" + assert_eq 3 "${EXIT_CODE}" "schema_version table still exists. (expected %d, exit code %d)" + + # Create the database + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "cannot initialize the database, expected exit code: %d, actual: %d" + + # Verify that kea-admin db-version returns the latest version. + run_command \ + "${kea_admin}" db-version pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" + version="${OUTPUT}" + assert_str_eq "18.0" "${version}" "Expected kea-admin to return %s, returned value was %s" + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +pgsql_upgrade_1_0_to_2_0_test() { + # Added state column to lease4 + run_command \ + pgsql_execute "select state from lease4" + assert_eq 0 "${EXIT_CODE}" "lease4 is missing state column. (expected status code %d, returned %d)" + + # Added state column to lease6 + run_command \ + pgsql_execute "select state from lease6" + assert_eq 0 "${EXIT_CODE}" "lease6 is missing state column. (expected status code %d, returned %d)" + + # Added stored procedures for lease dumps + run_command \ + pgsql_execute "select lease4DumpHeader from lease4DumpHeader()" + assert_eq 0 "${EXIT_CODE}" "function lease4DumpHeader() broken or missing. (expected status code %d, returned %d)" + + run_command \ + pgsql_execute "select address from lease4DumpData()" + assert_eq 0 "${EXIT_CODE}" "function lease4DumpData() broken or missing. (expected status code %d, returned %d)" + + run_command \ + pgsql_execute "select lease6DumpHeader from lease6DumpHeader()" + assert_eq 0 "${EXIT_CODE}" "function lease6DumpHeader() broken or missing. (expected status code %d, returned %d)" + + run_command \ + pgsql_execute "select address from lease6DumpData()" + assert_eq 0 "${EXIT_CODE}" "function lease6DumpData() broken or missing. (expected status code %d, returned %d)" +} + +pgsql_upgrade_2_0_to_3_0_test() { + # Added hwaddr, hwtype, and hwaddr_source columns to lease6 table + run_command \ + pgsql_execute "select hwaddr, hwtype, hwaddr_source from lease6" + assert_eq 0 "${EXIT_CODE}" "lease6 table not upgraded to 3.0 (expected status code %d, returned %d)" + + # Added lease_hwaddr_source table + run_command \ + pgsql_execute "select hwaddr_source, name from lease_hwaddr_source" + assert_eq 0 "${EXIT_CODE}" "lease_hwaddr_source table is missing or broken. (expected status code %d, returned %d)" + + # Added hosts table + run_command \ + pgsql_execute "select host_id, dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, dhcp6_subnet_id, ipv4_address, hostname, dhcp4_client_classes, dhcp6_client_classes, dhcp4_next_server, dhcp4_server_hostname, dhcp4_boot_file_name, auth_key from hosts" + assert_eq 0 "${EXIT_CODE}" "hosts table is missing or broken. (expected status code %d, returned %d)" + + # Added ipv6_reservations table + run_command \ + pgsql_execute "select reservation_id, address, prefix_len, type, dhcp6_iaid, host_id from ipv6_reservations" + assert_eq 0 "${EXIT_CODE}" "ipv6_reservations table is missing or broken. (expected status code %d, returned %d)" + + # Added dhcp4_options table + run_command \ + pgsql_execute "select option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp4_subnet_id, host_id, scope_id from dhcp4_options" + assert_eq 0 "${EXIT_CODE}" "dhcp4_options table is missing or broken. (expected status code %d, returned %d)" + + # Added dhcp6_options table + run_command \ + pgsql_execute "select option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp6_subnet_id, host_id,scope_id from dhcp6_options" + assert_eq 0 "${EXIT_CODE}" "dhcp6_options table is missing or broken. (expected status code %d, returned %d)" + + # Added host_identifier_type table + run_command \ + pgsql_execute "select type, name from host_identifier_type" + assert_eq 0 "${EXIT_CODE}" "host_identifier_type table is missing or broken. (expected status code %d, returned %d)" + + # Added dhcp_option_scope table + run_command \ + pgsql_execute "select scope_id, scope_name from dhcp_option_scope" + assert_eq 0 "${EXIT_CODE}" "dhcp_option_scope table is missing or broken. (expected status code %d, returned %d)" + + # Added dhcp6_options table + run_command \ + pgsql_execute "select option_id, code, value, formatted_value, space, persistent, dhcp_client_class, dhcp6_subnet_id, host_id,scope_id from dhcp6_options" + assert_eq 0 "${EXIT_CODE}" "dhcp6_options table is missing or broken. (expected status code %d, returned %d)" + + # Added order by clause to lease4DumpData + run_command \ + pgsql_execute "select address from lease4DumpData()" + assert_eq 0 "${EXIT_CODE}" "function lease4DumpData() broken or missing. (expected status code %d, returned %d)" + run_command \ + pgsql_execute "\sf lease4DumpData" + assert_eq 0 "${EXIT_CODE}" "\sf of lease4DumpData failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Eci 'order by [a-z]*[\.]?address') || true + assert_eq 1 "${count}" "lease4DumpData is missing order by clause. (expected count %d, returned %d)" + + # Added hwaddr columns to lease6DumpHeader + run_command \ + pgsql_execute "select lease6DumpHeader from lease6DumpHeader()" + assert_eq 0 "${EXIT_CODE}" "function lease6DumpHeader() broken or missing. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Fci 'hwaddr') || true + assert_eq 1 "${count}" "lease6DumpHeader is missing the hwaddr column" + count=$(echo "${OUTPUT}" | grep -Fci 'hwtype') || true + assert_eq 1 "${count}" "lease6DumpHeader is missing the hwtype column" + count=$(echo "${OUTPUT}" | grep -Fci 'hwaddr_source') || true + assert_eq 1 "${count}" "lease6DumpHeader is missing the hwaddr_source column" + + # Added hwaddr columns to lease6DumpData + run_command \ + pgsql_execute "select hwaddr,hwtype,hwaddr_source from lease6DumpData()" + assert_eq 0 "${EXIT_CODE}" "function lease6DumpData() broken or missing. (expected status code %d, returned %d)" + + # Added order by clause to lease6DumpData + run_command \ + pgsql_execute "\sf lease6DumpData" + assert_eq 0 "${EXIT_CODE}" "\sf of lease6DumpData failed. (expected status code %d, returned %d)" + count=$(echo "${OUTPUT}" | grep -Eci 'order by [a-z]*[\.]?address') || true + assert_eq 1 "${count}" "lease6DumpData is missing order by clause. (expected count %d, returned %d)" + + # lease_hardware_source should have row for source = 0 + run_command \ + pgsql_execute "select count(hwaddr_source) from lease_hwaddr_source where hwaddr_source = 0 and name='HWADDR_SOURCE_UNKNOWN'" + assert_eq 0 "${EXIT_CODE}" "select from lease_hwaddr_source failed. (expected status code %d, returned %d)" + assert_eq 1 "${OUTPUT}" "lease_hwaddr_source does not contain entry for HWADDR_SOURCE_UNKNOWN. (record count %d, expected %d)" +} + +pgsql_upgrade_3_0_to_6_1_test() { + # Added user_context to lease4 + run_command \ + pgsql_execute "select user_context from lease4" + assert_eq 0 "${EXIT_CODE}" "lease4 is missing user_context column. (expected status code %d, returned %d)" + + # Added user_context to lease6 + run_command \ + pgsql_execute "select user_context from lease6" + assert_eq 0 "${EXIT_CODE}" "lease6 is missing user_context column. (expected status code %d, returned %d)" + + # Added logs table + run_command \ + pgsql_execute "select timestamp, address, log from logs" + assert_eq 0 "${EXIT_CODE}" "logs table is missing or broken. (expected status code %d, returned %d)" +} + +pgsql_upgrade_6_1_to_6_2_test() { + insert_sql="\ +insert into hosts(dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, ipv4_address) values (decode('010101010101', 'hex'), 0, 1, x'FFAF0002'::int);\ +insert into hosts(dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, ipv4_address) values (decode('010101010102', 'hex'), 0, 1, x'FFAF0002'::int)" + run_command \ + pgsql_execute "$insert_sql" + assert_eq 0 "${EXIT_CODE}" "insert into hosts failed, expected exit code %d, actual %d" +} + +pgsql_upgrade_6_2_to_7_0_test() { + # dhcp4_server should have a single entry for 'all' + select_sql="SELECT id, tag, description, modification_ts from dhcp4_server where id = 1 and tag = 'all'" + run_command \ + pgsql_execute "$select_sql" + assert_eq 0 "${EXIT_CODE}" "the dhcp4_server table is broken or missing. (expected status code %d, returned %d)" + + # dhcp6_server should have a single entry for 'all' + select_sql="SELECT id, tag, description, modification_ts from dhcp6_server where id = 1 and tag = 'all'" + run_command \ + pgsql_execute "$select_sql" + assert_eq 0 "${EXIT_CODE}" "the dhcp6_server table is broken or missing. (expected status code %d, returned %d)" + + # Verify that session variable setting is present and functional. + session_sql="\ +select get_session_value('kea.text'); \ +select set_session_value('kea.text', 'booya'); \ +select get_session_value('kea.text'); \ +select get_session_boolean('kea.bool'); \ +select set_session_value('kea.bool', true); \ +select get_session_boolean('kea.bool'); \ +select get_session_big_int('kea.bigint'); \ +select set_session_value('kea.bigint', cast('1984' as BIGINT)); \ +select get_session_big_int('kea.bigint'); \ +" + run_command \ + pgsql_execute "$session_sql" + assert_eq 0 "${EXIT_CODE}" "session variable handling broken. (expected status code %d, returned %d)" + clean_out=$(echo "${OUTPUT}" | tr '\n' ' ') + assert_str_eq " booya f t 0 1984 " "${clean_out}" "session variable output incorrect" +} + +pgsql_upgrade_7_0_to_8_0_test() { + run_command \ + pgsql_execute "$session_sql" + + # Added class_id to dhcp4_option_def + run_command \ + pgsql_execute "select class_id from dhcp4_option_def" + assert_eq 0 "${EXIT_CODE}" "dhcp4_option_def is missing class_id column. (expected status code %d, returned %d)" + + # Added class_id to dhcp6_option_def + run_command \ + pgsql_execute "select class_id from dhcp6_option_def" + assert_eq 0 "${EXIT_CODE}" "dhcp6_option_def is missing class_id column. (expected status code %d, returned %d)" + + # Added preferred lifetime columns to dhcp6_client_class. + run_command \ + pgsql_execute "select preferred_lifetime, min_preferred_lifetime, max_preferred_lifetime from dhcp6_client_class" + assert_eq 0 "${EXIT_CODE}" "dhcp6_client_class is missing preferred lifetime column(s). (expected status code %d, returned %d)" + + # Check the output of colonSeparatedHex(). + run_command \ + pgsql_execute "SELECT colonSeparatedHex('f123456789')" + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq 'f1:23:45:67:89' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT colonSeparatedHex('')" + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT colonSeparatedHex('f')" + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq '0f' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT colonSeparatedHex('f1')" + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq 'f1' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT colonSeparatedHex('f12')" + assert_eq 0 "${EXIT_CODE}" 'colonSeparatedHex() failed, expected exit code %d, actual %d' + assert_str_eq '0f:12' "${OUTPUT}" + + # Check lease4Dump*(). + run_command \ + pgsql_execute "INSERT INTO lease4 VALUES(10,E'\\\\x3230',E'\\\\x3330',40,TO_TIMESTAMP(1678900000),50,'t','t','one,example,com',0,'{ \"a\": 1, \"b\": 2 }')" + assert_eq 0 "${EXIT_CODE}" 'INSERT INTO lease4 failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT * FROM lease4DumpHeader()" + assert_eq 0 "${EXIT_CODE}" 'lease4DumpHeader() failed, expected exit code %d, actual %d' + assert_str_eq 'address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT * FROM lease4DumpData()" --field-separator=',' + assert_eq 0 "${EXIT_CODE}" 'lease4DumpData() failed, expected exit code %d, actual %d' + assert_str_eq '0.0.0.10,32:30,33:30,40,1678900000,50,1,1,oneˎxampleˌom,0,{ "a": 1, "b": 2 },0' "${OUTPUT}" + + # Check lease6Dump*(). + run_command \ + pgsql_execute "INSERT INTO lease6 VALUES(cast('::10' as inet),E'\\\\x3230',30,TO_TIMESTAMP(1678900000),40,50,1,60,70,'t','t','one,example,com',0,E'\\\\x3830',16,0,'{ \"a\": 1, \"b\": 2 }',0)" + assert_eq 0 "${EXIT_CODE}" 'INSERT INTO lease6 failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT * FROM lease6DumpHeader()" + assert_eq 0 "${EXIT_CODE}" 'lease6DumpHeader() failed, expected exit code %d, actual %d' + assert_str_eq 'address,duid,valid_lifetime,expire,subnet_id,pref_lifetime,lease_type,iaid,prefix_len,fqdn_fwd,fqdn_rev,hostname,hwaddr,state,user_context,hwtype,hwaddr_source,pool_id' "${OUTPUT}" + + run_command \ + pgsql_execute "SELECT * FROM lease6DumpData()" --field-separator=',' + assert_eq 0 "${EXIT_CODE}" 'lease6DumpData() failed, expected exit code %d, actual %d' + assert_str_eq '::10,32:30,30,1678900000,40,50,1,60,70,1,1,oneˎxampleˌom,38:30,0,{ "a": 1, "b": 2 },16,0,0' "${OUTPUT}" + + # Check lease4Upload(). + run_command \ + pgsql_execute "SELECT lease4Upload('192.0.0.0','ff0102030405','01ff0102030405',7200,1234567890,1,0,0,'',0,'',0)" + assert_eq 0 "${EXIT_CODE}" 'lease4Upload() failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" + + # Check lease6Upload(). + run_command \ + pgsql_execute "SELECT lease6Upload('2001:db8::','000100012955cb80ff0102030407',7200,1234567890,1,3600,0,1,128,0,0,'','ff0102030407',0,'',90,16,0)" + assert_eq 0 "${EXIT_CODE}" 'lease6Upload() failed, expected exit code %d, actual %d' + assert_str_eq '' "${OUTPUT}" +} + +pgsql_upgrade_8_0_to_9_0_test() { + run_command \ + pgsql_execute "$session_sql" + + # Most changes are not readily testable without querying the information schema, + # not sure the effort is worthwhile. Verify that function gmt_epoch() was created. + run_command \ + pgsql_execute "select gmt_epoch(now())" + assert_eq 0 "${EXIT_CODE}" "function gmt_epoch() broken or missing. (expected status code %d, returned %d)" +} + +pgsql_upgrade_9_0_to_10_test() { + run_command \ + pgsql_execute "$session_sql" + + # Get function source code so we can check that it returns NEW. + # Function name must be lower case for WHERE clause. + run_command \ + pgsql_execute "select proname,prosrc from pg_proc where proname='func_dhcp6_client_class_check_dependency_bins'" + assert_eq 0 "${EXIT_CODE}" "function func_dhcp6_client_class_check_dependency_BINS() broken or missing. (expected status code %d, returned %d)" + + count=$(echo "${OUTPUT}" | grep -Eci 'RETURN NEW') || true + assert_eq 1 "${count}" "func_dhcp6_client_class_check_dependency_BINS is missing RETURN NEW. (expected count %d, returned %d)" +} + +pgsql_upgrade_10_to_11_test() { + run_command \ + pgsql_execute "$session_sql" + + # Get function source code so we can check that it returns NEW. + # Function name must be lower case for WHERE clause. + run_command \ + pgsql_execute "select proname,prosrc from pg_proc where proname='createoptionauditdhcp6'" + assert_eq 0 "${EXIT_CODE}" "function createOptionAuditDHCP6() broken or missing. (expected status code %d, returned %d)" + + count=$(echo "${OUTPUT}" | grep -Eci 'SELECT dhcp6_pd_pool.subnet_id INTO sid FROM dhcp6_pd_pool WHERE id = pd_pool_id') || true + assert_eq 1 "${count}" "function createOptionAuditDHCP6() is missing changed line. (expected count %d, returned %d)" +} + +pgsql_upgrade_11_to_12_test() { + run_command \ + pgsql_execute "$session_sql" + + # Check function source code + run_command \ + pgsql_execute "select proname,prosrc from pg_proc where proname='func_dhcp4_shared_network_bdel'" + assert_eq 0 "${EXIT_CODE}" "function func_dhcp4_shared_network_BDEL() broken or missing. (expected status code %d, returned %d)" + + count=$(echo "${OUTPUT}" | grep -Eci 'UPDATE dhcp4_subnet SET shared_network_name = NULL') || true + assert_eq 1 "${count}" "function func_dhcp4_shared_network_BDEL() is missing changed line. (expected count %d, returned %d)" + + # Check function source code + run_command \ + pgsql_execute "select proname,prosrc from pg_proc where proname='func_dhcp6_shared_network_bdel'" + assert_eq 0 "${EXIT_CODE}" "function func_dhcp6_shared_network_BDEL() broken or missing. (expected status code %d, returned %d)" + + count=$(echo "${OUTPUT}" | grep -Eci 'UPDATE dhcp6_subnet SET shared_network_name = NULL') || true + assert_eq 1 "${count}" "function func_dhcp6_shared_network_BDEL() is missing changed line. (expected count %d, returned %d)" + + # user_context should have been added to dhcp4_client_class + qry="select user_context from dhcp4_client_class limit 1" + run_command \ + pgsql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # user_context should have been added to dhcp6_client_class + qry="select user_context from dhcp6_client_class limit 1" + run_command \ + pgsql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" +} + +pgsql_upgrade_12_to_13_test() { + # -- lease counting tests -- + + # Clean up. + query="DELETE FROM lease4; DELETE FROM lease6" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + # Populate the lease tables. Also check that @json_supported is NULL at + # first, and then it is set after inserting leases. + run_command \ + pgsql_execute " + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (100,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (101,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (102,1,2, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (103,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (104,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease4 (address, subnet_id, state, user_context) VALUES (105,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::10' as inet),0,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::11' as inet),0,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::12' as inet),0,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::13' as inet),0,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::14' as inet),2,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::15' as inet),2,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::16' as inet),2,1,0, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + INSERT INTO lease6 (address, lease_type, subnet_id, state, user_context) VALUES (cast('::17' as inet),2,1,1, + '{\"ISC\": {\"client-classes\": [\"ALL\", \"KNOWN\", \"bar\", \"foo\"] } }'); + " + assert_eq 0 "${EXIT_CODE}" 'INSERT INTO leases when upgrading from 11 to 12 failed. expected %d, returned %d' + assert_str_eq '' "${OUTPUT}" "INSERT INTO leases when upgrading from 11 to 12 failed. expected output %s, returned %s" + + # Check that @json_supported is NULL by default. + query="SELECT isJsonSupported()" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + json_supported="${OUTPUT}" + if test "${json_supported}" != 'f' && test "${json_supported}" != 't'; then + assert_str_eq '[ft]' "${json_supported}" "${query}. expected '[ft]', returned '${json_supported}'" + fi + + for v in 4 6; do + # Check that client classes were counted correctly. + query="SELECT leases FROM lease${v}_stat_by_client_class WHERE client_class = 'foo' LIMIT 1" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + if test "${json_supported}" = 't'; then + assert_str_eq 2 "${OUTPUT}" "${query}: expected output %s, returned %s" + else + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + # -- Verify some calls to checkLeaseXLimits(). -- + + query="SELECT checkLease${v}Limits('')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{}')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 1 } ] } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 1 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"subnet\": { \"id\": 1, \"address-limit\": 1 } } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 1 for subnet ID 1, current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 2 } ] } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"subnet\": { \"id\": 1, \"address-limit\": 2 } } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for subnet ID 1, current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 4 } ] } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"subnet\": { \"id\": 1, \"address-limit\": 4 } } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 1 }, { \"name\": \"bar\", \"address-limit\": 1 } ], \"subnet\": { \"id\": 1, \"address-limit\": 1 } } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 1 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 2 }, { \"name\": \"bar\", \"address-limit\": 4 } ], \"subnet\": { \"id\": 1, \"address-limit\": 4 } } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for client class \"foo\", current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 4 }, { \"name\": \"bar\", \"address-limit\": 4 } ], \"subnet\": { \"id\": 1, \"address-limit\": 2 } } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq "address limit 2 for subnet ID 1, current lease count 2" "${OUTPUT}" "${query}: expected output %s, returned %s" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + query="SELECT checkLease${v}Limits('{ \"ISC\": { \"limits\": { \"client-classes\": [ { \"name\": \"foo\", \"address-limit\": 4 }, { \"name\": \"bar\", \"address-limit\": 4 } ], \"subnet\": { \"id\": 1, \"address-limit\": 4 } } } }')" + run_command \ + pgsql_execute "${query}" + if test "${json_supported}" = 't'; then + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + else + # Should fail with ERROR: operator does not exist: json -> unknown + assert_eq 3 "${EXIT_CODE}" "${query}: expected %d, returned %d" + fi + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + done + + # Check that leases counters cannot go negative. + for v in 4 6; do + query="SELECT leases FROM lease${v}_stat WHERE subnet_id = 1 AND state = 0 LIMIT 1" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '2' "${OUTPUT}" "${query}: expected output %s, returned %s" + + # Artificially change the subnet counter from 2 down to 1. + query="UPDATE lease${v}_stat SET leases = 1 WHERE subnet_id = 1 AND state = 0" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + if test "${json_supported}" = 't'; then + query="SELECT leases FROM lease${v}_stat_by_client_class WHERE client_class = 'foo' LIMIT 1" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '2' "${OUTPUT}" "${query}: expected output %s, returned %s" + + # Artificially change the client class counter from 2 down to 1. + query="UPDATE lease${v}_stat_by_client_class SET leases = 1 WHERE client_class = 'foo'" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + + # Clean up. + query="DELETE FROM lease${v}" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '' "${OUTPUT}" "${query}: expected output %s, returned %s" + + # SELECT should finish successfully and the subnet counter should be 0. + query="SELECT leases FROM lease${v}_stat WHERE subnet_id = 1 AND state = 0 LIMIT 1" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '0' "${OUTPUT}" "${query}: expected output %s, returned %s" + + if test "${json_supported}" = 't'; then + # SELECT should finish successfully and the client class counter should be 0. + query="SELECT leases FROM lease${v}_stat_by_client_class WHERE client_class = 'foo' LIMIT 1" + run_command \ + pgsql_execute "${query}" + assert_eq 0 "${EXIT_CODE}" "${query}: expected %d, returned %d" + assert_str_eq '0' "${OUTPUT}" "${query}: expected output %s, returned %s" + fi + done +} + +pgsql_upgrade_13_to_14_test() { + run_command \ + pgsql_execute "$session_sql" + + # Added cancelled column to dhcp4_options + run_command \ + pgsql_execute "select cancelled from dhcp4_options" + assert_eq 0 "${EXIT_CODE}" "dhcp4_options is missing cancelled column. (expected status code %d, returned %d)" + + # Added cancelled column to dhcp6_options + run_command \ + pgsql_execute "select cancelled from dhcp6_options" + assert_eq 0 "${EXIT_CODE}" "dhcp6_options is missing cancelled column. (expected status code %d, returned %d)" + + # Check if offer_lifetime was added to dhcp4_shared_network table. + qry="SELECT offer_lifetime from dhcp4_shared_network limit 1" + run_command \ + pgsql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if offer_lifetime was added to dhcp4_subnet table. + qry="SELECT offer_lifetime from dhcp4_subnet limit 1" + run_command \ + pgsql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" + + # Check if offer_lifetime was added to dhcp4_client_class table. + qry="SELECT offer_lifetime from dhcp4_client_class limit 1" + run_command \ + pgsql_execute "${qry}" + assert_eq 0 "${EXIT_CODE}" "${qry}. (expected status code %d, returned %d)" +} + +pgsql_upgrade_14_to_15_test() { + # Added relay_id column to lease4 + run_command \ + pgsql_execute "select relay_id from lease4" + assert_eq 0 "${EXIT_CODE}" "lease4 is missing relay_id column. (expected status code %d, returned %d)" + + # Added remote_id column to lease4 + run_command \ + pgsql_execute "select remote_id from lease4" + assert_eq 0 "${EXIT_CODE}" "lease4 is missing remote_id column. (expected status code %d, returned %d)" +} + +pgsql_upgrade_15_to_16_test() { + # Added allocator column to dhcp4_shared_network + run_command \ + pgsql_execute "select allocator from dhcp4_shared_network" + assert_eq 0 "${EXIT_CODE}" "dhcp4_shared_network is missing allocator column. (expected status code %d, returned %d)" + + # Added allocator column to dhcp6_shared_network + run_command \ + pgsql_execute "select allocator from dhcp6_shared_network" + assert_eq 0 "${EXIT_CODE}" "dhcp6_shared_network is missing allocator column. (expected status code %d, returned %d)" + + # Added pd_allocator column to dhcp6_shared_network + run_command \ + pgsql_execute "select pd_allocator from dhcp6_shared_network" + assert_eq 0 "${EXIT_CODE}" "dhcp6_shared_network is missing pd_allocator column. (expected status code %d, returned %d)" + + # Added allocator column to dhcp4_subnet + run_command \ + pgsql_execute "select allocator from dhcp4_subnet" + assert_eq 0 "${EXIT_CODE}" "dhcp4_subnet is missing allocator column. (expected status code %d, returned %d)" + + # Added allocator column to dhcp6_subnet + run_command \ + pgsql_execute "select allocator from dhcp6_subnet" + assert_eq 0 "${EXIT_CODE}" "dhcp6_subnet is missing allocator column. (expected status code %d, returned %d)" + + # Added pd_allocator column to dhcp6_subnet + run_command \ + pgsql_execute "select pd_allocator from dhcp6_subnet" + assert_eq 0 "${EXIT_CODE}" "dhcp6_subnet is missing pd_allocator column. (expected status code %d, returned %d)" +} + +pgsql_upgrade_16_to_17_test() { + # Added lease4_pool_stat table + run_command \ + pgsql_execute "SELECT subnet_id, pool_id, state, leases FROM lease4_pool_stat" + assert_eq 0 "${EXIT_CODE}" "lease4_pool_stat table is missing or broken. (expected status code %d, returned %d)" + + # Added lease6_pool_stat table + run_command \ + pgsql_execute "SELECT subnet_id, pool_id, lease_type, state, leases FROM lease6_pool_stat" + assert_eq 0 "${EXIT_CODE}" "lease6_pool_stat table is missing or broken. (expected status code %d, returned %d)" + + # Added lease6_relay_id table + run_command \ + pgsql_execute "select extended_info_id, relay_id, lease_addr from lease6_relay_id" + assert_eq 0 "${EXIT_CODE}" "lease6_relay_id table is missing or broken. (expected status code %d, returned %d)" + + # Added lease6_remote_id table + run_command \ + pgsql_execute "select extended_info_id, remote_id, lease_addr from lease6_remote_id" + assert_eq 0 "${EXIT_CODE}" "lease6_remote_id table is missing or broken. (expected status code %d, returned %d)" +} + +pgsql_upgrade_17_to_18_test() { + # Verify that lease6 address is binary. + qry="insert into lease6 (address,duid,prefix_len,lease_type,subnet_id) values(cast('3001::99' as inet),'18219',128,1,0);" + run_statement "lease6_insert" "$qry" + + qry="select host(address) from lease6 where duid = '18219';" + run_statement "lease6_insert" "$qry" "3001::99" + + # Verify that ipv6_reservations address is binary. + qry="\ + insert into hosts(host_id, dhcp_identifier, dhcp_identifier_type) values (18219, '18219', 1); \ + insert into ipv6_reservations (address, prefix_len, type, dhcp6_iaid, host_id) \ + values (cast('3001::99' as inet), 128, 1, 123, 18219); \ + select host(address) from ipv6_reservations where host_id = 18219;" + + run_statement "ipv6_reservations_insert" "$qry" "3001::99" +} + +pgsql_upgrade_test() { + test_start "pgsql.upgrade" + + # Wipe the whole database + pgsql_wipe + + # Initialize database to schema 1.0. + run_command \ + pgsql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.pgsql" + assert_eq 0 "${EXIT_CODE}" "cannot initialize the database, expected exit code: %d, actual: %d" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "db-upgrade failed, expected exit code: %d, actual: %d" + + # Verify upgraded schema reports the latest version. + version=$("${kea_admin}" db-version pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}") + assert_str_eq "18.0" "${version}" 'Expected kea-admin to return %s, returned value was %s' + + # Check 1.0 to 2.0 upgrade + pgsql_upgrade_1_0_to_2_0_test + + # Check 2.0 to 3.0 upgrade + pgsql_upgrade_2_0_to_3_0_test + + # Check 3.0 to 6.1 upgrade + pgsql_upgrade_3_0_to_6_1_test + + # Check 6.1 to 6.2 upgrade + pgsql_upgrade_6_1_to_6_2_test + + # Check 6.2 to 7.0 upgrade + pgsql_upgrade_6_2_to_7_0_test + + # Check 7.0 to 8.0 upgrade + pgsql_upgrade_7_0_to_8_0_test + + # Check 8.0 to 9.0 upgrade + pgsql_upgrade_8_0_to_9_0_test + + # Check 9.0 to 10 upgrade + pgsql_upgrade_9_0_to_10_test + + # Check 10.0 to 11.0 upgrade + pgsql_upgrade_10_to_11_test + + # Check 11.0 to 12.0 upgrade + pgsql_upgrade_11_to_12_test + + # Check 12.0 to 13.0 upgrade + pgsql_upgrade_12_to_13_test + + # Check 13.0 to 14.0 upgrade + pgsql_upgrade_13_to_14_test + + # Check 14.0 to 15.0 upgrade + pgsql_upgrade_14_to_15_test + + # Check 15 to 16 upgrade + pgsql_upgrade_15_to_16_test + + # Check 16 to 17 upgrade + pgsql_upgrade_16_to_17_test + + # Check 17 to 18 upgrade + pgsql_upgrade_17_to_18_test + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Test verifies the ability to dump lease4 data to CSV file +# The dump output file is compared against a reference file. +# If the dump is successful, the file contents will be the +# same. Note that the expire field in the lease4 table +# is of data type "timestamp with timezone". This means that +# the dumped file content is dependent upon the timezone +# setting the PostgreSQL server is using. To account for +# this the reference data contains a tag, "<timestamp>" +# where the expire column's data would normally be. This +# tag is replaced during text execution with a value +# determined by querying the PostgreSQL server. This +# updated reference data is captured in a temporary file +# which is used for the actual comparison. +# May accept additional parameters to be passed to lease-dump. +pgsql_lease4_dump_test() { + test_start "pgsql.lease4_dump_test" + + test_dir="@abs_top_srcdir@/src/bin/admin/tests" + output_dir="@abs_top_builddir@/src/bin/admin/tests" + + output_file="$output_dir/data/pgsql.lease4_dump_test.output.csv" + ref_file="$test_dir/data/lease4_dump_test.reference.csv" + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${output_file}" + touch "${output_file}.tmp" + else + rm -f "${output_file}" + rm -f "${output_file}.tmp" + fi + + # Let's wipe the whole database + pgsql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" + + # Insert the reference records. Normally, for the bytea values, you would have two backslashes. + # Because shell evaluates the double quoted string one more time, they need to be doubled. + # Otherwise, the value is interpreted as ASCII instead of raw bytes. + insert_sql="\ +insert into lease4 values(10,E'\\\\x3230',E'\\\\x3330',40,TO_TIMESTAMP(1642000000),50,'t','t','one.example.com',0,'');\ +insert into lease4 values(11,'',E'\\\\x313233',40,TO_TIMESTAMP(1643210000),50,'t','t','',1,'{ }');\ +insert into lease4 values(12,E'\\\\x3232','',40,TO_TIMESTAMP(1643212345),50,'t','t','three,example,com',2,'{ \"a\": 1, \"b\": \"c\" }')" + + run_command \ + pgsql_execute "$insert_sql" + assert_eq 0 "${EXIT_CODE}" "insert into lease4 failed, expected exit code %d, actual %d" + + # Dump lease4 to output_file + run_command \ + "${kea_admin}" lease-dump pgsql -4 -u "${db_user}" -p "${db_password}" -n "${db_name}" \ + -d "${db_scripts_dir}" -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -4 failed, expected exit code %d, actual %d" + + # Compare the dump output to reference file, they should be identical + run_command \ + cmp -s "${output_file}" "${ref_file}" + assert_eq 0 "${EXIT_CODE}" "dump file does not match reference file, expected exit code %d, actual %d, diff:\n$(diff "${ref_file}" "${output_file}")" + + # Remove the files. + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Test verifies the ability to dump lease6 data to CSV file +# The dump output file is compared against a reference file. +# If the dump is successful, the file contents will be the +# same. Note that the expire field in the lease6 table +# is of data type "timestamp with timezone". This means that +# the dumped file content is dependent upon the timezone +# setting the PostgreSQL server is using. To account for +# this the reference data contains a tag, "<timestamp>" +# where the expire column's data would normally be. This +# tag is replaced during text execution with a value +# determined by querying the PostgreSQL server. This +# updated reference data is captured in a temporary file +# which is used for the actual comparison. +pgsql_lease6_dump_test() { + test_start "pgsql.lease6_dump_test" + + test_dir="@abs_top_srcdir@/src/bin/admin/tests" + output_dir="@abs_top_builddir@/src/bin/admin/tests" + + output_file="$output_dir/data/pgsql.lease6_dump_test.output.csv" + ref_file="$test_dir/data/lease6_dump_test.reference.csv" + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${output_file}" + touch "${output_file}.tmp" + else + rm -f "${output_file}" + rm -f "${output_file}.tmp" + fi + + # Let's wipe the whole database + pgsql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, status code %d" + + # Insert the reference records. Normally, for the bytea values, you would have two backslashes. + # Because shell evaluates the double quoted string one more time, they need to be doubled. + # Otherwise, the value is interpreted as ASCII instead of raw bytes. + insert_sql="\ +insert into lease6 values(cast('::10' as inet),E'\\\\x323033',30,TO_TIMESTAMP(1642000000),40,50,1,60,128,'t','t','one.example.com',0,decode(encode('80','hex'),'hex'),90,16,'',0); \ +insert into lease6 values(cast('::11' as inet),E'\\\\x323133',30,TO_TIMESTAMP(1643210000),40,50,1,60,128,'t','t','',1,decode(encode('80','hex'),'hex'),90,1,'{ }',0); \ +insert into lease6 values(cast('::12' as inet),E'\\\\x323233',30,TO_TIMESTAMP(1643212345),40,50,1,60,128,'t','t','three,example,com',2,decode(encode('80','hex'),'hex'),90,4,'{ \"a\": 1, \"b\": \"c\" }',0)" + + run_command \ + pgsql_execute "$insert_sql" + assert_eq 0 "${EXIT_CODE}" "insert into lease6 failed, expected exit code %d, actual %d" + + # Dump lease6 to output_file + run_command \ + "${kea_admin}" lease-dump pgsql -6 -u "${db_user}" -p "${db_password}" -n "${db_name}" \ + -d "${db_scripts_dir}" -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -6 failed, expected exit code %d, actual %d" + + # Compare the dump output to reference file, they should be identical + run_command \ + cmp -s "${output_file}" "${ref_file}" + assert_eq 0 "${EXIT_CODE}" "dump file does not match reference file, expected exit code %d, actual %d, diff:\n$(diff "${ref_file}" "${output_file}")" + + # Remove the files. + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# May accept additional parameters to be passed to lease-dump or to lease-upload. +pgsql_lease4_upload_test() { + test_start "pgsql.lease4_upload_test" + + input_file="@abs_top_srcdir@/src/bin/admin/tests/data/lease4_dump_test.reference.csv" + input_file_cp="@abs_top_builddir@/src/bin/admin/tests/data/lease4_dump_test.reference.csv" + output_file="@abs_top_builddir@/src/bin/admin/tests/data/lease4_dump_test.output.csv" + + if [ "${input_file}" != "${input_file_cp}" ]; then + cp -f ${input_file} ${input_file_cp} + input_file=${input_file_cp} + input_file_cp="" + fi + + # Wipe the whole database. + pgsql_wipe + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${input_file}.tmp" + touch "${output_file}" + touch "${output_file}.tmp" + else + rm -f "${input_file}.tmp" + rm -f "${output_file}" + rm -f "${output_file}.tmp" + fi + + # Initialize the database. + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" \ + -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" + + # Upload leases. + run_command \ + "${kea_admin}" lease-upload pgsql -4 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -i "${input_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-upload -4 failed, expected exit code %d, actual %d" + + # Dump leases. + run_command \ + "${kea_admin}" lease-dump pgsql -4 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -4 failed, expected exit code %d, actual %d" + + # Compare the initial file used for upload to the file retrieved from dump, they should be identical. + run_command \ + cmp -s "${input_file}" "${output_file}" + assert_eq 0 "${EXIT_CODE}" "file resulted from dump after upload does not match file used for upload, expected exit code %d, actual %d, diff:\n$(diff "${input_file}" "${output_file}")" + + # Remove the files. + if [ "${input_file}" != "${input_file_cp}" ]; then + rm -f "${input_file}" + fi + rm -f "${input_file}.tmp" + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Wipe the whole database. + pgsql_wipe + + test_finish 0 +} + +pgsql_lease6_upload_test() { + test_start "pgsql.lease6_upload_test" + + input_file="@abs_top_srcdir@/src/bin/admin/tests/data/lease6_dump_test.reference.csv" + input_file_cp="@abs_top_builddir@/src/bin/admin/tests/data/lease6_dump_test.reference.csv" + output_file="@abs_top_builddir@/src/bin/admin/tests/data/lease6_dump_test.output.csv" + + if [ "${input_file}" != "${input_file_cp}" ]; then + cp -f ${input_file} ${input_file_cp} + input_file=${input_file_cp} + input_file_cp="" + fi + + # Wipe the whole database. + pgsql_wipe + + # Clean up any test files left from prior failed runs unless -y was provided in which case + # explicitly create the file to check that it will be automatically deleted. + # files should be removed by kea-admin itself. + if printf '%s' "$@" | grep 'y' > /dev/null; then + touch "${input_file}.tmp" + touch "${output_file}" + touch "${output_file}.tmp" + else + rm -f "${input_file}.tmp" + rm -f "${output_file}" + rm -f "${output_file}.tmp" + fi + + # Initialize the database. + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" \ + -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "could not create database, expected exit code %d, actual %d" + + # Upload leases. + run_command \ + "${kea_admin}" lease-upload pgsql -6 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -i "${input_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-upload -6 failed, expected exit code %d, actual %d" + + # Dump leases. + run_command \ + "${kea_admin}" lease-dump pgsql -6 -u "${db_user}" \ + -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" \ + -o "${output_file}" "$@" + assert_eq 0 "${EXIT_CODE}" "kea-admin lease-dump -6 failed, expected exit code %d, actual %d" + + # Compare the initial file used for upload to the file retrieved from dump, they should be identical. + run_command \ + cmp -s "${input_file}" "${output_file}" + assert_eq 0 "${EXIT_CODE}" "file resulted from dump after upload does not match file used for upload, expected exit code %d, actual %d, diff:\n$(diff "${input_file}" "${output_file}")" + + # Remove the files. + if [ "${input_file}" != "${input_file_cp}" ]; then + rm -f "${input_file}" + fi + rm -f "${input_file}.tmp" + rm -f "${output_file}" + rm -f "${output_file}.tmp" + + # Wipe the whole database. + pgsql_wipe + + test_finish 0 +} + +# Upgrades an existing schema to a target newer version +# param target_version - desired schema version as "major.minor" +pgsql_upgrade_schema_to_version() { + target_version=$1 + + upgrade_scripts_dir=${db_scripts_dir}/pgsql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find ${upgrade_scripts_dir} -name 'upgrade*.sh' -type f | wc -l) + if [ "${num_files}" -eq 0 ]; then + upgrade_scripts_dir=@abs_top_builddir@/src/share/database/scripts/pgsql + + # Check if the scripts directory exists at all. + if [ ! -d ${upgrade_scripts_dir} ]; then + log_error "Invalid scripts directory: ${upgrade_scripts_dir}" + exit 1 + fi + + # Check if there are any files in it + num_files=$(find "${upgrade_scripts_dir}" -name 'upgrade*.sh' -type f | wc -l) + fi + + if [ "${num_files}" -eq 0 ]; then + log_error "No scripts in ${upgrade_scripts_dir}?" + exit 1 + fi + + # Postgres psql does not accept pw on command line, but can do it + # thru an env + export PGPASSWORD=$db_password + + for script in "${upgrade_scripts_dir}"/upgrade*.sh + do + version=$(pgsql_version) + if [ "${version}" = "${target_version}" ] + then + break + fi + + echo "Processing $script file..." + "${script}" -U "${db_user}" -d "${db_name}" + done + + echo "Schema upgraded to $version" +} + +# Verifies lease4_stat trigger operations on +# an new, empty database. It inserts, updates, and +# deletes various leases, checking lease4_stat +# values along the way. +pgsql_lease4_stat_test() { + test_start "pgsql.lease4_stat_test" + + # Let's wipe the whole database + pgsql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init pgsql failed, expected %d, returned non-zero status code %d" + + # Verify lease4 stat table is present + qry="select count(subnet_id) from lease4_stat" + run_statement "#1" "$qry" 0 + + # Insert lease4 + qry="insert into lease4 (address, subnet_id, state) values (111,1,0)" + run_statement "#2" "$qry" + + # Assigned state count should be 1 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#3" "$qry" 1 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 0" + run_statement "#4" "$qry" 1 + + # Set lease state to declined + qry="update lease4 set state = 1 where address = 111" + run_statement "#5" "$qry" + + # Leases state count for assigned should be 0 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#6" "$qry" 0 + + # Leases state count for assigned should be 0 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 0" + run_statement "#7" "$qry" 0 + + # Leases state count for declined should be 1 + qry="select leases from lease4_stat where subnet_id = 1 and state = 1" + run_statement "#8" "$qry" 1 + + # Leases state count for declined should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 1" + run_statement "#9" "$qry" 1 + + # Delete the lease + qry="delete from lease4 where address = 111" + run_statement "#10" "$qry" + + # Leases state count for declined should be 0 + qry="select leases from lease4_stat where subnet_id = 1 and state = 1" + run_statement "#11" "$qry" 0 + + # Leases state count for declined should be 0 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 0 and state = 1" + run_statement "#12" "$qry" 0 + + # Insert lease4 + qry="insert into lease4 (address, subnet_id, pool_id, state) values (112,1,1,0)" + run_statement "#13" "$qry" + + # Assigned state count should be 1 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#14" "$qry" 1 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 1 and state = 0" + run_statement "#15" "$qry" 1 + + # Insert lease4 + qry="insert into lease4 (address, subnet_id, pool_id, state) values (113,1,2,0)" + run_statement "#16" "$qry" + + # Assigned state count should be 2 + qry="select leases from lease4_stat where subnet_id = 1 and state = 0" + run_statement "#17" "$qry" 2 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 1 and state = 0" + run_statement "#18" "$qry" 1 + + # Assigned state count should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 1 and pool_id = 2 and state = 0" + run_statement "#19" "$qry" 1 + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Verifies that lease6_stat triggers operate correctly +# for using a given address and lease_type. It will +# insert a lease, update it, and delete checking the +# lease stat counts along the way. It assumes the +# database has been created but is empty. +# param addr - address to use to add to subnet 1 +# param ltype - type of lease to create +pgsql_lease6_stat_per_type() { + addr=$1;shift + addr1=$1;shift + addr2=$1;shift + ltype=$1 + + # insert a lease6 for addr and ltype, state assigned + qry="insert into lease6 (address, lease_type, subnet_id, state) values (cast('$addr' as inet),$ltype,1,0)" + run_statement "#2" "$qry" + + # assigned stat should be 1 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#3" "$qry" 1 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 0" + run_statement "#4" "$qry" 1 + + # update the lease, changing state to declined + qry="update lease6 set state = 1 where address = cast('$addr' as inet)" + run_statement "#5" "$qry" + + # leases stat for assigned state should be 0 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#6" "$qry" 0 + + # leases stat for assigned state should be 0 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 0" + run_statement "#7" "$qry" 0 + + # leases count for declined state should be 1 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 1" + run_statement "#8" "$qry" 1 + + # leases count for declined state should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 1" + run_statement "#9" "$qry" 1 + + # delete the lease + qry="delete from lease6 where address = '$addr'" + run_statement "#10" "$qry" + + # leases count for declined state should be 0 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#11" "$qry" 0 + + # leases count for declined state should be 0 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 0 and state = 0" + run_statement "#12" "$qry" 0 + + # insert a lease6 for addr and ltype, state assigned + qry="insert into lease6 (address, lease_type, subnet_id, pool_id, state) values (cast('$addr1' as inet),$ltype,1,1,0)" + run_statement "#13" "$qry" + + # assigned stat should be 1 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#14" "$qry" 1 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 1 and state = 0" + run_statement "#15" "$qry" 1 + + # insert a lease6 for addr and ltype, state assigned + qry="insert into lease6 (address, lease_type, subnet_id, pool_id, state) values (cast('$addr2' as inet),$ltype,1,2,0)" + run_statement "#16" "$qry" + + # assigned stat should be 2 + qry="select leases from lease6_stat where subnet_id = 1 and lease_type = $ltype and state = 0" + run_statement "#17" "$qry" 2 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 1 and state = 0" + run_statement "#18" "$qry" 1 + + # assigned stat should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 1 and lease_type = $ltype and pool_id = 2 and state = 0" + run_statement "#19" "$qry" 1 +} + +# Verifies that lease6_stat triggers operation correctly +# for both NA and PD lease types, pgsql_lease6_stat_per_type() +pgsql_lease6_stat_test() { + + test_start "pgsql.lease6_stat_test" + + # Let's wipe the whole database + pgsql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init pgsql failed, expected %d, returned non-zero status code %d" + + # verify lease6 stat table is present + qry="select count(subnet_id) from lease6_stat" + run_statement "#1" "$qry" + + # Test for address 111, NA lease type + pgsql_lease6_stat_per_type "::11" "::12" "::13" "0" + + # Test for address 222, PD lease type + pgsql_lease6_stat_per_type "::22" "::23" "::24" "1" + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Verifies that you can upgrade from earlier version and +# lease<4/6>_stat tables will be populated based on existing +# leases and that the stat triggers work properly. +pgsql_lease_stat_upgrade_test() { + test_start "pgsql.lease_stat_upgrade_test" + + # Let's wipe the whole database + pgsql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which prepopulate the lease stat + # tables. + # + # Initialize database to schema 1.0. + pgsql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.pgsql" + assert_eq 0 "${EXIT_CODE}" "cannot initialize 1.0 database, expected exit code: %d, actual: %d" + + # Now upgrade to schema 2.0, this has lease_state in it + pgsql_upgrade_schema_to_version 2.0 + + # Now we need insert some leases to "migrate" for both v4 and v6 + qry=\ +"insert into lease4 (address, subnet_id, state) values (111,10,0);\ + insert into lease4 (address, subnet_id, state) values (222,10,0);\ + insert into lease4 (address, subnet_id, state) values (333,10,1);\ + insert into lease4 (address, subnet_id, state) values (444,10,2);\ + insert into lease4 (address, subnet_id, state) values (555,77,0)" + run_statement "insert v4 leases" "$qry" + + qry=\ +"insert into lease6 (address, lease_type, subnet_id, state) values ('::11',0,40,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::22',0,40,1);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::33',1,40,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::44',1,50,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::55',1,50,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values ('::66',1,40,2)" + run_statement "insert v6 leases" "$qry" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + + # + # First we'll verify lease4_stats are correct after migration. + # + + # Assigned leases for subnet 10 should be 2 + qry="select leases from lease4_stat where subnet_id = 10 and state = 0" + run_statement "#4.1" "$qry" 2 + + # Assigned leases for subnet 10 should be 2 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 0" + run_statement "#4.2" "$qry" 2 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.3" "$qry" 1 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 0 and state = 0" + run_statement "#4.4" "$qry" 1 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_stat where state = 2" + run_statement "#4.5" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_pool_stat where state = 2" + run_statement "#4.6" "$qry" 0 + + # + # Now we'll verify v4 trigger operation for insert, update, and delete + # + + # Insert a new lease subnet 77 + qry="insert into lease4 (address, subnet_id, pool_id, state) values (777,77,1,0)" + run_statement "#4.7" "$qry" + + # Assigned count for subnet 77 should be 2 + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.8" "$qry" 2 + + # Assigned count for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 0 and state = 0" + run_statement "#4.9" "$qry" 1 + + # Assigned count for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 0" + run_statement "#4.10" "$qry" 1 + + # Update the state of the new lease to declined + qry="update lease4 set state = 1 where address = 777" + run_statement "#4.11" "$qry" + + # Assigned count for subnet 77 should be 1 again + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.12" "$qry" 1 + + # Assigned count for subnet 77 should be 1 again + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 0 and state = 0" + run_statement "#4.13" "$qry" 1 + + # Assigned count for subnet 77 should be 0 again + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 0" + run_statement "#4.14" "$qry" 0 + + # Declined count for subnet 77 should be 1 + qry="select leases from lease4_stat where subnet_id = 77 and state = 1" + run_statement "#4.15" "$qry" 1 + + # Declined count for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 1" + run_statement "#4.16" "$qry" 1 + + # Delete the lease. + qry="delete from lease4 where address = 777" + run_statement "#4.17" "$qry" + + # Declined count for subnet 77 should be 0 + qry="select leases from lease4_stat where subnet_id = 77 and state = 1" + run_statement "#4.18" "$qry" 0 + + # Declined count for subnet 77 should be 0 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 1 and state = 1" + run_statement "#4.19" "$qry" 0 + + # + # Next we'll verify lease6_stats are correct after migration. + # + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 0" + run_statement "#6.1" "$qry" 1 + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 0" + run_statement "#6.2" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 1 and state = 0" + run_statement "#6.3" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.4" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 1" + run_statement "#6.5" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 1" + run_statement "#6.6" "$qry" 1 + + # Assigned (PD) leases for subnet 50 should be 2 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.7" "$qry" 2 + + # Assigned (PD) leases for subnet 50 should be 2 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.8" "$qry" 2 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_stat where state = 2" + run_statement "#6.9" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_pool_stat where state = 2" + run_statement "#6.10" "$qry" 0 + + # + # Finally we'll verify v6 trigger operation for insert, update, and delete + # + + # Insert a new lease subnet 50 + qry="insert into lease6 (address, subnet_id, pool_id, lease_type, state) values ('::77',50,1,1,0)" + run_statement "#6.11" "$qry" + + # Assigned count for subnet 50 should be 3 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.12" "$qry" 3 + + # Assigned count for subnet 50 should be 2 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.13" "$qry" 2 + + # Assigned count for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 1 and state = 0" + run_statement "#6.14" "$qry" 1 + + # Update the state of the new lease to expired + qry="update lease6 set state = 2 where address = '::77'" + run_statement "#6.15" "$qry" + + # Assigned count for subnet 50 should be 2 again + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.16" "$qry" 2 + + # Assigned count for subnet 50 should be 0 again + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 1 and state = 0" + run_statement "#6.17" "$qry" 0 + + # Delete another PD lease. + qry="delete from lease6 where address = '::55'" + run_statement "#6.18" "$qry" + + # Assigned leases for subnet 50 should be 1 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.19" "$qry" 1 + + # Assigned leases for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.20" "$qry" 1 + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +pgsql_lease_stat_recount_test() { + test_start "pgsql.lease_stat_recount_test" + + # Let's wipe the whole database + pgsql_wipe + + # Ok, now let's initialize the database + run_command \ + "${kea_admin}" db-init pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + assert_eq 0 "${EXIT_CODE}" "kea-admin db-init pgsql failed, expected %d, returned non-zero status code %d" + + # Now we need insert some leases to "recount" + qry=\ +"insert into lease4 (address, subnet_id, state) values (111,10,0);\ + insert into lease4 (address, subnet_id, pool_id, state) values (222,10,1,0);\ + insert into lease4 (address, subnet_id, state) values (333,10,1);\ + insert into lease4 (address, subnet_id, state) values (444,10,2);\ + insert into lease4 (address, subnet_id, pool_id, state) values (555,77,2,0)" + run_statement "insert v4 leases" "$qry" + + qry=\ +"insert into lease6 (address, lease_type, subnet_id, state) values (cast('::11' as inet),0,40,0);\ + insert into lease6 (address, lease_type, subnet_id, pool_id, state) values (cast('::22' as inet),0,40,1,1);\ + insert into lease6 (address, lease_type, subnet_id, state) values (cast('::33' as inet),1,40,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values (cast('::44' as inet),1,50,0);\ + insert into lease6 (address, lease_type, subnet_id, pool_id, state) values (cast('::55' as inet),1,50,2,0);\ + insert into lease6 (address, lease_type, subnet_id, state) values (cast('::66' as inet),1,40,2)" + run_statement "insert v6 leases" "$qry" + + # Now we change some counters. + qry=\ +"insert into lease4_stat (subnet_id, state, leases) values (20,0,1);\ + update lease4_stat set leases = 5 where subnet_id = 10 and state = 0;\ + delete from lease4_stat where subnet_id = 10 and state = 2" + run_statement "change v4 stats" "$qry" + + qry=\ +"insert into lease4_pool_stat (subnet_id, pool_id, state, leases) values (20,3,0,1);\ + update lease4_pool_stat set leases = 5 where subnet_id = 10 and pool_id = 0 and state = 0;\ + delete from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 2" + run_statement "change v4 stats" "$qry" + + qry=\ +"insert into lease6_stat (subnet_id, lease_type, state, leases) values (20,1,0,1);\ + update lease6_stat set leases = 5 where subnet_id = 40 and lease_type = 0 and state = 0;\ + delete from lease6_stat where subnet_id = 40 and lease_type = 1 and state = 2" + run_statement "change v6 stats" "$qry" + + qry=\ +"insert into lease6_pool_stat (subnet_id, pool_id, lease_type, state, leases) values (20,3,1,0,1);\ + update lease6_pool_stat set leases = 5 where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 0;\ + delete from lease6_pool_stat where subnet_id = 40 and lease_type = 1 and pool_id = 0 and state = 2" + run_statement "change v6 stats" "$qry" + + # Recount all statistics from scratch. + run_command \ + "${kea_admin}" stats-recount pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" + assert_eq 0 "${EXIT_CODE}" "kea-admin stats-recount pgsql failed, expected %d, returned non-zero status code %d" + + # + # First we'll verify lease4_stats are correct after recount. + # + + # Assigned leases for subnet 10 should be 2 + qry="select leases from lease4_stat where subnet_id = 10 and state = 0" + run_statement "#4.1" "$qry" 2 + + # Assigned leases for subnet 10 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 0" + run_statement "#4.2" "$qry" 1 + + # Assigned leases for subnet 10 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 1 and state = 0" + run_statement "#4.3" "$qry" 1 + + # Declined leases for subnet 10 should be 1 + qry="select leases from lease4_stat where subnet_id = 10 and state = 1" + run_statement "#4.4" "$qry" 1 + + # Declined leases for subnet 10 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 10 and pool_id = 0 and state = 0" + run_statement "#4.5" "$qry" 1 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_stat where subnet_id = 77 and state = 0" + run_statement "#4.6" "$qry" 1 + + # Assigned leases for subnet 77 should be 1 + qry="select leases from lease4_pool_stat where subnet_id = 77 and pool_id = 2 and state = 0" + run_statement "#4.7" "$qry" 1 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_stat where state = 2" + run_statement "#4.8" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease4_pool_stat where state = 2" + run_statement "#4.9" "$qry" 0 + + # + # Next we'll verify lease6_stats are correct after recount. + # + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 0" + run_statement "#6.1" "$qry" 1 + + # Assigned leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 0 and state = 0" + run_statement "#6.2" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 1 and state = 0" + run_statement "#6.3" "$qry" 1 + + # Assigned (PD) leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.4" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_stat where subnet_id = 40 and lease_type = 0 and state = 1" + run_statement "#6.5" "$qry" 1 + + # Declined leases for subnet 40 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 40 and lease_type = 0 and pool_id = 1 and state = 1" + run_statement "#6.6" "$qry" 1 + + # Assigned (PD) leases for subnet 50 should be 2 + qry="select leases from lease6_stat where subnet_id = 50 and lease_type = 1 and state = 0" + run_statement "#6.7" "$qry" 2 + + # Assigned (PD) leases for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 0 and state = 0" + run_statement "#6.8" "$qry" 1 + + # Assigned (PD) leases for subnet 50 should be 1 + qry="select leases from lease6_pool_stat where subnet_id = 50 and lease_type = 1 and pool_id = 2 and state = 0" + run_statement "#6.9" "$qry" 1 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_stat where state = 2" + run_statement "#6.10" "$qry" 0 + + # Should be no records for EXPIRED + qry="select count(subnet_id) from lease6_pool_stat where state = 2" + run_statement "#6.11" "$qry" 0 + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Verifies that you can upgrade from an earlier version and +# that unused subnet ID values in hosts and options tables are +# converted to NULL. +pgsql_unused_subnet_id_test() { + test_start "pgsql.unused_subnet_id_test" + + # Let's wipe the whole database + pgsql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which prepopulate the lease stat + # tables. + # + # Initialize database to schema 1.0. + pgsql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.pgsql" + assert_eq 0 "${EXIT_CODE}" "cannot initialize 1.0 database, expected exit code: %d, actual: %d" + + # Now upgrade to schema 4.0 + pgsql_upgrade_schema_to_version 4.0 + + # Now we need insert some hosts to "migrate" for both v4 and v6 + qry=\ +"insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname)\ + values (0, '0123456', 0, 0, 'both'); \ + insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname)\ + values (0, '1123456', 4, 0, 'v4only'); + insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname)\ + values (0, '2123456', 0, 6, 'v6only');\ + insert into hosts (dhcp_identifier_type, dhcp_identifier, dhcp4_subnet_id, dhcp6_subnet_id, hostname) \ + values (0, '3123456', 4, 6, 'neither')" + + run_statement "insert hosts" "$qry" + + # Now we need insert some options to "migrate" for both v4 and v6 + qry=\ +"insert into dhcp4_options (code, dhcp4_subnet_id, scope_id) values (1, 4, 0);\ + insert into dhcp4_options (code, dhcp4_subnet_id, scope_id) values (2, 0, 0);\ + insert into dhcp6_options (code, dhcp6_subnet_id, scope_id) values (1, 6, 0);\ + insert into dhcp6_options (code, dhcp6_subnet_id, scope_id) values (2, 0, 0)" + + run_statement "insert options" "$qry" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + + # Upgrade should succeed + assert_eq 0 "${EXIT_CODE}" "upgrade failed" + + # Two hosts should have null v4 subnet ids + qry="select count(host_id) from hosts where dhcp4_subnet_id is null" + run_statement "#hosts.1" "$qry" 2 + + # Two hosts should have v4 subnet ids = 4 + qry="select count(host_id) from hosts where dhcp4_subnet_id = 4" + run_statement "#hosts.2" "$qry" 2 + + # Two hosts should have null v6 subnet ids + qry="select count(host_id) from hosts where dhcp6_subnet_id is null" + run_statement "#hosts.3" "$qry" 2 + + # Two hosts should should have v6 subnet ids = 6 + qry="select count(host_id) from hosts where dhcp6_subnet_id = 6" + run_statement "#hosts.4" "$qry" 2 + + # One option should have null v4 subnet id + qry="select count(option_id) from dhcp4_options where dhcp4_subnet_id is null" + run_statement "#options.1" "$qry" 1 + + # One option should have v4 subnet id = 4 + qry="select count(option_id) from dhcp4_options where dhcp4_subnet_id = 4" + run_statement "#options.2" "$qry" 1 + + # One option should have null v6 subnet id + qry="select count(option_id) from dhcp6_options where dhcp6_subnet_id is null" + run_statement "#options.3" "$qry" 1 + + # One option should have v4 subnet id = 6 + qry="select count(option_id) from dhcp6_options where dhcp6_subnet_id = 6" + run_statement "#options.4" "$qry" 1 + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Verifies that you can upgrade from earlier version and that initial EMPTY DUID +# (0x00) value in lease6 table is updated to proper value (0x000000). +pgsql_update_empty_duid_test() { + test_start "pgsql.update_empty_duid_test" + + # Let's wipe the whole database + pgsql_wipe + + # We need to create an older database with lease data so we can + # verify the upgrade mechanisms which prepopulate the lease stat + # tables. + # + # Initialize database to schema 1.0. + pgsql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.pgsql" + assert_eq 0 "${EXIT_CODE}" "cannot initialize 1.0 database, expected exit code: %d, actual: %d" + + # Now upgrade to schema 15.0 + pgsql_upgrade_schema_to_version 15.0 + + qry=\ +"insert into lease6 values('::10',E'\\\\x323033',30,TO_TIMESTAMP(1642000000),40,50,1,60,70,'t','t','one.example.com',0,decode(encode('80','hex'),'hex'),90,16,''); \ + insert into lease6 values('::11',E'\\\\x00',30,TO_TIMESTAMP(1643210000),40,50,1,60,70,'t','t','',1,decode(encode('80','hex'),'hex'),90,1,'{ }')" + + run_statement "insert v6 leases" "$qry" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + + # leases count for declined state should be 1 with DUID updated (0x000000) + qry="select count(*) from lease6 where address = '::11' and duid = E'\\\\x000000' and state = 1" + run_statement "#2" "$qry" 1 + + # leases count for non declined state should be 1 with DUID unchanged (0x323033) + qry="select count(*) from lease6 where address = '::10' and duid = E'\\\\x323033' and state = 0" + run_statement "#3" "$qry" 1 + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Verifies that converting from lease6.address to binary column works +# while preserving data. +pgsql_update_v6_addresses_to_binary() { + test_start "pgsql.update_v6_address_to_binary" + + # Let's wipe the whole database + pgsql_wipe + + # Initialize database to schema 1.0. + pgsql_execute_script "@abs_top_srcdir@/src/bin/admin/tests/dhcpdb_create_1.0.pgsql" + assert_eq 0 "${EXIT_CODE}" "cannot initialize 1.0 database, expected exit code: %d, actual: %d" + + # Now upgrade to schema 16.0 + pgsql_upgrade_schema_to_version 16.0 + + sql=\ +"insert into lease6 (address, lease_type, subnet_id) values('2601:19e:8100:1e10:b1b:51a8:f616:cf14', 1, 1); +insert into lease6 (address, lease_type, subnet_id) values('2601:19e:8100:1e10:b1b:51a8:f616:cf15', 1, 1);" + + run_statement "insert v6 leases" "$sql" + + # Insert ipv6_reservations address is binary. + sql=\ +"insert into hosts(host_id, dhcp_identifier, dhcp_identifier_type) values (18219, '18219', 1); \ +insert into ipv6_reservations (address, prefix_len, type, dhcp6_iaid, host_id) \ + values ('2601:19e:8100:1e10:b1b:51a8:f616:cf16', 128, 1, 123, 18219);" + + run_statement "insert an ipv6 reservation" "$sql" + + # Let's upgrade it to the latest version. + run_command \ + "${kea_admin}" db-upgrade pgsql -u "${db_user}" -p "${db_password}" -n "${db_name}" -d "${db_scripts_dir}" + + # leases count for declined state should be 1 with DUID updated (0x000000) + qry="select count(*) from lease6 where address = cast('2601:19e:8100:1e10:b1b:51a8:f616:cf14' as inet);" + run_statement "#2" "$qry" 1 + + # leases count for non declined state should be 1 with DUID unchanged (0x323033) + qry="select count(*) from lease6 where address = cast('2601:19e:8100:1e10:b1b:51a8:f616:cf15' as inet);" + run_statement "#3" "$qry" 1 + + # verify the reservation is intact + qry="select host(address) from ipv6_reservations where host_id = 18219;" + run_statement "ipv6_reservations_insert" "$qry" "2601:19e:8100:1e10:b1b:51a8:f616:cf16" + + # Let's wipe the whole database + pgsql_wipe + + test_finish 0 +} + +# Run tests. +pgsql_db_init_test +pgsql_db_version_test +pgsql_upgrade_test +pgsql_lease4_dump_test +pgsql_lease4_dump_test -y +pgsql_lease6_dump_test +pgsql_lease6_dump_test -y +pgsql_lease4_upload_test +pgsql_lease4_upload_test -y +pgsql_lease6_upload_test +pgsql_lease6_upload_test -y +pgsql_lease4_stat_test +pgsql_lease6_stat_test +pgsql_lease_stat_upgrade_test +pgsql_lease_stat_recount_test +pgsql_unused_subnet_id_test +pgsql_update_empty_duid_test +pgsql_update_v6_addresses_to_binary |