summaryrefslogtreecommitdiffstats
path: root/src/hooks/dhcp/lease_cmds
diff options
context:
space:
mode:
Diffstat (limited to 'src/hooks/dhcp/lease_cmds')
-rw-r--r--src/hooks/dhcp/lease_cmds/Makefile.am88
-rw-r--r--src/hooks/dhcp/lease_cmds/Makefile.in1053
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.cc2308
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.dox110
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds.h593
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc318
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_log.cc18
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_log.h23
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_messages.cc49
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_messages.h25
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes67
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_parser.cc383
-rw-r--r--src/hooks/dhcp/lease_cmds/lease_parser.h100
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/Makefile.am63
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/Makefile.in1072
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/lease_cmds4_unittest.cc3826
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/lease_cmds6_unittest.cc4466
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc101
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h554
-rw-r--r--src/hooks/dhcp/lease_cmds/tests/run_unittests.cc19
-rw-r--r--src/hooks/dhcp/lease_cmds/version.cc17
21 files changed, 15253 insertions, 0 deletions
diff --git a/src/hooks/dhcp/lease_cmds/Makefile.am b/src/hooks/dhcp/lease_cmds/Makefile.am
new file mode 100644
index 0000000..65f9342
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/Makefile.am
@@ -0,0 +1,88 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Ensure that the message file and doxygen file is included in the distribution
+EXTRA_DIST = lease_cmds_messages.mes
+EXTRA_DIST += lease_cmds.dox
+
+CLEANFILES = *.gcno *.gcda
+
+# convenience archive
+
+noinst_LTLIBRARIES = liblease_cmds.la
+
+liblease_cmds_la_SOURCES = lease_cmds.cc lease_cmds.h
+liblease_cmds_la_SOURCES += lease_cmds_callouts.cc
+liblease_cmds_la_SOURCES += lease_parser.h lease_parser.cc
+liblease_cmds_la_SOURCES += lease_cmds_log.cc lease_cmds_log.h
+liblease_cmds_la_SOURCES += lease_cmds_messages.cc lease_cmds_messages.h
+liblease_cmds_la_SOURCES += version.cc
+
+liblease_cmds_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblease_cmds_la_CPPFLAGS = $(AM_CPPFLAGS)
+
+# install the shared object into $(libdir)/kea/hooks
+lib_hooksdir = $(libdir)/kea/hooks
+lib_hooks_LTLIBRARIES = libdhcp_lease_cmds.la
+
+libdhcp_lease_cmds_la_SOURCES =
+libdhcp_lease_cmds_la_LDFLAGS = $(AM_LDFLAGS)
+libdhcp_lease_cmds_la_LDFLAGS += -avoid-version -export-dynamic -module
+libdhcp_lease_cmds_la_LIBADD = liblease_cmds.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/process/libkea-process.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/eval/libkea-eval.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/stats/libkea-stats.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/http/libkea-http.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/database/libkea-database.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/util/libkea-util.la
+libdhcp_lease_cmds_la_LIBADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+libdhcp_lease_cmds_la_LIBADD += $(LOG4CPLUS_LIBS)
+libdhcp_lease_cmds_la_LIBADD += $(CRYPTO_LIBS)
+libdhcp_lease_cmds_la_LIBADD += $(BOOST_LIBS)
+
+# If we want to get rid of all generated messages files, we need to use
+# make maintainer-clean. The proper way to introduce custom commands for
+# that operation is to define maintainer-clean-local target. However,
+# make maintainer-clean also removes Makefile, so running configure script
+# is required. To make it easy to rebuild messages without going through
+# reconfigure, a new target messages-clean has been added.
+maintainer-clean-local:
+ rm -f lease_cmds_messages.h lease_cmds_messages.cc
+
+# To regenerate messages files, one can do:
+#
+# make messages-clean
+# make messages
+#
+# This is needed only when a .mes file is modified.
+messages-clean: maintainer-clean-local
+
+if GENERATE_MESSAGES
+
+# Define rule to build logging source files from message file
+messages: lease_cmds_messages.h lease_cmds_messages.cc
+ @echo Message files regenerated
+
+lease_cmds_messages.h lease_cmds_messages.cc: lease_cmds_messages.mes
+ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
+
+else
+
+messages lease_cmds_messages.h lease_cmds_messages.cc:
+ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+endif
+
diff --git a/src/hooks/dhcp/lease_cmds/Makefile.in b/src/hooks/dhcp/lease_cmds/Makefile.in
new file mode 100644
index 0000000..a369989
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/Makefile.in
@@ -0,0 +1,1053 @@
+# 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/hooks/dhcp/lease_cmds
+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_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_sysrepo.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__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)$(lib_hooksdir)"
+LTLIBRARIES = $(lib_hooks_LTLIBRARIES) $(noinst_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libdhcp_lease_cmds_la_DEPENDENCIES = liblease_cmds.la \
+ $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \
+ $(top_builddir)/src/lib/process/libkea-process.la \
+ $(top_builddir)/src/lib/eval/libkea-eval.la \
+ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+ $(top_builddir)/src/lib/stats/libkea-stats.la \
+ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+ $(top_builddir)/src/lib/http/libkea-http.la \
+ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+ $(top_builddir)/src/lib/database/libkea-database.la \
+ $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+ $(top_builddir)/src/lib/log/libkea-log.la \
+ $(top_builddir)/src/lib/util/libkea-util.la \
+ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_libdhcp_lease_cmds_la_OBJECTS =
+libdhcp_lease_cmds_la_OBJECTS = $(am_libdhcp_lease_cmds_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libdhcp_lease_cmds_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libdhcp_lease_cmds_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+liblease_cmds_la_LIBADD =
+am_liblease_cmds_la_OBJECTS = liblease_cmds_la-lease_cmds.lo \
+ liblease_cmds_la-lease_cmds_callouts.lo \
+ liblease_cmds_la-lease_parser.lo \
+ liblease_cmds_la-lease_cmds_log.lo \
+ liblease_cmds_la-lease_cmds_messages.lo \
+ liblease_cmds_la-version.lo
+liblease_cmds_la_OBJECTS = $(am_liblease_cmds_la_OBJECTS)
+liblease_cmds_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/liblease_cmds_la-lease_cmds.Plo \
+ ./$(DEPDIR)/liblease_cmds_la-lease_cmds_callouts.Plo \
+ ./$(DEPDIR)/liblease_cmds_la-lease_cmds_log.Plo \
+ ./$(DEPDIR)/liblease_cmds_la-lease_cmds_messages.Plo \
+ ./$(DEPDIR)/liblease_cmds_la-lease_parser.Plo \
+ ./$(DEPDIR)/liblease_cmds_la-version.Plo
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(libdhcp_lease_cmds_la_SOURCES) $(liblease_cmds_la_SOURCES)
+DIST_SOURCES = $(libdhcp_lease_cmds_la_SOURCES) \
+ $(liblease_cmds_la_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_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_SYSREPO = @HAVE_SYSREPO@
+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@
+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_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+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
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+# Ensure that the message file and doxygen file is included in the distribution
+EXTRA_DIST = lease_cmds_messages.mes lease_cmds.dox
+CLEANFILES = *.gcno *.gcda
+
+# convenience archive
+noinst_LTLIBRARIES = liblease_cmds.la
+liblease_cmds_la_SOURCES = lease_cmds.cc lease_cmds.h \
+ lease_cmds_callouts.cc lease_parser.h lease_parser.cc \
+ lease_cmds_log.cc lease_cmds_log.h lease_cmds_messages.cc \
+ lease_cmds_messages.h version.cc
+liblease_cmds_la_CXXFLAGS = $(AM_CXXFLAGS)
+liblease_cmds_la_CPPFLAGS = $(AM_CPPFLAGS)
+
+# install the shared object into $(libdir)/kea/hooks
+lib_hooksdir = $(libdir)/kea/hooks
+lib_hooks_LTLIBRARIES = libdhcp_lease_cmds.la
+libdhcp_lease_cmds_la_SOURCES =
+libdhcp_lease_cmds_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version \
+ -export-dynamic -module
+libdhcp_lease_cmds_la_LIBADD = liblease_cmds.la \
+ $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \
+ $(top_builddir)/src/lib/process/libkea-process.la \
+ $(top_builddir)/src/lib/eval/libkea-eval.la \
+ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+ $(top_builddir)/src/lib/stats/libkea-stats.la \
+ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+ $(top_builddir)/src/lib/http/libkea-http.la \
+ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+ $(top_builddir)/src/lib/database/libkea-database.la \
+ $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+ $(top_builddir)/src/lib/log/libkea-log.la \
+ $(top_builddir)/src/lib/util/libkea-util.la \
+ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/hooks/dhcp/lease_cmds/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/hooks/dhcp/lease_cmds/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-lib_hooksLTLIBRARIES: $(lib_hooks_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_hooks_LTLIBRARIES)'; test -n "$(lib_hooksdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(lib_hooksdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(lib_hooksdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(lib_hooksdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(lib_hooksdir)"; \
+ }
+
+uninstall-lib_hooksLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_hooks_LTLIBRARIES)'; test -n "$(lib_hooksdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(lib_hooksdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(lib_hooksdir)/$$f"; \
+ done
+
+clean-lib_hooksLTLIBRARIES:
+ -test -z "$(lib_hooks_LTLIBRARIES)" || rm -f $(lib_hooks_LTLIBRARIES)
+ @list='$(lib_hooks_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libdhcp_lease_cmds.la: $(libdhcp_lease_cmds_la_OBJECTS) $(libdhcp_lease_cmds_la_DEPENDENCIES) $(EXTRA_libdhcp_lease_cmds_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libdhcp_lease_cmds_la_LINK) -rpath $(lib_hooksdir) $(libdhcp_lease_cmds_la_OBJECTS) $(libdhcp_lease_cmds_la_LIBADD) $(LIBS)
+
+liblease_cmds.la: $(liblease_cmds_la_OBJECTS) $(liblease_cmds_la_DEPENDENCIES) $(EXTRA_liblease_cmds_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(liblease_cmds_la_LINK) $(liblease_cmds_la_OBJECTS) $(liblease_cmds_la_LIBADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblease_cmds_la-lease_cmds.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblease_cmds_la-lease_cmds_callouts.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblease_cmds_la-lease_cmds_log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblease_cmds_la-lease_cmds_messages.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblease_cmds_la-lease_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liblease_cmds_la-version.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+liblease_cmds_la-lease_cmds.lo: lease_cmds.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -MT liblease_cmds_la-lease_cmds.lo -MD -MP -MF $(DEPDIR)/liblease_cmds_la-lease_cmds.Tpo -c -o liblease_cmds_la-lease_cmds.lo `test -f 'lease_cmds.cc' || echo '$(srcdir)/'`lease_cmds.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblease_cmds_la-lease_cmds.Tpo $(DEPDIR)/liblease_cmds_la-lease_cmds.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds.cc' object='liblease_cmds_la-lease_cmds.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -c -o liblease_cmds_la-lease_cmds.lo `test -f 'lease_cmds.cc' || echo '$(srcdir)/'`lease_cmds.cc
+
+liblease_cmds_la-lease_cmds_callouts.lo: lease_cmds_callouts.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -MT liblease_cmds_la-lease_cmds_callouts.lo -MD -MP -MF $(DEPDIR)/liblease_cmds_la-lease_cmds_callouts.Tpo -c -o liblease_cmds_la-lease_cmds_callouts.lo `test -f 'lease_cmds_callouts.cc' || echo '$(srcdir)/'`lease_cmds_callouts.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblease_cmds_la-lease_cmds_callouts.Tpo $(DEPDIR)/liblease_cmds_la-lease_cmds_callouts.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds_callouts.cc' object='liblease_cmds_la-lease_cmds_callouts.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -c -o liblease_cmds_la-lease_cmds_callouts.lo `test -f 'lease_cmds_callouts.cc' || echo '$(srcdir)/'`lease_cmds_callouts.cc
+
+liblease_cmds_la-lease_parser.lo: lease_parser.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -MT liblease_cmds_la-lease_parser.lo -MD -MP -MF $(DEPDIR)/liblease_cmds_la-lease_parser.Tpo -c -o liblease_cmds_la-lease_parser.lo `test -f 'lease_parser.cc' || echo '$(srcdir)/'`lease_parser.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblease_cmds_la-lease_parser.Tpo $(DEPDIR)/liblease_cmds_la-lease_parser.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_parser.cc' object='liblease_cmds_la-lease_parser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -c -o liblease_cmds_la-lease_parser.lo `test -f 'lease_parser.cc' || echo '$(srcdir)/'`lease_parser.cc
+
+liblease_cmds_la-lease_cmds_log.lo: lease_cmds_log.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -MT liblease_cmds_la-lease_cmds_log.lo -MD -MP -MF $(DEPDIR)/liblease_cmds_la-lease_cmds_log.Tpo -c -o liblease_cmds_la-lease_cmds_log.lo `test -f 'lease_cmds_log.cc' || echo '$(srcdir)/'`lease_cmds_log.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblease_cmds_la-lease_cmds_log.Tpo $(DEPDIR)/liblease_cmds_la-lease_cmds_log.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds_log.cc' object='liblease_cmds_la-lease_cmds_log.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -c -o liblease_cmds_la-lease_cmds_log.lo `test -f 'lease_cmds_log.cc' || echo '$(srcdir)/'`lease_cmds_log.cc
+
+liblease_cmds_la-lease_cmds_messages.lo: lease_cmds_messages.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -MT liblease_cmds_la-lease_cmds_messages.lo -MD -MP -MF $(DEPDIR)/liblease_cmds_la-lease_cmds_messages.Tpo -c -o liblease_cmds_la-lease_cmds_messages.lo `test -f 'lease_cmds_messages.cc' || echo '$(srcdir)/'`lease_cmds_messages.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblease_cmds_la-lease_cmds_messages.Tpo $(DEPDIR)/liblease_cmds_la-lease_cmds_messages.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds_messages.cc' object='liblease_cmds_la-lease_cmds_messages.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -c -o liblease_cmds_la-lease_cmds_messages.lo `test -f 'lease_cmds_messages.cc' || echo '$(srcdir)/'`lease_cmds_messages.cc
+
+liblease_cmds_la-version.lo: version.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -MT liblease_cmds_la-version.lo -MD -MP -MF $(DEPDIR)/liblease_cmds_la-version.Tpo -c -o liblease_cmds_la-version.lo `test -f 'version.cc' || echo '$(srcdir)/'`version.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/liblease_cmds_la-version.Tpo $(DEPDIR)/liblease_cmds_la-version.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='version.cc' object='liblease_cmds_la-version.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liblease_cmds_la_CPPFLAGS) $(CPPFLAGS) $(liblease_cmds_la_CXXFLAGS) $(CXXFLAGS) -c -o liblease_cmds_la-version.lo `test -f 'version.cc' || echo '$(srcdir)/'`version.cc
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(LTLIBRARIES)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(lib_hooksdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-lib_hooksLTLIBRARIES clean-libtool \
+ clean-noinstLTLIBRARIES mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds_callouts.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds_log.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds_messages.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_parser.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-version.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am: install-lib_hooksLTLIBRARIES
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds_callouts.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds_log.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_cmds_messages.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-lease_parser.Plo
+ -rm -f ./$(DEPDIR)/liblease_cmds_la-version.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic \
+ maintainer-clean-local
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-lib_hooksLTLIBRARIES
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic \
+ clean-lib_hooksLTLIBRARIES clean-libtool \
+ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-lib_hooksLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic \
+ maintainer-clean-local mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am \
+ uninstall-lib_hooksLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+# If we want to get rid of all generated messages files, we need to use
+# make maintainer-clean. The proper way to introduce custom commands for
+# that operation is to define maintainer-clean-local target. However,
+# make maintainer-clean also removes Makefile, so running configure script
+# is required. To make it easy to rebuild messages without going through
+# reconfigure, a new target messages-clean has been added.
+maintainer-clean-local:
+ rm -f lease_cmds_messages.h lease_cmds_messages.cc
+
+# To regenerate messages files, one can do:
+#
+# make messages-clean
+# make messages
+#
+# This is needed only when a .mes file is modified.
+messages-clean: maintainer-clean-local
+
+# Define rule to build logging source files from message file
+@GENERATE_MESSAGES_TRUE@messages: lease_cmds_messages.h lease_cmds_messages.cc
+@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated
+
+@GENERATE_MESSAGES_TRUE@lease_cmds_messages.h lease_cmds_messages.cc: lease_cmds_messages.mes
+@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
+
+@GENERATE_MESSAGES_FALSE@messages lease_cmds_messages.h lease_cmds_messages.cc:
+@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.cc b/src/hooks/dhcp/lease_cmds/lease_cmds.cc
new file mode 100644
index 0000000..f4e8935
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds.cc
@@ -0,0 +1,2308 @@
+// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <config/command_mgr.h>
+#include <config/cmds_impl.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <asiolink/io_address.h>
+#include <database/db_exceptions.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/dhcpsrv_exceptions.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/ncr_generator.h>
+#include <dhcpsrv/resource_handler.h>
+#include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/sanity_checker.h>
+#include <dhcp/duid.h>
+#include <hooks/hooks.h>
+#include <exceptions/exceptions.h>
+#include <lease_cmds.h>
+#include <lease_parser.h>
+#include <lease_cmds_log.h>
+#include <stats/stats_mgr.h>
+#include <util/encode/hex.h>
+#include <util/multi_threading_mgr.h>
+#include <util/strutil.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/algorithm/string.hpp>
+#include <string>
+#include <sstream>
+
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::dhcp_ddns;
+using namespace isc::config;
+using namespace isc::asiolink;
+using namespace isc::hooks;
+using namespace isc::stats;
+using namespace isc::util;
+using namespace std;
+
+namespace isc {
+namespace lease_cmds {
+
+/// @brief Wrapper class around reservation command handlers.
+class LeaseCmdsImpl : private CmdsImpl {
+public:
+
+ /// @brief Parameters specified for lease commands.
+ class Parameters {
+ public:
+
+ /// @brief specifies type of query (by IP addr, by hwaddr, by DUID)
+ typedef enum {
+ TYPE_ADDR, ///< query by IP address (either v4 or v6)
+ TYPE_HWADDR, ///< query by hardware address (v4 only)
+ TYPE_DUID, ///< query by DUID (v6 only)
+ TYPE_CLIENT_ID ///< query by client identifier (v4 only).
+ } Type;
+
+ /// @brief Specifies subnet-id (always used)
+ SubnetID subnet_id;
+
+ /// @brief Specifies IPv4/v6 address (used when query_type is TYPE_ADDR)
+ IOAddress addr;
+
+ /// @brief Specifies hardware address (used when query_type is TYPE_HWADDR)
+ HWAddrPtr hwaddr;
+
+ /// @brief Specifies identifier value (used when query_type is TYPE_DUID)
+ isc::dhcp::DuidPtr duid;
+
+ /// @brief Specifies identifier value (used when query_type is TYPE_CLIENT_ID)
+ isc::dhcp::ClientIdPtr client_id;
+
+ /// @brief Attempts to covert text to one of specified types
+ ///
+ /// Supported values are: "address", hw-address and duid.
+ ///
+ /// @param txt text to be converted
+ ///
+ /// @return value converted to Parameters::Type
+ ///
+ /// @throw BadValue if unsupported type is specified
+ static Type txtToType(const std::string& txt) {
+ if (txt == "address") {
+ return (Parameters::TYPE_ADDR);
+ } else if (txt == "hw-address") {
+ return (Parameters::TYPE_HWADDR);
+ } else if (txt == "duid") {
+ return (Parameters::TYPE_DUID);
+ } else if (txt == "client-id") {
+ return (Parameters::TYPE_CLIENT_ID);
+ } else {
+ isc_throw(BadValue, "Incorrect identifier type: "
+ << txt << ", the only supported values are: "
+ "address, hw-address, duid");
+ }
+ }
+
+ /// @brief specifies parameter types
+ Type query_type;
+
+ /// @brief Lease type (NA,TA or PD) used for v6 leases
+ Lease::Type lease_type;
+
+ /// @brief IAID identifier used for v6 leases
+ uint32_t iaid;
+
+ /// @brief Indicates whether or not DNS should be updated.
+ bool updateDDNS;
+
+ /// @brief Default constructor.
+ Parameters()
+ : subnet_id(0), addr("::"), query_type(TYPE_ADDR),
+ lease_type(Lease::TYPE_NA), iaid(0), updateDDNS(false) {
+ }
+ };
+
+public:
+
+ /// @brief lease4-add, lease6-add command handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::leaseAddHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// add command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ leaseAddHandler(CalloutHandle& handle);
+
+ /// @brief lease6-bulk-apply command handler
+ ///
+ /// Provides the implementation for the
+ /// @ref isc::lease_cmds::LeaseCmds::lease6BulkApplyHandler.
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// add command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ lease6BulkApplyHandler(CalloutHandle& handle);
+
+ /// @brief lease4-get, lease6-get command handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::leaseGetHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ leaseGetHandler(CalloutHandle& handle);
+
+ /// @brief lease4-get-all, lease6-get-all commands handler
+ ///
+ /// These commands attempt to retrieve all IPv4 or IPv6 leases,
+ /// or all IPv4 or all IPv6 leases belonging to the particular
+ /// subnets. If no subnet identifiers are provided, it returns all
+ /// IPv4 or IPv6 leases from the database.
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise.
+ int
+ leaseGetAllHandler(CalloutHandle& handle);
+
+ /// @brief lease4-get-page, lease6-get-page commands handler
+ ///
+ /// These commands attempt to retrieve 1 page of leases. The maximum size
+ /// of the page is specified by the caller. The caller also specifies
+ /// the last address returned in the previous page. The new page
+ /// starts from the first address following the address specified by
+ /// the caller. If the first page should be returned the IPv4
+ /// zero address, IPv6 zero address or the keyword "start" should
+ /// be provided instead of the last address.
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get commands JSON text in the "command" argument.
+ ///
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetPageHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-by-hw-address command handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::leaseGetByHwAddressHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ ///
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByHwAddressHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-by-client-id command handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::leaseGetByClientIdHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ ///
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByClientIdHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease6-get-by-duid command handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::leaseGetByDuidHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ ///
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByDuidHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-by-hostname and lease6-get-by-hostname commands
+ /// handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::leaseGetByHostnameHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ ///
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByHostnameHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-del command handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease4DelHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// delete command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ lease4DelHandler(CalloutHandle& handle);
+
+ /// @brief lease6-del command handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease6DelHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// delete command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ lease6DelHandler(CalloutHandle& handle);
+
+ /// @brief lease4-update handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease4UpdateHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// update command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ lease4UpdateHandler(CalloutHandle& handle);
+
+ /// @brief lease6-update handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease6UpdateHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// update command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ lease6UpdateHandler(CalloutHandle& handle);
+
+ /// @brief lease4-wipe handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease4WipeHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// wipe command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ lease4WipeHandler(CalloutHandle& handle);
+
+ /// @brief lease6-wipe handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease6WipeHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// wipe command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int
+ lease6WipeHandler(CalloutHandle& handle);
+
+ /// @brief lease4-resend-ddns handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease4ResendDdnsHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// lease4-resend-ddns command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int lease4ResendDdnsHandler(CalloutHandle& handle);
+
+ /// @brief lease6-resend-ddns handler
+ ///
+ /// Provides the implementation for @ref isc::lease_cmds::LeaseCmds::lease6ResendDdnsHandler
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// lease6-resend-ddns command JSON text in the "command" argument
+ ///
+ /// @return 0 upon success, non-zero otherwise
+ int lease6ResendDdnsHandler(CalloutHandle& handle);
+
+ /// @brief Extracts parameters required for reservation-get and reservation-del
+ ///
+ /// See @ref Parameters class for detailed description of what is expected
+ /// in the args structure.
+ ///
+ /// @param v6 whether addresses allowed are v4 (false) or v6 (true)
+ /// @param args arguments passed to command
+ ///
+ /// @return parsed parameters
+ ///
+ /// @throw BadValue if input arguments don't make sense.
+ Parameters getParameters(bool v6, const ConstElementPtr& args);
+
+ /// @brief Convenience function fetching IPv6 address to be used to
+ /// delete a lease.
+ ///
+ /// The returned parameter depends on the @c query_type value stored
+ /// in the passed object. Note that the HW address is not allowed and
+ /// this query type results in an exception. If the query type is of
+ /// the address type, the address is returned. If the type is set to
+ /// DUID, this function will try to find the lease for this DUID
+ /// and return the corresponding address.
+ ///
+ /// @param parameters parameters extracted from the command.
+ ///
+ /// @return Lease of the lease to be deleted.
+ ///
+ /// @throw InvalidParameter if the DUID is not found when needed to
+ /// find the lease or if the query type is by HW address.
+ /// @throw InvalidOperation if the query type is unknown.
+ Lease6Ptr getIPv6LeaseForDelete(const Parameters& parameters) const;
+
+ /// @brief Returns a map holding brief information about a lease which
+ /// failed to be deleted, updated or added.
+ ///
+ /// The DUID is only included if it is non-null. The address is only
+ /// included if it is non-zero.
+ ///
+ /// @param lease_type lease type.
+ /// @param lease_address lease address.
+ /// @param duid DUID of the client.
+ /// @param control_result Control result: "empty" of the lease was
+ /// not found, "error" otherwise.
+ /// @param error_message Error message.
+ ///
+ /// @return The JSON map of the failed leases.
+ ElementPtr createFailedLeaseMap(const Lease::Type& lease_type,
+ const IOAddress& lease_address,
+ const DuidPtr& duid,
+ const int control_result,
+ const std::string& error_message) const;
+
+ /// @brief Fetches an IP address parameter from a map of parameters
+ ///
+ /// @param map of parameters in which to look
+ /// @name name of the parameter desired
+ /// @param family expected protocol family of the address parameter,
+ /// AF_INET or AF_INET6
+ ///
+ /// @return IOAddress containing the value of the parameter.
+ ///
+ /// @throw BadValue if the parameter is missing or invalid
+ IOAddress getAddressParam(ConstElementPtr params, const std::string name,
+ short family = AF_INET) const;
+
+ /// @brief Update stats when adding lease.
+ ///
+ /// @param lease Added lease.
+ static void updateStatsOnAdd(const Lease4Ptr& lease);
+
+ /// @brief Update stats when adding lease.
+ ///
+ /// @param lease Added lease.
+ static void updateStatsOnAdd(const Lease6Ptr& lease);
+
+ /// @brief Update stats when updating lease.
+ ///
+ /// @param existing Lease data before update.
+ /// @param lease Lease data after update.
+ static void updateStatsOnUpdate(const Lease4Ptr& existing,
+ const Lease4Ptr& lease);
+
+ /// @brief Update stats when updating lease.
+ ///
+ /// @param existing Lease data before update.
+ /// @param lease Lease data after update.
+ static void updateStatsOnUpdate(const Lease6Ptr& existing,
+ const Lease6Ptr& lease);
+
+ /// @brief Update stats when deleting lease.
+ ///
+ /// @param lease Deleted lease.
+ static void updateStatsOnDelete(const Lease4Ptr& lease);
+
+ /// @brief Update stats when deleting lease.
+ ///
+ /// @param lease Deleted lease.
+ static void updateStatsOnDelete(const Lease6Ptr& lease);
+
+ /// @brief Add or update lease.
+ ///
+ /// @param lease The lease to be added or updated (if exists).
+ /// @param force_create Flag to indicate if the function should try to
+ /// create the lease if it does not exists, or simply update and fail in
+ /// such case.
+ ///
+ /// @return true if lease has been successfully added, false otherwise.
+ static bool addOrUpdate4(Lease4Ptr lease, bool force_create);
+
+ /// @brief Add or update lease.
+ ///
+ /// @param lease The lease to be added or updated (if exists).
+ /// @param force_create Flag to indicate if the function should try to
+ /// create the lease if it does not exists, or simply update and fail in
+ /// such case.
+ ///
+ /// @return true if lease has been successfully added, false otherwise.
+ static bool addOrUpdate6(Lease6Ptr lease, bool force_create);
+};
+
+void
+LeaseCmdsImpl::updateStatsOnAdd(const Lease4Ptr& lease) {
+ if (!lease->stateExpiredReclaimed()) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "assigned-addresses"),
+ int64_t(1));
+ if (lease->stateDeclined()) {
+ StatsMgr::instance().addValue("declined-addresses", int64_t(1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(1));
+ }
+ }
+}
+
+void
+LeaseCmdsImpl::updateStatsOnAdd(const Lease6Ptr& lease) {
+ if (!lease->stateExpiredReclaimed()) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ lease->type_ == Lease::TYPE_NA ?
+ "assigned-nas" : "assigned-pds"),
+ int64_t(1));
+ if (lease->stateDeclined()) {
+ StatsMgr::instance().addValue("declined-addresses", int64_t(1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(1));
+ }
+ }
+}
+
+void
+LeaseCmdsImpl::updateStatsOnUpdate(const Lease4Ptr& existing,
+ const Lease4Ptr& lease) {
+ if (!existing->stateExpiredReclaimed()) {
+ // old lease is non expired-reclaimed
+ if (existing->subnet_id_ != lease->subnet_id_) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", existing->subnet_id_,
+ "assigned-addresses"),
+ int64_t(-1));
+ }
+ if (existing->stateDeclined()) {
+ // old lease is declined
+ StatsMgr::instance().addValue("declined-addresses", int64_t(-1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", existing->subnet_id_,
+ "declined-addresses"),
+ int64_t(-1));
+ }
+ if (!lease->stateExpiredReclaimed()) {
+ // new lease is non expired-reclaimed
+ if (existing->subnet_id_ != lease->subnet_id_) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "assigned-addresses"),
+ int64_t(1));
+ }
+ if (lease->stateDeclined()) {
+ // new lease is declined
+ StatsMgr::instance().addValue("declined-addresses", int64_t(1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(1));
+ }
+ }
+ } else {
+ // old lease is expired-reclaimed
+ if (!lease->stateExpiredReclaimed()) {
+ // new lease is non expired-reclaimed
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "assigned-addresses"),
+ int64_t(1));
+ if (lease->stateDeclined()) {
+ // new lease is declined
+ StatsMgr::instance().addValue("declined-addresses", int64_t(1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(1));
+ }
+ }
+ }
+}
+
+void
+LeaseCmdsImpl::updateStatsOnUpdate(const Lease6Ptr& existing,
+ const Lease6Ptr& lease) {
+ if (!existing->stateExpiredReclaimed()) {
+ // old lease is non expired-reclaimed
+ if (existing->subnet_id_ != lease->subnet_id_) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", existing->subnet_id_,
+ lease->type_ == Lease::TYPE_NA ?
+ "assigned-nas" : "assigned-pds"),
+ int64_t(-1));
+ }
+ if (existing->stateDeclined()) {
+ // old lease is declined
+ StatsMgr::instance().addValue("declined-addresses", int64_t(-1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", existing->subnet_id_,
+ "declined-addresses"),
+ int64_t(-1));
+ }
+ if (!lease->stateExpiredReclaimed()) {
+ // new lease is non expired-reclaimed
+ if (existing->subnet_id_ != lease->subnet_id_) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ lease->type_ == Lease::TYPE_NA ?
+ "assigned-nas" : "assigned-pds"),
+ int64_t(1));
+ }
+ if (lease->stateDeclined()) {
+ // new lease is declined
+ StatsMgr::instance().addValue("declined-addresses", int64_t(1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(1));
+ }
+ }
+ } else {
+ // old lease is expired-reclaimed
+ if (!lease->stateExpiredReclaimed()) {
+ // new lease is non expired-reclaimed
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ lease->type_ == Lease::TYPE_NA ?
+ "assigned-nas" : "assigned-pds"),
+ int64_t(1));
+ if (lease->stateDeclined()) {
+ // new lease is declined
+ StatsMgr::instance().addValue("declined-addresses", int64_t(1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(1));
+ }
+ }
+ }
+}
+
+void
+LeaseCmdsImpl::updateStatsOnDelete(const Lease4Ptr& lease) {
+ if (!lease->stateExpiredReclaimed()) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "assigned-addresses"),
+ int64_t(-1));
+ if (lease->stateDeclined()) {
+ StatsMgr::instance().addValue("declined-addresses", int64_t(-1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(-1));
+ }
+ }
+}
+
+void
+LeaseCmdsImpl::updateStatsOnDelete(const Lease6Ptr& lease) {
+ if (!lease->stateExpiredReclaimed()) {
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ lease->type_ == Lease::TYPE_NA ?
+ "assigned-nas" : "assigned-pds"),
+ int64_t(-1));
+ if (lease->stateDeclined()) {
+ StatsMgr::instance().addValue("declined-addresses", int64_t(-1));
+
+ StatsMgr::instance().addValue(
+ StatsMgr::generateName("subnet", lease->subnet_id_,
+ "declined-addresses"),
+ int64_t(-1));
+ }
+ }
+}
+
+bool
+LeaseCmdsImpl::addOrUpdate4(Lease4Ptr lease, bool force_create) {
+ Lease4Ptr existing = LeaseMgrFactory::instance().getLease4(lease->addr_);
+ if (force_create && !existing) {
+ // lease does not exist
+ if (!LeaseMgrFactory::instance().addLease(lease)) {
+ isc_throw(db::DuplicateEntry,
+ "lost race between calls to get and add");
+ }
+ LeaseCmdsImpl::updateStatsOnAdd(lease);
+ return (true);
+ }
+ if (existing) {
+ // Update lease current expiration time with value received from the
+ // database. Some database backends reject operations on the lease if
+ // the current expiration time value does not match what is stored.
+ Lease::syncCurrentExpirationTime(*existing, *lease);
+ }
+ try {
+ LeaseMgrFactory::instance().updateLease4(lease);
+ } catch (const NoSuchLease&) {
+ isc_throw(InvalidOperation, "failed to update the lease with address "
+ << lease->addr_ << " either because the lease has been "
+ "deleted or it has changed in the database, in both cases a "
+ "retry might succeed");
+ }
+
+ LeaseCmdsImpl::updateStatsOnUpdate(existing, lease);
+ return (false);
+}
+
+bool
+LeaseCmdsImpl::addOrUpdate6(Lease6Ptr lease, bool force_create) {
+ Lease6Ptr existing =
+ LeaseMgrFactory::instance().getLease6(lease->type_, lease->addr_);
+ if (force_create && !existing) {
+ // lease does not exist
+ if (!LeaseMgrFactory::instance().addLease(lease)) {
+ isc_throw(db::DuplicateEntry,
+ "lost race between calls to get and add");
+ }
+ LeaseCmdsImpl::updateStatsOnAdd(lease);
+ return (true);
+ }
+ if (existing) {
+ // Update lease current expiration time with value received from the
+ // database. Some database backends reject operations on the lease if
+ // the current expiration time value does not match what is stored.
+ Lease::syncCurrentExpirationTime(*existing, *lease);
+ }
+ try {
+ LeaseMgrFactory::instance().updateLease6(lease);
+ } catch (const NoSuchLease&) {
+ isc_throw(InvalidOperation, "failed to update the lease with address "
+ << lease->addr_ << " either because the lease has been "
+ "deleted or it has changed in the database, in both cases a "
+ "retry might succeed");
+ }
+
+ LeaseCmdsImpl::updateStatsOnUpdate(existing, lease);
+ return (false);
+}
+
+int
+LeaseCmdsImpl::leaseAddHandler(CalloutHandle& handle) {
+ // Arbitrary defaulting to DHCPv4 or with other words extractCommand
+ // below is not expected to throw...
+ bool v4 = true;
+ string txt = "malformed command";
+
+ stringstream resp;
+ try {
+ extractCommand(handle);
+ v4 = (cmd_name_ == "lease4-add");
+
+ txt = "(missing parameters)";
+ if (!cmd_args_) {
+ isc_throw(isc::BadValue, "no parameters specified for the command");
+ }
+
+ txt = cmd_args_->str();
+
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+
+ Lease4Ptr lease4;
+ Lease6Ptr lease6;
+ // This parameter is ignored for the commands adding the lease.
+ bool force_create = false;
+ if (v4) {
+ Lease4Parser parser;
+ lease4 = parser.parse(config, cmd_args_, force_create);
+ if (lease4) {
+ bool success;
+ if (!MultiThreadingMgr::instance().getMode()) {
+ // Not multi-threading.
+ success = LeaseMgrFactory::instance().addLease(lease4);
+ } else {
+ // Multi-threading, try to lock first to avoid a race.
+ ResourceHandler4 resource_handler;
+ if (resource_handler.tryLock4(lease4->addr_)) {
+ success = LeaseMgrFactory::instance().addLease(lease4);
+ } else {
+ isc_throw(ResourceBusy,
+ "ResourceBusy: IP address:" << lease4->addr_
+ << " could not be added.");
+ }
+ }
+
+ if (!success) {
+ isc_throw(db::DuplicateEntry, "IPv4 lease already exists.");
+ }
+
+ LeaseCmdsImpl::updateStatsOnAdd(lease4);
+ resp << "Lease for address " << lease4->addr_.toText()
+ << ", subnet-id " << lease4->subnet_id_ << " added.";
+ }
+ } else {
+ Lease6Parser parser;
+ lease6 = parser.parse(config, cmd_args_, force_create);
+ if (lease6) {
+ bool success;
+ if (!MultiThreadingMgr::instance().getMode()) {
+ // Not multi-threading.
+ success = LeaseMgrFactory::instance().addLease(lease6);
+ } else {
+ // Multi-threading, try to lock first to avoid a race.
+ ResourceHandler resource_handler;
+ if (resource_handler.tryLock(lease6->type_, lease6->addr_)) {
+ success = LeaseMgrFactory::instance().addLease(lease6);
+ } else {
+ isc_throw(ResourceBusy,
+ "ResourceBusy: IP address:" << lease6->addr_
+ << " could not be added.");
+ }
+ }
+
+ if (!success) {
+ isc_throw(db::DuplicateEntry, "IPv6 lease already exists.");
+ }
+
+ LeaseCmdsImpl::updateStatsOnAdd(lease6);
+ if (lease6->type_ == Lease::TYPE_NA) {
+ resp << "Lease for address " << lease6->addr_.toText()
+ << ", subnet-id " << lease6->subnet_id_ << " added.";
+ } else {
+ resp << "Lease for prefix " << lease6->addr_.toText()
+ << "/" << static_cast<int>(lease6->prefixlen_)
+ << ", subnet-id " << lease6->subnet_id_ << " added.";
+ }
+ }
+ }
+
+ } catch (const std::exception& ex) {
+ LOG_ERROR(lease_cmds_logger, v4 ? LEASE_CMDS_ADD4_FAILED : LEASE_CMDS_ADD6_FAILED)
+ .arg(txt)
+ .arg(ex.what());
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ LOG_INFO(lease_cmds_logger,
+ v4 ? LEASE_CMDS_ADD4 : LEASE_CMDS_ADD6).arg(txt);
+ setSuccessResponse(handle, resp.str());
+ return (0);
+}
+
+LeaseCmdsImpl::Parameters
+LeaseCmdsImpl::getParameters(bool v6, const ConstElementPtr& params) {
+ Parameters x;
+
+ if (!params || params->getType() != Element::map) {
+ isc_throw(BadValue, "Parameters missing or are not a map.");
+ }
+
+ if (params->contains("update-ddns")) {
+ ConstElementPtr tmp = params->get("update-ddns");
+ if (tmp->getType() != Element::boolean) {
+ isc_throw(BadValue, "'update-ddns' is not a boolean");
+ } else {
+ x.updateDDNS = tmp->boolValue();
+ }
+ }
+
+ // We support several sets of parameters for leaseX-get/lease-del:
+ // lease-get(type, address)
+ // lease-get(type, subnet-id, identifier-type, identifier)
+
+ if (params->contains("type")) {
+ string t = params->get("type")->stringValue();
+ if (t == "IA_NA" || t == "0") {
+ x.lease_type = Lease::TYPE_NA;
+ } else if (t == "IA_TA" || t == "1") {
+ x.lease_type = Lease::TYPE_TA;
+ } else if (t == "IA_PD" || t == "2") {
+ x.lease_type = Lease::TYPE_PD;
+ } else if (t == "V4" || t == "3") {
+ x.lease_type = Lease::TYPE_V4;
+ } else {
+ isc_throw(BadValue, "Invalid lease type specified: "
+ << t << ", only supported values are: IA_NA, IA_TA,"
+ << " IA_PD and V4");
+ }
+ }
+
+ ConstElementPtr tmp = params->get("ip-address");
+ if (tmp) {
+ if (tmp->getType() != Element::string) {
+ isc_throw(BadValue, "'ip-address' is not a string.");
+ }
+
+ x.addr = IOAddress(tmp->stringValue());
+
+ if ((v6 && !x.addr.isV6()) || (!v6 && !x.addr.isV4())) {
+ stringstream txt;
+ txt << "Invalid " << (v6 ? "IPv6" : "IPv4")
+ << " address specified: " << tmp->stringValue();
+ isc_throw(BadValue, txt.str());
+ }
+
+ x.query_type = Parameters::TYPE_ADDR;
+ return (x);
+ }
+
+ tmp = params->get("subnet-id");
+ if (!tmp) {
+ isc_throw(BadValue, "Mandatory 'subnet-id' parameter missing.");
+ }
+ if (tmp->getType() != Element::integer) {
+ isc_throw(BadValue, "'subnet-id' parameter is not integer.");
+ }
+ x.subnet_id = tmp->intValue();
+
+ if (params->contains("iaid")) {
+ x.iaid = params->get("iaid")->intValue();
+ }
+
+ // No address specified. Ok, so it must be identifier based query.
+ // "identifier-type": "duid",
+ // "identifier": "aa:bb:cc:dd:ee:..."
+
+ ConstElementPtr type = params->get("identifier-type");
+ ConstElementPtr ident = params->get("identifier");
+ if (!type || type->getType() != Element::string) {
+ isc_throw(BadValue, "No 'ip-address' provided"
+ " and 'identifier-type' is either missing or not a string.");
+ }
+ if (!ident || ident->getType() != Element::string) {
+ isc_throw(BadValue, "No 'ip-address' provided"
+ " and 'identifier' is either missing or not a string.");
+ }
+
+ // Got the parameters. Let's see if their values make sense.
+ // Try to convert identifier-type
+ x.query_type = Parameters::txtToType(type->stringValue());
+
+ switch (x.query_type) {
+ case Parameters::TYPE_HWADDR: {
+ HWAddr hw = HWAddr::fromText(ident->stringValue());
+ x.hwaddr = HWAddrPtr(new HWAddr(hw));
+ break;
+ }
+ case Parameters::TYPE_CLIENT_ID: {
+ x.client_id = ClientId::fromText(ident->stringValue());
+ break;
+ }
+ case Parameters::TYPE_DUID: {
+ DUID duid = DUID::fromText(ident->stringValue());
+ x.duid = DuidPtr(new DUID(duid));
+ break;
+ }
+ case Parameters::TYPE_ADDR: {
+ // We should never get here. The address clause should have been caught
+ // earlier.
+ return (x);
+ }
+ default: {
+ isc_throw(BadValue, "Identifier type " << type->stringValue() <<
+ " is not supported.");
+ }
+ }
+
+ return (x);
+}
+
+int
+LeaseCmdsImpl::leaseGetHandler(CalloutHandle& handle) {
+ Parameters p;
+ Lease4Ptr lease4;
+ Lease6Ptr lease6;
+ bool v4;
+ try {
+ extractCommand(handle);
+ v4 = (cmd_name_ == "lease4-get");
+
+ p = getParameters(!v4, cmd_args_);
+ switch (p.query_type) {
+ case Parameters::TYPE_ADDR: {
+ // Query by address
+ if (v4) {
+ lease4 = LeaseMgrFactory::instance().getLease4(p.addr);
+ } else {
+ lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, p.addr);
+ }
+ break;
+ }
+ case Parameters::TYPE_HWADDR:
+ if (v4) {
+ if (!p.hwaddr) {
+ isc_throw(InvalidParameter, "Program error: Query by hw-address "
+ "requires hwaddr to be specified");
+ }
+
+ lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id);
+ } else {
+ isc_throw(isc::InvalidParameter, "Query by hw-address is not allowed in v6.");
+ }
+ break;
+
+ case Parameters::TYPE_DUID:
+ if (!v4) {
+ if (!p.duid) {
+ isc_throw(InvalidParameter, "Program error: Query by duid "
+ "requires duid to be specified");
+ }
+
+ lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid,
+ p.iaid, p.subnet_id);
+ } else {
+ isc_throw(InvalidParameter, "Query by duid is not allowed in v4.");
+ }
+ break;
+
+ case Parameters::TYPE_CLIENT_ID:
+ if (v4) {
+ if (!p.client_id) {
+ isc_throw(InvalidParameter, "Program error: Query by client-id "
+ "requires client-id to be specified");
+ }
+
+ lease4 = LeaseMgrFactory::instance().getLease4(*p.client_id, p.subnet_id);
+ } else {
+ isc_throw(isc::InvalidParameter, "Query by client-id is not allowed in v6.");
+ }
+ break;
+
+ default: {
+ isc_throw(InvalidOperation, "Unknown query type: " << static_cast<int>(p.query_type));
+ break;
+ }
+ }
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ ElementPtr lease_json;
+ if (v4 && lease4) {
+ lease_json = lease4->toElement();
+ ConstElementPtr response = createAnswer(CONTROL_RESULT_SUCCESS,
+ "IPv4 lease found.", lease_json);
+ setResponse(handle, response);
+ } else if (!v4 && lease6) {
+ lease_json = lease6->toElement();
+ ConstElementPtr response = createAnswer(CONTROL_RESULT_SUCCESS,
+ "IPv6 lease found.", lease_json);
+ setResponse(handle, response);
+ } else {
+ // If we got here, the lease has not been found.
+ setErrorResponse(handle, "Lease not found.", CONTROL_RESULT_EMPTY);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::leaseGetAllHandler(CalloutHandle& handle) {
+ bool v4 = true;
+ try {
+ extractCommand(handle);
+ v4 = (cmd_name_ == "lease4-get-all");
+
+ ElementPtr leases_json = Element::createList();
+
+ // The argument may contain a list of subnets for which leases should
+ // be returned.
+ if (cmd_args_) {
+ ConstElementPtr subnets = cmd_args_->get("subnets");
+ if (!subnets) {
+ isc_throw(BadValue, "'subnets' parameter not specified");
+ }
+ if (subnets->getType() != Element::list) {
+ isc_throw(BadValue, "'subnets' parameter must be a list");
+ }
+
+ const std::vector<ElementPtr>& subnet_ids = subnets->listValue();
+ for (auto subnet_id = subnet_ids.begin();
+ subnet_id != subnet_ids.end();
+ ++subnet_id) {
+ if ((*subnet_id)->getType() != Element::integer) {
+ isc_throw(BadValue, "listed subnet identifiers must be numbers");
+ }
+
+ if (v4) {
+ Lease4Collection leases =
+ LeaseMgrFactory::instance().getLeases4((*subnet_id)->intValue());
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+ } else {
+ Lease6Collection leases =
+ LeaseMgrFactory::instance().getLeases6((*subnet_id)->intValue());
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+ }
+ }
+
+ } else {
+ // There is no 'subnets' argument so let's return all leases.
+ if (v4) {
+ Lease4Collection leases = LeaseMgrFactory::instance().getLeases4();
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+ } else {
+ Lease6Collection leases = LeaseMgrFactory::instance().getLeases6();
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+ }
+ }
+
+ std::ostringstream s;
+ s << leases_json->size()
+ << " IPv" << (v4 ? "4" : "6")
+ << " lease(s) found.";
+ ElementPtr args = Element::createMap();
+ args->set("leases", leases_json);
+ ConstElementPtr response =
+ createAnswer(leases_json->size() > 0 ?
+ CONTROL_RESULT_SUCCESS :
+ CONTROL_RESULT_EMPTY,
+ s.str(), args);
+ setResponse(handle, response);
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (CONTROL_RESULT_ERROR);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::leaseGetPageHandler(CalloutHandle& handle) {
+ bool v4 = true;
+ try {
+ extractCommand(handle);
+ v4 = (cmd_name_ == "lease4-get-page");
+
+ // arguments must always be present
+ if (!cmd_args_) {
+ isc_throw(BadValue, "no parameters specified for the " << cmd_name_
+ << " command");
+ }
+
+ // The 'from' argument denotes from which lease we should start the
+ // results page. The results page excludes this lease.
+ ConstElementPtr from = cmd_args_->get("from");
+ if (!from) {
+ isc_throw(BadValue, "'from' parameter not specified");
+ }
+
+ // The 'from' argument is a string. It may contain a 'start' keyword or
+ // an IP address.
+ if (from->getType() != Element::string) {
+ isc_throw(BadValue, "'from' parameter must be a string");
+ }
+
+ boost::scoped_ptr<IOAddress> from_address;
+ try {
+ if (from->stringValue() == "start") {
+ from_address.reset(new IOAddress(v4 ? "0.0.0.0" : "::"));
+
+ } else {
+ // Conversion of a string to an IP address may throw.
+ from_address.reset(new IOAddress(from->stringValue()));
+ }
+
+ } catch (...) {
+ isc_throw(BadValue, "'from' parameter value is neither 'start' keyword nor "
+ "a valid IPv" << (v4 ? "4" : "6") << " address");
+ }
+
+ // It must be either IPv4 address for lease4-get-page or IPv6 address for
+ // lease6-get-page.
+ if (v4 && (!from_address->isV4())) {
+ isc_throw(BadValue, "'from' parameter value " << from_address->toText()
+ << " is not an IPv4 address");
+
+ } else if (!v4 && from_address->isV4()) {
+ isc_throw(BadValue, "'from' parameter value " << from_address->toText()
+ << " is not an IPv6 address");
+ }
+
+ // The 'limit' is a desired page size. It must always be present.
+ ConstElementPtr page_limit = cmd_args_->get("limit");
+ if (!page_limit) {
+ isc_throw(BadValue, "'limit' parameter not specified");
+ }
+
+ // The 'limit' must be a number.
+ if (page_limit->getType() != Element::integer) {
+ isc_throw(BadValue, "'limit' parameter must be a number");
+ }
+
+ // Retrieve the desired page size.
+ size_t page_limit_value = static_cast<size_t>(page_limit->intValue());
+
+ ElementPtr leases_json = Element::createList();
+
+ if (v4) {
+ // Get page of IPv4 leases.
+ Lease4Collection leases =
+ LeaseMgrFactory::instance().getLeases4(*from_address,
+ LeasePageSize(page_limit_value));
+
+ // Convert leases into JSON list.
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+
+ } else {
+ // Get page of IPv6 leases.
+ Lease6Collection leases =
+ LeaseMgrFactory::instance().getLeases6(*from_address,
+ LeasePageSize(page_limit_value));
+ // Convert leases into JSON list.
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+ }
+
+ // Prepare textual status.
+ std::ostringstream s;
+ s << leases_json->size()
+ << " IPv" << (v4 ? "4" : "6")
+ << " lease(s) found.";
+ ElementPtr args = Element::createMap();
+
+ // Put gathered data into arguments map.
+ args->set("leases", leases_json);
+ args->set("count", Element::create(static_cast<int64_t>(leases_json->size())));
+
+ // Create the response.
+ ConstElementPtr response =
+ createAnswer(leases_json->size() > 0 ?
+ CONTROL_RESULT_SUCCESS :
+ CONTROL_RESULT_EMPTY,
+ s.str(), args);
+ setResponse(handle, response);
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (CONTROL_RESULT_ERROR);
+ }
+
+ return (CONTROL_RESULT_SUCCESS);
+}
+
+int
+LeaseCmdsImpl::leaseGetByHwAddressHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ // arguments must always be present
+ if (!cmd_args_ || (cmd_args_->getType() != Element::map)) {
+ isc_throw(BadValue, "Command arguments missing or a not a map.");
+ }
+
+ // the hw-address parameter is mandatory.
+ ConstElementPtr hw_address = cmd_args_->get("hw-address");
+ if (!hw_address) {
+ isc_throw(BadValue, "'hw-address' parameter not specified");
+ }
+
+ // The 'hw-address' argument is a string.
+ if (hw_address->getType() != Element::string) {
+ isc_throw(BadValue, "'hw-address' parameter must be a string");
+ }
+
+ HWAddr hwaddr = HWAddr::fromText(hw_address->stringValue());
+
+ Lease4Collection leases =
+ LeaseMgrFactory::instance().getLease4(hwaddr);
+ ElementPtr leases_json = Element::createList();
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+
+ std::ostringstream s;
+ s << leases_json->size() << " IPv4 lease(s) found.";
+ ElementPtr args = Element::createMap();
+ args->set("leases", leases_json);
+ ConstElementPtr response =
+ createAnswer(leases_json->size() > 0 ?
+ CONTROL_RESULT_SUCCESS :
+ CONTROL_RESULT_EMPTY,
+ s.str(), args);
+ setResponse(handle, response);
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (CONTROL_RESULT_ERROR);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::leaseGetByClientIdHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ // arguments must always be present
+ if (!cmd_args_ || (cmd_args_->getType() != Element::map)) {
+ isc_throw(BadValue, "Command arguments missing or a not a map.");
+ }
+
+ // the client-id parameter is mandatory.
+ ConstElementPtr client_id = cmd_args_->get("client-id");
+ if (!client_id) {
+ isc_throw(BadValue, "'client-id' parameter not specified");
+ }
+
+ // The 'client-id' argument is a string.
+ if (client_id->getType() != Element::string) {
+ isc_throw(BadValue, "'client-id' parameter must be a string");
+ }
+
+ ClientIdPtr clientid = ClientId::fromText(client_id->stringValue());
+
+ Lease4Collection leases =
+ LeaseMgrFactory::instance().getLease4(*clientid);
+ ElementPtr leases_json = Element::createList();
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+
+ std::ostringstream s;
+ s << leases_json->size() << " IPv4 lease(s) found.";
+ ElementPtr args = Element::createMap();
+ args->set("leases", leases_json);
+ ConstElementPtr response =
+ createAnswer(leases_json->size() > 0 ?
+ CONTROL_RESULT_SUCCESS :
+ CONTROL_RESULT_EMPTY,
+ s.str(), args);
+ setResponse(handle, response);
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (CONTROL_RESULT_ERROR);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::leaseGetByDuidHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ // arguments must always be present
+ if (!cmd_args_ || (cmd_args_->getType() != Element::map)) {
+ isc_throw(BadValue, "Command arguments missing or a not a map.");
+ }
+
+ // the duid parameter is mandatory.
+ ConstElementPtr duid = cmd_args_->get("duid");
+ if (!duid) {
+ isc_throw(BadValue, "'duid' parameter not specified");
+ }
+
+ // The 'duid' argument is a string.
+ if (duid->getType() != Element::string) {
+ isc_throw(BadValue, "'duid' parameter must be a string");
+ }
+
+ DUID duid_ = DUID::fromText(duid->stringValue());
+
+ Lease6Collection leases =
+ LeaseMgrFactory::instance().getLeases6(duid_);
+ ElementPtr leases_json = Element::createList();
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+
+ std::ostringstream s;
+ s << leases_json->size() << " IPv6 lease(s) found.";
+ ElementPtr args = Element::createMap();
+ args->set("leases", leases_json);
+ ConstElementPtr response =
+ createAnswer(leases_json->size() > 0 ?
+ CONTROL_RESULT_SUCCESS :
+ CONTROL_RESULT_EMPTY,
+ s.str(), args);
+ setResponse(handle, response);
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (CONTROL_RESULT_ERROR);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::leaseGetByHostnameHandler(CalloutHandle& handle) {
+ bool v4;
+ try {
+ extractCommand(handle);
+ v4 = (cmd_name_ == "lease4-get-by-hostname");
+
+ // arguments must always be present
+ if (!cmd_args_ || (cmd_args_->getType() != Element::map)) {
+ isc_throw(BadValue, "Command arguments missing or a not a map.");
+ }
+
+ // the hostname parameter is mandatory.
+ ConstElementPtr hostname = cmd_args_->get("hostname");
+ if (!hostname) {
+ isc_throw(BadValue, "'hostname' parameter not specified");
+ }
+
+ // The 'hostname' argument is a string.
+ if (hostname->getType() != Element::string) {
+ isc_throw(BadValue, "'hostname' parameter must be a string");
+ }
+
+ std::string hostname_ = hostname->stringValue();
+ /// The 'hostname' argument should not be empty.
+ if (hostname_.empty()) {
+ isc_throw(BadValue, "'hostname' parameter is empty");
+ }
+ boost::algorithm::to_lower(hostname_);
+
+ ElementPtr leases_json = Element::createList();
+ if (v4) {
+ Lease4Collection leases =
+ LeaseMgrFactory::instance().getLeases4(hostname_);
+
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+ } else {
+ Lease6Collection leases =
+ LeaseMgrFactory::instance().getLeases6(hostname_);
+
+ for (auto lease : leases) {
+ ElementPtr lease_json = lease->toElement();
+ leases_json->add(lease_json);
+ }
+ }
+
+ std::ostringstream s;
+ s << leases_json->size()
+ << " IPv" << (v4 ? "4" : "6")
+ << " lease(s) found.";
+ ElementPtr args = Element::createMap();
+ args->set("leases", leases_json);
+ ConstElementPtr response =
+ createAnswer(leases_json->size() > 0 ?
+ CONTROL_RESULT_SUCCESS :
+ CONTROL_RESULT_EMPTY,
+ s.str(), args);
+ setResponse(handle, response);
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (CONTROL_RESULT_ERROR);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::lease4DelHandler(CalloutHandle& handle) {
+ Parameters p;
+ Lease4Ptr lease4;
+ try {
+ extractCommand(handle);
+ p = getParameters(false, cmd_args_);
+
+ switch (p.query_type) {
+ case Parameters::TYPE_ADDR: {
+ // If address was specified explicitly, let's use it as is.
+ lease4 = LeaseMgrFactory::instance().getLease4(p.addr);
+ if (!lease4) {
+ setErrorResponse(handle, "IPv4 lease not found.", CONTROL_RESULT_EMPTY);
+ return (0);
+ }
+ break;
+ }
+ case Parameters::TYPE_HWADDR: {
+ if (!p.hwaddr) {
+ isc_throw(InvalidParameter, "Program error: Query by hw-address "
+ "requires hwaddr to be specified");
+ }
+
+ // Let's see if there's such a lease at all.
+ lease4 = LeaseMgrFactory::instance().getLease4(*p.hwaddr, p.subnet_id);
+ if (!lease4) {
+ setErrorResponse(handle, "IPv4 lease not found.", CONTROL_RESULT_EMPTY);
+ return (0);
+ }
+ break;
+ }
+ case Parameters::TYPE_CLIENT_ID: {
+ if (!p.client_id) {
+ isc_throw(InvalidParameter, "Program error: Query by client-id "
+ "requires client-id to be specified");
+ }
+
+ // Let's see if there's such a lease at all.
+ lease4 = LeaseMgrFactory::instance().getLease4(*p.client_id, p.subnet_id);
+ if (!lease4) {
+ setErrorResponse(handle, "IPv4 lease not found.", CONTROL_RESULT_EMPTY);
+ return (0);
+ }
+ break;
+ }
+ case Parameters::TYPE_DUID: {
+ isc_throw(InvalidParameter, "Delete by duid is not allowed in v4.");
+ break;
+ }
+ default: {
+ isc_throw(InvalidOperation, "Unknown query type: " << static_cast<int>(p.query_type));
+ break;
+ }
+ }
+
+ if (LeaseMgrFactory::instance().deleteLease(lease4)) {
+ setSuccessResponse(handle, "IPv4 lease deleted.");
+ LeaseCmdsImpl::updateStatsOnDelete(lease4);
+ } else {
+ setErrorResponse (handle, "IPv4 lease not found.", CONTROL_RESULT_EMPTY);
+ }
+
+ // Queue an NCR to remove DNS if configured and the lease has it.
+ if (p.updateDDNS) {
+ queueNCR(CHG_REMOVE, lease4);
+ }
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::lease6BulkApplyHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ // Arguments are mandatory.
+ if (!cmd_args_ || (cmd_args_->getType() != Element::map)) {
+ isc_throw(BadValue, "Command arguments missing or a not a map.");
+ }
+
+ // At least one of the 'deleted-leases' or 'leases' must be present.
+ auto deleted_leases = cmd_args_->get("deleted-leases");
+ auto leases = cmd_args_->get("leases");
+
+ if (!deleted_leases && !leases) {
+ isc_throw(BadValue, "neither 'deleted-leases' nor 'leases' parameter"
+ " specified");
+ }
+
+ // Make sure that 'deleted-leases' is a list, if present.
+ if (deleted_leases && (deleted_leases->getType() != Element::list)) {
+ isc_throw(BadValue, "the 'deleted-leases' parameter must be a list");
+ }
+
+ // Make sure that 'leases' is a list, if present.
+ if (leases && (leases->getType() != Element::list)) {
+ isc_throw(BadValue, "the 'leases' parameter must be a list");
+ }
+
+ // Parse deleted leases without deleting them from the database
+ // yet. If any of the deleted leases or new leases appears to be
+ // malformed we can easily rollback.
+ std::list<std::pair<Parameters, Lease6Ptr> > parsed_deleted_list;
+ if (deleted_leases) {
+ auto leases_list = deleted_leases->listValue();
+
+ // Iterate over leases to be deleted.
+ for (auto lease_params : leases_list) {
+ // Parsing the lease may throw and it means that the lease
+ // information is malformed.
+ Parameters p = getParameters(true, lease_params);
+ auto lease = getIPv6LeaseForDelete(p);
+ parsed_deleted_list.push_back(std::make_pair(p, lease));
+ }
+ }
+
+ // Parse new/updated leases without affecting the database to detect
+ // any errors that should cause an error response.
+ std::list<Lease6Ptr> parsed_leases_list;
+ if (leases) {
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+
+ // Iterate over all leases.
+ auto leases_list = leases->listValue();
+ for (auto lease_params : leases_list) {
+
+ Lease6Parser parser;
+ bool force_update;
+
+ // If parsing the lease fails we throw, as it indicates that the
+ // command is malformed.
+ Lease6Ptr lease6 = parser.parse(config, lease_params, force_update);
+ parsed_leases_list.push_back(lease6);
+ }
+ }
+
+ // Count successful deletions and updates.
+ size_t success_count = 0;
+
+ ElementPtr failed_deleted_list;
+ if (!parsed_deleted_list.empty()) {
+
+ // Iterate over leases to be deleted.
+ for (auto lease_params_pair : parsed_deleted_list) {
+
+ // This part is outside of the try-catch because an exception
+ // indicates that the command is malformed.
+ Parameters p = lease_params_pair.first;
+ auto lease = lease_params_pair.second;
+
+ try {
+ if (lease) {
+ // This may throw if the lease couldn't be deleted for
+ // any reason, but we still want to proceed with other
+ // leases.
+ if (LeaseMgrFactory::instance().deleteLease(lease)) {
+ ++success_count;
+ LeaseCmdsImpl::updateStatsOnDelete(lease);
+
+ } else {
+ // Lazy creation of the list of leases which failed to delete.
+ if (!failed_deleted_list) {
+ failed_deleted_list = Element::createList();
+ }
+
+ // If the lease doesn't exist we also want to put it
+ // on the list of leases which failed to delete. That
+ // corresponds to the lease6-del command which returns
+ // an error when the lease doesn't exist.
+ failed_deleted_list->add(createFailedLeaseMap(p.lease_type,
+ p.addr, p.duid,
+ CONTROL_RESULT_EMPTY,
+ "lease not found"));
+ }
+ }
+
+ } catch (const std::exception& ex) {
+ // Lazy creation of the list of leases which failed to delete.
+ if (!failed_deleted_list) {
+ failed_deleted_list = Element::createList();
+ }
+ failed_deleted_list->add(createFailedLeaseMap(p.lease_type,
+ p.addr, p.duid,
+ CONTROL_RESULT_ERROR,
+ ex.what()));
+ }
+ }
+ }
+
+ // Process leases to be added or/and updated.
+ ElementPtr failed_leases_list;
+ if (!parsed_leases_list.empty()) {
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+
+ // Iterate over all leases.
+ for (auto lease : parsed_leases_list) {
+
+ try {
+ if (!MultiThreadingMgr::instance().getMode()) {
+ // Not multi-threading.
+ addOrUpdate6(lease, true);
+ } else {
+ // Multi-threading, try to lock first to avoid a race.
+ ResourceHandler resource_handler;
+ if (resource_handler.tryLock(lease->type_, lease->addr_)) {
+ addOrUpdate6(lease, true);
+ } else {
+ isc_throw(ResourceBusy,
+ "ResourceBusy: IP address:" << lease->addr_
+ << " could not be updated.");
+ }
+ }
+
+ ++success_count;
+ } catch (const std::exception& ex) {
+ // Lazy creation of the list of leases which failed to add/update.
+ if (!failed_leases_list) {
+ failed_leases_list = Element::createList();
+ }
+ failed_leases_list->add(createFailedLeaseMap(lease->type_,
+ lease->addr_,
+ lease->duid_,
+ CONTROL_RESULT_ERROR,
+ ex.what()));
+ }
+ }
+ }
+
+ // Start preparing the response.
+ ElementPtr args;
+
+ if (failed_deleted_list || failed_leases_list) {
+ // If there are any failed leases, let's include them in the response.
+ args = Element::createMap();
+
+ // failed-deleted-leases
+ if (failed_deleted_list) {
+ args->set("failed-deleted-leases", failed_deleted_list);
+ }
+
+ // failed-leases
+ if (failed_leases_list) {
+ args->set("failed-leases", failed_leases_list);
+ }
+ }
+
+ // Send the success response and include failed leases.
+ std::ostringstream resp_text;
+ resp_text << "Bulk apply of " << success_count << " IPv6 leases completed.";
+ auto answer = createAnswer(success_count > 0 ? CONTROL_RESULT_SUCCESS :
+ CONTROL_RESULT_EMPTY, resp_text.str(), args);
+ setResponse(handle, answer);
+
+ } catch (const std::exception& ex) {
+ // Unable to parse the command and similar issues.
+ setErrorResponse(handle, ex.what());
+ return (CONTROL_RESULT_ERROR);
+ }
+
+ return (CONTROL_RESULT_SUCCESS);
+}
+
+int
+LeaseCmdsImpl::lease6DelHandler(CalloutHandle& handle) {
+ Parameters p;
+ Lease6Ptr lease6;
+ IOAddress addr(IOAddress::IPV6_ZERO_ADDRESS());
+ try {
+ extractCommand(handle);
+ p = getParameters(true, cmd_args_);
+
+ switch (p.query_type) {
+ case Parameters::TYPE_ADDR: {
+ // If address was specified explicitly, let's use it as is.
+
+ // Let's see if there's such a lease at all.
+ lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, p.addr);
+ if (!lease6) {
+ setErrorResponse(handle, "IPv6 lease not found.", CONTROL_RESULT_EMPTY);
+ return (0);
+ }
+ break;
+ }
+ case Parameters::TYPE_HWADDR: {
+ isc_throw(InvalidParameter, "Delete by hw-address is not allowed in v6.");
+ break;
+ }
+ case Parameters::TYPE_DUID: {
+ if (!p.duid) {
+ isc_throw(InvalidParameter, "Program error: Query by duid "
+ "requires duid to be specified");
+ }
+
+ // Let's see if there's such a lease at all.
+ lease6 = LeaseMgrFactory::instance().getLease6(p.lease_type, *p.duid,
+ p.iaid, p.subnet_id);
+ if (!lease6) {
+ setErrorResponse(handle, "IPv6 lease not found.", CONTROL_RESULT_EMPTY);
+ return (0);
+ }
+ break;
+ }
+ default: {
+ isc_throw(InvalidOperation, "Unknown query type: " << static_cast<int>(p.query_type));
+ break;
+ }
+ }
+
+ if (LeaseMgrFactory::instance().deleteLease(lease6)) {
+ setSuccessResponse(handle, "IPv6 lease deleted.");
+ LeaseCmdsImpl::updateStatsOnDelete(lease6);
+ } else {
+ setErrorResponse (handle, "IPv6 lease not found.", CONTROL_RESULT_EMPTY);
+ }
+
+ // Queue an NCR to remove DNS if configured and the lease has it.
+ if (p.updateDDNS) {
+ queueNCR(CHG_REMOVE, lease6);
+ }
+
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::lease4UpdateHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ // We need the lease to be specified.
+ if (!cmd_args_) {
+ isc_throw(isc::BadValue, "no parameters specified for lease4-update command");
+ }
+
+ // Get the parameters specified by the user first.
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+ Lease4Ptr lease4;
+ Lease4Parser parser;
+ bool force_create = false;
+
+ // The parser does sanity checks (if the address is in scope, if
+ // subnet-id is valid, etc)
+ lease4 = parser.parse(config, cmd_args_, force_create);
+ bool added = false;
+ if (!MultiThreadingMgr::instance().getMode()) {
+ // Not multi-threading.
+ added = addOrUpdate4(lease4, force_create);
+ } else {
+ // Multi-threading, try to lock first to avoid a race.
+ ResourceHandler4 resource_handler;
+ if (resource_handler.tryLock4(lease4->addr_)) {
+ added = addOrUpdate4(lease4, force_create);
+ } else {
+ isc_throw(ResourceBusy,
+ "ResourceBusy: IP address:" << lease4->addr_
+ << " could not be updated.");
+ }
+ }
+
+ if (added) {
+ setSuccessResponse(handle, "IPv4 lease added.");
+ } else {
+ setSuccessResponse(handle, "IPv4 lease updated.");
+ }
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::lease6UpdateHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ // We need the lease to be specified.
+ if (!cmd_args_) {
+ isc_throw(isc::BadValue, "no parameters specified for lease6-update command");
+ }
+
+ // Get the parameters specified by the user first.
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+ Lease6Ptr lease6;
+ Lease6Parser parser;
+ bool force_create = false;
+
+ // The parser does sanity checks (if the address is in scope, if
+ // subnet-id is valid, etc)
+ lease6 = parser.parse(config, cmd_args_, force_create);
+ bool added = false;
+ if (!MultiThreadingMgr::instance().getMode()) {
+ // Not multi-threading.
+ added = addOrUpdate6(lease6, force_create);
+ } else {
+ // Multi-threading, try to lock first to avoid a race.
+ ResourceHandler resource_handler;
+ if (resource_handler.tryLock(lease6->type_, lease6->addr_)) {
+ added = addOrUpdate6(lease6, force_create);
+ } else {
+ isc_throw(ResourceBusy,
+ "ResourceBusy: IP address:" << lease6->addr_
+ << " could not be updated.");
+ }
+ }
+
+ if (added) {
+ setSuccessResponse(handle, "IPv6 lease added.");
+ } else {
+ setSuccessResponse(handle, "IPv6 lease updated.");
+ }
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::lease4WipeHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ SimpleParser parser;
+ SubnetID id = 0;
+
+ size_t num = 0; // number of leases deleted
+ stringstream ids; // a text with subnet-ids being wiped
+
+ // The subnet-id parameter is now optional.
+ if (cmd_args_ && cmd_args_->contains("subnet-id")) {
+ id = parser.getUint32(cmd_args_, "subnet-id");
+ }
+
+ if (id) {
+ // Wipe a single subnet.
+ num = LeaseMgrFactory::instance().wipeLeases4(id);
+ ids << " " << id;
+
+ auto observation = StatsMgr::instance().getObservation(
+ StatsMgr::generateName("subnet", id, "declined-addresses"));
+
+ int64_t previous_declined = 0;
+
+ if (observation) {
+ previous_declined = observation->getInteger().first;
+ }
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", id, "assigned-addresses"),
+ int64_t(0));
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", id, "declined-addresses"),
+ int64_t(0));
+
+ StatsMgr::instance().addValue("declined-addresses", -previous_declined);
+ } else {
+ // Wipe them all!
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+ ConstCfgSubnets4Ptr subnets = config->getCfgSubnets4();
+ const Subnet4Collection * subs = subnets->getAll();
+
+ // Go over all subnets and wipe leases in each of them.
+ for (auto sub : *subs) {
+ num += LeaseMgrFactory::instance().wipeLeases4(sub->getID());
+ ids << " " << sub->getID();
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", sub->getID(), "assigned-addresses"),
+ int64_t(0));
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", sub->getID(), "declined-addresses"),
+ int64_t(0));
+ }
+
+ StatsMgr::instance().setValue("declined-addresses", int64_t(0));
+ }
+
+ stringstream tmp;
+ tmp << "Deleted " << num << " IPv4 lease(s) from subnet(s)" << ids.str();
+ ConstElementPtr response = createAnswer(num ? CONTROL_RESULT_SUCCESS
+ : CONTROL_RESULT_EMPTY, tmp.str());
+ setResponse(handle, response);
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+LeaseCmdsImpl::lease6WipeHandler(CalloutHandle& handle) {
+ try {
+ extractCommand(handle);
+
+ SimpleParser parser;
+ SubnetID id = 0;
+
+ size_t num = 0; // number of leases deleted
+ stringstream ids; // a text with subnet-ids being wiped
+
+ /// @todo: consider extending the code with wipe-leases:
+ /// - of specific type (v6)
+ /// - from specific shared network
+ /// - from specific pool
+ /// see https://oldkea.isc.org/ticket/5543#comment:6 for background.
+
+ // The subnet-id parameter is now optional.
+ if (cmd_args_ && cmd_args_->contains("subnet-id")) {
+ id = parser.getUint32(cmd_args_, "subnet-id");
+ }
+
+ if (id) {
+ // Wipe a single subnet.
+ num = LeaseMgrFactory::instance().wipeLeases6(id);
+ ids << " " << id;
+
+ auto observation = StatsMgr::instance().getObservation(
+ StatsMgr::generateName("subnet", id, "declined-addresses"));
+
+ int64_t previous_declined = 0;
+
+ if (observation) {
+ previous_declined = observation->getInteger().first;
+ }
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", id, "assigned-nas" ),
+ int64_t(0));
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", id, "assigned-pds"),
+ int64_t(0));
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", id, "declined-addresses"),
+ int64_t(0));
+
+ StatsMgr::instance().addValue("declined-addresses", -previous_declined);
+ } else {
+ // Wipe them all!
+ ConstSrvConfigPtr config = CfgMgr::instance().getCurrentCfg();
+ ConstCfgSubnets6Ptr subnets = config->getCfgSubnets6();
+ const Subnet6Collection * subs = subnets->getAll();
+
+ // Go over all subnets and wipe leases in each of them.
+ for (auto sub : *subs) {
+ num += LeaseMgrFactory::instance().wipeLeases6(sub->getID());
+ ids << " " << sub->getID();
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", sub->getID(), "assigned-nas" ),
+ int64_t(0));
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", sub->getID(), "assigned-pds"),
+ int64_t(0));
+
+ StatsMgr::instance().setValue(
+ StatsMgr::generateName("subnet", sub->getID(), "declined-addresses"),
+ int64_t(0));
+ }
+
+ StatsMgr::instance().setValue("declined-addresses", int64_t(0));
+ }
+
+ stringstream tmp;
+ tmp << "Deleted " << num << " IPv6 lease(s) from subnet(s)" << ids.str();
+ ConstElementPtr response = createAnswer(num ? CONTROL_RESULT_SUCCESS
+ : CONTROL_RESULT_EMPTY, tmp.str());
+ setResponse(handle, response);
+ } catch (const std::exception& ex) {
+ setErrorResponse(handle, ex.what());
+ return (1);
+ }
+
+ return (0);
+}
+
+Lease6Ptr
+LeaseCmdsImpl::getIPv6LeaseForDelete(const Parameters& parameters) const {
+ Lease6Ptr lease6;
+
+ switch (parameters.query_type) {
+ case Parameters::TYPE_ADDR: {
+ // If address was specified explicitly, let's use it as is.
+
+ // Let's see if there's such a lease at all.
+ lease6 = LeaseMgrFactory::instance().getLease6(parameters.lease_type,
+ parameters.addr);
+ if (!lease6) {
+ lease6.reset(new Lease6());
+ lease6->addr_ = parameters.addr;
+ }
+ break;
+ }
+ case Parameters::TYPE_HWADDR: {
+ isc_throw(InvalidParameter, "Delete by hw-address is not allowed in v6.");
+ break;
+ }
+ case Parameters::TYPE_DUID: {
+ if (!parameters.duid) {
+ isc_throw(InvalidParameter, "Program error: Query by duid "
+ "requires duid to be specified");
+ }
+
+ // Let's see if there's such a lease at all.
+ lease6 = LeaseMgrFactory::instance().getLease6(parameters.lease_type,
+ *parameters.duid,
+ parameters.iaid,
+ parameters.subnet_id);
+ break;
+ }
+ default:
+ isc_throw(InvalidOperation, "Unknown query type: "
+ << static_cast<int>(parameters.query_type));
+ }
+
+ return (lease6);
+}
+
+IOAddress
+LeaseCmdsImpl::getAddressParam(ConstElementPtr params, const std::string name,
+ short family) const {
+ ConstElementPtr param = params->get(name);
+ if (!param) {
+ isc_throw(BadValue, "'" << name << "' parameter is missing.");
+ }
+
+ if (param->getType() != Element::string) {
+ isc_throw(BadValue, "'" << name << "' is not a string.");
+ }
+
+ IOAddress addr(0);
+ try {
+ addr = IOAddress(param->stringValue());
+ } catch (const std::exception& ex) {
+ isc_throw(BadValue, "'" << param->stringValue()
+ << "' is not a valid IP address.");
+ }
+
+ if (addr.getFamily() != family) {
+ isc_throw(BadValue, "Invalid "
+ << (family == AF_INET6 ? "IPv6" : "IPv4")
+ << " address specified: " << param->stringValue());
+ }
+
+ return (addr);
+}
+
+int
+LeaseCmdsImpl::lease4ResendDdnsHandler(CalloutHandle& handle) {
+ std::stringstream ss;
+ int resp_code = CONTROL_RESULT_ERROR;
+
+ try {
+ extractCommand(handle);
+
+ // Get the target lease address. Invalid value will throw.
+ IOAddress addr = getAddressParam(cmd_args_, "ip-address", AF_INET);
+
+ if (!CfgMgr::instance().getD2ClientMgr().ddnsEnabled()) {
+ ss << "DDNS updating is not enabled";
+ } else {
+ // Find the lease.
+ Lease4Ptr lease = LeaseMgrFactory::instance().getLease4(addr);
+ if (!lease) {
+ ss << "No lease found for: " << addr.toText();
+ resp_code = CONTROL_RESULT_EMPTY;
+ } else if (lease->hostname_.empty()) {
+ ss << "Lease for: " << addr.toText()
+ << ", has no hostname, nothing to update";
+ } else if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
+ ss << "Neither forward nor reverse updates enabled for lease for: "
+ << addr.toText();
+ } else {
+ // We have a lease with a hostname and updates in at least
+ // one direction enabled. Queue an NCR for it.
+ queueNCR(CHG_ADD, lease);
+ ss << "NCR generated for: " << addr.toText()
+ << ", hostname: " << lease->hostname_;
+ setSuccessResponse(handle, ss.str());
+ LOG_INFO(lease_cmds_logger, LEASE_CMDS_RESEND_DDNS4).arg(ss.str());
+ return (0);
+ }
+ }
+ } catch (const std::exception& ex) {
+ ss << ex.what();
+ }
+
+ LOG_ERROR(lease_cmds_logger, LEASE_CMDS_RESEND_DDNS4_FAILED).arg(ss.str());
+ setErrorResponse(handle, ss.str(), resp_code);
+ return (resp_code == CONTROL_RESULT_EMPTY ? 0 : 1);
+}
+
+int
+LeaseCmdsImpl::lease6ResendDdnsHandler(CalloutHandle& handle) {
+ std::stringstream ss;
+ int resp_code = CONTROL_RESULT_ERROR;
+
+ try {
+ extractCommand(handle);
+
+ // Get the target lease address. Invalid value will throw.
+ IOAddress addr = getAddressParam(cmd_args_, "ip-address", AF_INET6);
+
+ if (!CfgMgr::instance().getD2ClientMgr().ddnsEnabled()) {
+ ss << "DDNS updating is not enabled";
+ } else {
+ // Find the lease.
+ Lease6Ptr lease = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA, addr);
+ if (!lease) {
+ ss << "No lease found for: " << addr.toText();
+ resp_code = CONTROL_RESULT_EMPTY;
+ } else if (lease->hostname_.empty()) {
+ ss << "Lease for: " << addr.toText()
+ << ", has no hostname, nothing to update";
+ } else if (!lease->fqdn_fwd_ && !lease->fqdn_rev_) {
+ ss << "Neither forward nor reverse updates enabled for lease for: "
+ << addr.toText();
+ } else {
+ // We have a lease with a hostname and updates in at least
+ // one direction enabled. Queue an NCR for it.
+ queueNCR(CHG_ADD, lease);
+ ss << "NCR generated for: " << addr.toText()
+ << ", hostname: " << lease->hostname_;
+ setSuccessResponse(handle, ss.str());
+ LOG_INFO(lease_cmds_logger, LEASE_CMDS_RESEND_DDNS6).arg(ss.str());
+ return (0);
+ }
+ }
+ } catch (const std::exception& ex) {
+ ss << ex.what();
+ }
+
+ LOG_ERROR(lease_cmds_logger, LEASE_CMDS_RESEND_DDNS6_FAILED).arg(ss.str());
+ setErrorResponse(handle, ss.str(), resp_code);
+ return (resp_code == CONTROL_RESULT_EMPTY ? 0 : 1);
+}
+
+ElementPtr
+LeaseCmdsImpl::createFailedLeaseMap(const Lease::Type& lease_type,
+ const IOAddress& lease_address,
+ const DuidPtr& duid,
+ const int control_result,
+ const std::string& error_message) const {
+ auto failed_lease_map = Element::createMap();
+ failed_lease_map->set("type", Element::create(Lease::typeToText(lease_type)));
+
+ if (!lease_address.isV6Zero()) {
+ failed_lease_map->set("ip-address", Element::create(lease_address.toText()));
+
+ } else if (duid) {
+ failed_lease_map->set("duid", Element::create(duid->toText()));
+ }
+
+ // Associate the result with the lease.
+ failed_lease_map->set("result", Element::create(control_result));
+ failed_lease_map->set("error-message", Element::create(error_message));
+
+ return (failed_lease_map);
+}
+
+int
+LeaseCmds::leaseAddHandler(CalloutHandle& handle) {
+ return (impl_->leaseAddHandler(handle));
+}
+
+int
+LeaseCmds::lease6BulkApplyHandler(CalloutHandle& handle) {
+ return (impl_->lease6BulkApplyHandler(handle));
+}
+
+int
+LeaseCmds::leaseGetHandler(CalloutHandle& handle) {
+ return (impl_->leaseGetHandler(handle));
+}
+
+int
+LeaseCmds::leaseGetAllHandler(hooks::CalloutHandle& handle) {
+ return (impl_->leaseGetAllHandler(handle));
+}
+
+int
+LeaseCmds::leaseGetPageHandler(hooks::CalloutHandle& handle) {
+ return (impl_->leaseGetPageHandler(handle));
+}
+
+int
+LeaseCmds::leaseGetByHwAddressHandler(hooks::CalloutHandle& handle) {
+ return (impl_->leaseGetByHwAddressHandler(handle));
+}
+
+int
+LeaseCmds::leaseGetByClientIdHandler(hooks::CalloutHandle& handle) {
+ return (impl_->leaseGetByClientIdHandler(handle));
+}
+
+int
+LeaseCmds::leaseGetByDuidHandler(hooks::CalloutHandle& handle) {
+ return (impl_->leaseGetByDuidHandler(handle));
+}
+
+int
+LeaseCmds::leaseGetByHostnameHandler(hooks::CalloutHandle& handle) {
+ return (impl_->leaseGetByHostnameHandler(handle));
+}
+
+int
+LeaseCmds::lease4DelHandler(CalloutHandle& handle) {
+ return (impl_->lease4DelHandler(handle));
+}
+
+int
+LeaseCmds::lease6DelHandler(CalloutHandle& handle) {
+ return (impl_->lease6DelHandler(handle));
+}
+
+int
+LeaseCmds::lease4UpdateHandler(CalloutHandle& handle) {
+ return (impl_->lease4UpdateHandler(handle));
+}
+
+int
+LeaseCmds::lease6UpdateHandler(CalloutHandle& handle) {
+ return (impl_->lease6UpdateHandler(handle));
+}
+
+int
+LeaseCmds::lease4WipeHandler(CalloutHandle& handle) {
+ MultiThreadingCriticalSection cs;
+ return (impl_->lease4WipeHandler(handle));
+}
+
+int
+LeaseCmds::lease6WipeHandler(CalloutHandle& handle) {
+ MultiThreadingCriticalSection cs;
+ return (impl_->lease6WipeHandler(handle));
+}
+
+int
+LeaseCmds::lease4ResendDdnsHandler(CalloutHandle& handle) {
+ return (impl_->lease4ResendDdnsHandler(handle));
+}
+
+int
+LeaseCmds::lease6ResendDdnsHandler(CalloutHandle& handle) {
+ return (impl_->lease6ResendDdnsHandler(handle));
+}
+
+LeaseCmds::LeaseCmds()
+ :impl_(new LeaseCmdsImpl()) {
+}
+
+} // end of namespace lease_cmds
+} // end of namespace isc
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.dox b/src/hooks/dhcp/lease_cmds/lease_cmds.dox
new file mode 100644
index 0000000..4b81c4a
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds.dox
@@ -0,0 +1,110 @@
+// Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+/**
+
+@page libdhcp_lease_cmds Kea Lease Commands Hooks Library
+
+@section libdhcp_lease_cmdsIntro Introduction
+
+Welcome to Kea Lease Commands Hooks Library. This documentation is addressed to
+developers who are interested in the internal operation of the Lease Commands
+library. This file provides information needed to understand and perhaps extend
+this library.
+
+This documentation is stand-alone: you should have read and understood <a
+href="https://reports.kea.isc.org/dev_guide/">Kea Developer's Guide</a> and in
+particular its section about hooks.
+
+@section lease_cmds Lease Commands Overview
+
+Lease Commands (or lease_cmds) is a Hook library that can be loaded by Kea to
+extend it with additional mechanisms.
+
+The primary purpose of this library is to provide commands that manage leases.
+As such, the whole library is structured around command handlers. When the
+library is loaded it registers a number of handlers for new commands. When a
+command is issued (be it directly via control channel or indirectly via REST
+interface from control agent), the code receives a JSON command with
+parameters. Those are parsed and then actual operation commences. This
+operation always interacts with an instantiation of isc::dhcp::LeaseMgr
+instance, which is Kea's way of storing leases. At the time of writing this text
+(Aug. 2017), Kea supports four types of lease managers: memfile, MySQL or
+PostgreSQL. The lease commands provided by this library provide a unified
+interface for those backends.
+
+As with other hooks, this one also keeps its code in a separate namespace which
+corresponds to the file name of the library: isc::lease_cmds.
+
+@section lease_cmdsCode Lease Commands Code Overview
+
+The library operation starts with Kea calling the load() function (file
+load_unload.cc). It instantiates an isc::lease_cmds::LeaseCmds object.
+The constructor of that object registers all of the lease commands. For a list,
+see @ref isc::lease_cmds::LeaseCmds class documentation. This class uses Pimpl
+design pattern, thus the real implementation is hidden in isc::lease_cmds::LeaseCmdsImpl.
+
+Almost every command has its own handler, except few that share the same handler
+between v4 and v6 due to its similarity. For example
+isc::lease_cmds::LeaseCmdsImpl::leaseAddHandler handles lease4-add and lease6-add
+commands. Although the details differ between handlers, the general approach
+is the same. First, it starts with parameters sanitization and then some
+interaction with isc::dhcp::LeaseMgr is conducted.
+
+For commands that do something with a specific lease (lease4-get, lease6-get,
+lease4-del, lease6-del), there is a @ref isc::lease_cmds::LeaseCmdsImpl::Parameters
+class that contains parsed elements.
+
+For details see documentation and code of the following handlers:
+- @ref isc::lease_cmds::LeaseCmdsImpl::leaseAddHandler (lease4-add, lease6-add)
+- @ref isc::lease_cmds::LeaseCmdsImpl::leaseGetHandler (lease4-get, lease6-get)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease4DelHandler (lease4-del)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease6DelHandler (lease6-del)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease4UpdateHandler (lease4-update)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease6UpdateHandler (lease6-update)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease4WipeHandler (lease4-wipe)
+- @ref isc::lease_cmds::LeaseCmdsImpl::lease6WipeHandler (lease6-wipe)
+
+@section lease_cmdsDesigns Lease Commands Design choices
+
+The lease manipulation commands were implemented to provide a convenient interface
+for sysadmins. The primary goal was to offer a way to interact with the live
+lease database in unified way, regardless of the actual backend being used.
+
+For some backends (MySQL and PostgreSQL) it is possible to interact directly
+with the backend while Kea is running and possibly change its content. This
+ability is both powerful and dangerous. In particular, only rudimentary
+checks are enforced by the DB schemas (e.g. not possible to have two leases
+for the same address). However, it does not prevent sysadmins from making
+more obscure errors, like inserting leases for subnets that do not exist
+or configuring an address that is topologically outside of the subnet to which
+it should belong. These kind of checks are only possible by DHCP-aware
+code, which this library provides.
+
+Some of the queries may require a seemingly odd set of parameters. For example,
+lease6-get query requires at least DUID, subnet-id and IAID to retrieve a lease
+by DUID. The need for a sysadmin to know and specify an IAID is troublesome.
+However, the guiding principle here was to use whatever queries were already
+exposed by the lease manager and not introduce new indexes, unless absolutely
+necessary. This ensures that there is no performance degradation when the
+library is loaded. The only exception for that was lease4-wipe and lease6-wipe
+commands that remove all leases from specific subnet. As there were no
+queries that could retrieve or otherwise enumerate leases for a specific subnet,
+a new query type and a new index had to be added.
+
+@section lease_cmdsMTCompatibility Multi-Threading Compatibility
+
+The Lease Commands Hook library is compatible with multi-threading.
+All commands protect the resource they touch so with split spaces
+a race with the allocation engine should not be possible when
+called by a high availability server with a sane configuration.
+When a race is detected a critical section is used.
+
+Note an expired lease reclamation is called only from the periodic
+process or by a command. In both cases it is executed by the main
+thread so the same thread as lease commands.
+
+*/
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds.h b/src/hooks/dhcp/lease_cmds/lease_cmds.h
new file mode 100644
index 0000000..31dd8b6
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds.h
@@ -0,0 +1,593 @@
+// Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef LEASE_CMDS_H
+#define LEASE_CMDS_H
+
+#include <cc/data.h>
+#include <hooks/hooks.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace lease_cmds {
+
+/// @brief Forward declaration of implementation class.
+class LeaseCmdsImpl;
+
+/// @brief Implements the logic for processing commands pertaining to
+/// lease manipulation.
+///
+/// This class is used by the callouts implementing command handlers for
+/// lease manipulations.
+class LeaseCmds {
+public:
+ /// @brief Constructor.
+ ///
+ /// It creates an instance of the @c LeaseCmdsImpl.
+ LeaseCmds();
+
+ /// @brief lease4-add, lease6-add command handler
+ ///
+ /// This command attempts to add a lease.
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// This function covers both v4 and v6 leases.
+ ///
+ /// Example command for v4:
+ /// {
+ /// "command": "lease4-add",
+ /// "parameters": {
+ /// "address": "192.0.2.1",
+ /// "hwaddr": "00:01:02:03:04:05",
+ /// "client-id": "this-is-a-client",
+ /// "valid-lft": 3600,
+ /// "expire": 12345678,
+ /// "subnet-id": 1,
+ /// "fqdn-fwd": true,
+ /// "fqdn-rev": true,
+ /// "hostname": "myhost.example.org",
+ /// "state": 0
+ /// }
+ /// }
+ /// Example command for v6:
+ /// {
+ /// "command": "lease6-add",
+ /// "arguments": {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:abcd::",
+ /// "type": "IA_PD",
+ /// "prefix-len": 48,
+ /// "duid": "01:02:03:04:05:06:07:08",
+ /// "iaid": 1234,
+ /// "preferred-lft": 500,
+ /// "valid-lft": 1000,
+ /// "expire": 12345678,
+ /// "fqdn-fwd": true,
+ /// "fqdn-rev": true,
+ /// "hostname": "urania.example.org""
+ /// }
+ /// }
+ ///
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// add command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ leaseAddHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease6-bulk-apply command handler
+ ///
+ /// This command conveys information about multiple leases to be added,
+ /// updated or deleted. This command should be used instead of lease6-add,
+ /// lease6-update and lease6-del when it is desired to apply multiple
+ /// lease changes within a single transaction. This is much faster and
+ /// should be used in cases when the performance is critical. This
+ /// command was added as a result of our experience with High Availability
+ /// where multiple IPv6 addresses and/or prefixes can be allocated for
+ /// a single DHCPv6 packet.
+ ///
+ /// @note Unlike leaseX-del, this command does not support "update-ddns" and
+ /// this will not generate CHG_REMOVEs for deleted leases.
+ ///
+ /// Example structure of the command:
+ ///
+ /// {
+ /// "command": "lease6-bulk-apply",
+ /// "arguments": {
+ /// "deleted-leases": [
+ /// {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:abcd::",
+ /// "type": "IA_PD",
+ /// ...
+ /// },
+ /// {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:abcd::234",
+ /// "type": "IA_NA",
+ /// ...
+ /// }
+ /// ],
+ /// "leases": [
+ /// {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:cafe::",
+ /// "type": "IA_PD",
+ /// ...
+ /// },
+ /// {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:abcd::333",
+ /// "type": "IA_NA",
+ /// ...
+ /// }
+ /// ]
+ /// }
+ /// }
+ ///
+ /// The response indicates which of the leases failed to be applied.
+ /// For example:
+ ///
+ /// {
+ /// "result": 0,
+ /// "text": IPv6 leases bulk apply completed.
+ /// "arguments": {
+ /// "failed-deleted-leases": [
+ /// {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:abcd::",
+ /// "type": "IA_PD"
+ /// }
+ /// ],
+ /// "failed-leases": [
+ /// {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8:cafe::",
+ /// "type": "IA_PD",
+ /// ...
+ /// }
+ /// ]
+ /// }
+ /// }
+ ///
+ /// The command handler first attempts to delete all leases listed in
+ /// the "deleted-leases" list. Next, it adds the leases listed in the
+ /// "leases" list. If any of these leases already exists, it is updated.
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// add command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease6BulkApplyHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get, lease6-get command handler
+ ///
+ /// This command attempts to retrieve a lease that match selected criteria.
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// The following types of parameters are supported:
+ /// - (subnet-id, address) for both v4 and v6
+ /// - (subnet-id, identifier-type, identifier) for v4
+ /// - (subnet-id, type, iana, identifier-type, identifier) for v6
+ ///
+ /// Example command for query by (subnet-id, address):
+ /// {
+ /// "command": "lease4-get",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "ip-address": "192.0.2.202"
+ /// }
+ /// }
+ ///
+ /// Example command for query by (subnet-id, identifier-type, identifier)
+ /// {
+ /// "command": "lease4-get",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "identifier-type": "hw-address",
+ /// "identifier": "00:01:02:03:04:05"
+ /// }
+ /// }
+ ///
+ /// Example command for query by (subnet-id, type, iana, identifier-type,
+ /// identifier):
+ /// {
+ /// "command": "lease6-get",
+ /// "arguments": {
+ /// "subnet-id": 66,
+ /// "iaid": 42,
+ /// "type": "IA_NA",
+ /// "identifier-type": "duid",
+ /// "identifier": "77:77:77:77:77:77:77:77"
+ /// }
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ /// @return result of the operation (includes lease details, if found),
+ /// 1 if an error occurs, 3 if lease not found.
+ int
+ leaseGetHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-all, lease6-get-all commands handler
+ ///
+ /// These commands attempt to retrieve all IPv4 or IPv6 leases,
+ /// or all IPv4 or all IPv6 leases belonging to the particular
+ /// subnets. If no subnet identifiers are provided, it returns all
+ /// IPv4 or IPv6 leases from the database.
+ ///
+ /// Example command for IPv4 query by (subnet-ids):
+ /// {
+ /// "command": "lease4-get-all",
+ /// "arguments": {
+ /// "subnets": [ 1, 2, 3, 4 ]
+ /// }
+ /// }
+ ///
+ /// Example command for retrieving all IPv6 leases:
+ /// {
+ /// "command": "lease6-get-all",
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetAllHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-page, lease6-get-page commands handler
+ ///
+ /// These commands attempt to retrieve 1 page of all IPv4 or IPv6
+ /// leases. The size of the page is specified by the caller. The
+ /// caller also specifies the last address returned in the previous
+ /// page. The new page starts from the first address following the
+ /// address specified by the caller. If the first page should be
+ /// returned the IPv4 zero address, IPv6 zero address or the keyword
+ /// "start" should be provided instead of the last address.
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get commands JSON text in the "command" argument.
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetPageHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-by-hw-address command handler
+ ///
+ /// This command attempts to retrieve all IPv4 leases with a particular
+ /// hardware address.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease4-get-by-hw-address",
+ /// "arguments": {
+ /// "hwaddr": "00:01:02:03:04:05"
+ /// }
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByHwAddressHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-by-client-id command handler
+ ///
+ /// This command attempts to retrieve all IPv4 leases with a particular
+ /// Client Id.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease4-get-by-client-id",
+ /// "arguments": {
+ /// "client-id": "this-is-a-client"
+ /// }
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByClientIdHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease6-get-by-duid command handler
+ ///
+ /// This command attempts to retrieve all IPv6 leases with a particular
+ /// DUID.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease6-get-by-duid",
+ /// "arguments": {
+ /// "duid": "01:02:03:04:05:06:07:08"
+ /// }
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByDuidHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-get-by-hostname and lease6-get-by-hostname commands
+ /// handler
+ ///
+ /// These commands attempt to retrieve all IPv4 or Ipv6 leases with
+ /// a particular hostname.
+ ///
+ /// Example command for v4:
+ /// {
+ /// "command": "lease4-get-by-hostname",
+ /// "arguments": {
+ /// "hostname": "urania.example.org"
+ /// }
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// get command JSON text in the "command" argument
+ /// @return 0 if the handler has been invoked successfully, 1 if an
+ /// error occurs, 3 if no leases are returned.
+ int
+ leaseGetByHostnameHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-del command handler
+ ///
+ /// This command attempts to delete an IPv4 lease that match selected
+ /// criteria.
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly. If the lease is deleted successfully, then a call
+ /// to @ref isc::dhcp::queueNCR() is issued, which to generate an
+ /// CHG_REMOVE request to kea-dhcp-ddns, if appropriate.
+ ///
+ /// Two types of parameters are supported: (subnet-id, address) or
+ /// (subnet-id, identifier-type, identifier).
+ ///
+ ///
+ /// Example command for deletion by (subnet-id, address):
+ /// {
+ /// "command": "lease4-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "ip-address": "192.0.2.202"
+ /// }
+ /// }
+ ///
+ /// Example command for deletion by (subnet-id, identifier-type, identifier)
+ /// {
+ /// "command": "lease4-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "identifier-type": "hw-address",
+ /// "identifier": "00:01:02:03:04:05"
+ /// }
+ /// }";
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// delete command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease4DelHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease6-del command handler
+ ///
+ /// This command attempts to delete a lease that match selected criteria.
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly. If the lease is deleted successfully, then a call
+ /// to @ref isc::dhcp::queueNCR() is issued, which to generate an
+ /// CHG_REMOVE request to kea-dhcp-ddns, if appropriate.
+ ///
+ /// Two types of parameters are supported: (subnet-id, address) or
+ /// (subnet-id, type, iaid, identifier-type, identifier).
+ ///
+ /// Example command for deletion by (subnet-id, address):
+ /// {
+ /// "command": "lease6-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "ip-address": "192.0.2.202"
+ /// }
+ /// }
+ ///
+ /// Example command for deletion by (subnet-id, type, iaid, identifier-type,
+ /// identifier):
+ /// {
+ /// "command": "lease6-del",
+ /// "arguments": {
+ /// "subnet-id": 1,
+ /// "type": "IA_NA",
+ /// "iaid": 123456,
+ /// "identifier-type": "hw-address",
+ /// "identifier": "00:01:02:03:04:05"
+ /// }
+ /// }";
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// delete command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease6DelHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-update handler
+ ///
+ /// This command attempts to update existing IPv4 lease. The parameters
+ /// specified will replace existing lease. The only condition is that
+ /// the IP address must not change. If you want to change the IP address,
+ /// please use lease4-del and lease4-add instead.
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease4-update",
+ /// "arguments": {
+ /// "subnet-id": 44,
+ /// "ip-address": "192.0.2.1",
+ /// "hw-address": "1a:1b:1c:1d:1e:1f",
+ /// "hostname": "newhostname.example.org"
+ /// }
+ /// };
+ ///
+ /// The optional 'force-create' boolean parameter may be specified to force
+ /// the lease to be created if it doesn't exist. By default, this parameter
+ /// is set to false, which means that the lease is not created.
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// update command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease4UpdateHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease6-update handler
+ ///
+ /// This command attempts to update existing IPv6 lease. The parameters
+ /// specified will replace existing lease. The only condition is that
+ /// the IP address must not change. If you want to change the IP address,
+ /// please use lease6-del and lease6-add instead.
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease6-update",
+ /// "arguments": {
+ /// "subnet-id": 66,
+ /// "ip-address": "2001:db8::1",
+ /// "iaid": 7654321,
+ /// "duid": "88:88:88:88:88:88:88:88",
+ /// "hostname": "newhostname.example.org"
+ /// }
+ /// }";
+ ///
+ /// The optional 'force-create' boolean parameter may be specified to force
+ /// the lease to be created if it doesn't exist. By default, this parameter
+ /// is set to false, which means that the lease is not created.
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// update command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease6UpdateHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-wipe handler
+ ///
+ /// This commands attempts to remove all IPv4 leases from a specific
+ /// subnet. Currently the leases are removed from the database,
+ /// without any processing (like calling hooks or doing DDNS
+ /// cleanups).
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease4-wipe",
+ /// "arguments": {
+ /// "subnet-id": 44
+ /// }
+ /// }";
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// wipe command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease4WipeHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease6-wipe handler
+ ///
+ /// This commands attempts to remove all IPv4 leases from a specific
+ /// subnet. Currently the leases are removed from the database,
+ /// without any processing (like calling hooks or doing DDNS
+ /// cleanups).
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// Example command:
+ /// {
+ /// "command": "lease4-wipe",
+ /// "arguments": {
+ /// "subnet-id": 44
+ /// }
+ /// };
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// wipe command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease6WipeHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease4-resend-ddns command handler
+ ///
+ /// This command attempts to resend the DDNS updates for the IPv4 lease that
+ /// matches the selection criteria.
+ ///
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// A single parameter is supported: ip-address:
+ ///
+ /// Example command to resend DDNS based on existing FDQN and flags
+ /// {
+ /// "command": "lease4-resend-ddns",
+ /// "arguments": {
+ /// "ip-address": "192.0.2.202"
+ /// }
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// lease4-resend-ddns command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease4ResendDdnsHandler(hooks::CalloutHandle& handle);
+
+ /// @brief lease6-resend-ddns command handler
+ ///
+ /// This command attempts to resend the DDNS updates for the IPv6 lease that
+ /// matches the selection criteria.
+ ///
+ /// It extracts the command name and arguments from the given Callouthandle,
+ /// attempts to process them, and then set's the handle's "response"
+ /// argument accordingly.
+ ///
+ /// A single parameter is supported: ip-address:
+ ///
+ /// Example command to resend DDNS based on existing FDQN and flags
+ /// {
+ /// "command": "lease6-resend-ddns",
+ /// "arguments": {
+ /// "ip-address": "2001:db8:abcd::",
+ /// }
+ /// }
+ ///
+ /// @param handle Callout context - which is expected to contain the
+ /// lease6-resend-ddns command JSON text in the "command" argument
+ /// @return result of the operation
+ int
+ lease6ResendDdnsHandler(hooks::CalloutHandle& handle);
+
+private:
+ /// Pointer to the actual implementation
+ boost::shared_ptr<LeaseCmdsImpl> impl_;
+};
+
+};
+};
+
+#endif
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc
new file mode 100644
index 0000000..a0a1e4b
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_callouts.cc
@@ -0,0 +1,318 @@
+// Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+// Functions accessed by the hooks framework use C linkage to avoid the name
+// mangling that accompanies use of the C++ compiler as well as to avoid
+// issues related to namespaces.
+
+#include <config.h>
+
+#include <lease_cmds.h>
+#include <lease_cmds_log.h>
+#include <cc/command_interpreter.h>
+#include <hooks/hooks.h>
+
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::hooks;
+using namespace isc::lease_cmds;
+
+extern "C" {
+
+/// @brief This is a command callout for 'lease4-add' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease4_add(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.leaseAddHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-add' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease6_add(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.leaseAddHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-bulk-apply' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease6_bulk_apply(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.lease6BulkApplyHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-get' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease4_get(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.leaseGetHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-get' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease6_get(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.leaseGetHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-get-all' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease4_get_all(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetAllHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-get-all' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease6_get_all(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetAllHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-get-page' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease4_get_page(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetPageHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-get-page' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease6_get_page(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetPageHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-get-by-hw-address' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease4_get_by_hw_address(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetByHwAddressHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-get-by-client-id' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease4_get_by_client_id(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetByClientIdHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-get-by-duid' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease6_get_by_duid(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetByDuidHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-get-by-hostname' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease4_get_by_hostname(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetByHostnameHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-get-by-hostname' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 if an error occurs, 3 if no leases are returned.
+int lease6_get_by_hostname(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return (lease_cmds.leaseGetByHostnameHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-del' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease4_del(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease4DelHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-del' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease6_del(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease6DelHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-update' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease4_update(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease4UpdateHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-update' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease6_update(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease6UpdateHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-wipe' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease4_wipe(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease4WipeHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-wipe' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease6_wipe(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease6WipeHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease4-resend-ddns' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease4_resend_ddns(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease4ResendDdnsHandler(handle));
+}
+
+/// @brief This is a command callout for 'lease6-resend-ddns' command.
+///
+/// @param handle Callout handle used to retrieve a command and
+/// provide a response.
+/// @return 0 if this callout has been invoked successfully,
+/// 1 otherwise.
+int lease6_resend_ddns(CalloutHandle& handle) {
+ LeaseCmds lease_cmds;
+ return(lease_cmds.lease6ResendDdnsHandler(handle));
+}
+
+/// @brief This function is called when the library is loaded.
+///
+/// @param handle library handle
+/// @return 0 when initialization is successful, 1 otherwise
+int load(LibraryHandle& handle) {
+ handle.registerCommandCallout("lease4-add", lease4_add);
+ handle.registerCommandCallout("lease6-add", lease6_add);
+ handle.registerCommandCallout("lease6-bulk-apply", lease6_bulk_apply);
+ handle.registerCommandCallout("lease4-get", lease4_get);
+ handle.registerCommandCallout("lease6-get", lease6_get);
+ handle.registerCommandCallout("lease4-get-all", lease4_get_all);
+ handle.registerCommandCallout("lease6-get-all", lease6_get_all);
+ handle.registerCommandCallout("lease4-get-page", lease4_get_page);
+ handle.registerCommandCallout("lease6-get-page", lease6_get_page);
+ handle.registerCommandCallout("lease4-get-by-hw-address",
+ lease4_get_by_hw_address);
+ handle.registerCommandCallout("lease4-get-by-client-id",
+ lease4_get_by_client_id);
+ handle.registerCommandCallout("lease6-get-by-duid", lease6_get_by_duid);
+ handle.registerCommandCallout("lease4-get-by-hostname",
+ lease4_get_by_hostname);
+ handle.registerCommandCallout("lease6-get-by-hostname",
+ lease6_get_by_hostname);
+ handle.registerCommandCallout("lease4-del", lease4_del);
+ handle.registerCommandCallout("lease6-del", lease6_del);
+ handle.registerCommandCallout("lease4-update", lease4_update);
+ handle.registerCommandCallout("lease6-update", lease6_update);
+ handle.registerCommandCallout("lease4-wipe", lease4_wipe);
+ handle.registerCommandCallout("lease6-wipe", lease6_wipe);
+ handle.registerCommandCallout("lease4-resend-ddns", lease4_resend_ddns);
+ handle.registerCommandCallout("lease6-resend-ddns", lease6_resend_ddns);
+
+ LOG_INFO(lease_cmds_logger, LEASE_CMDS_INIT_OK);
+ return (0);
+}
+
+/// @brief This function is called when the library is unloaded.
+///
+/// @return 0 if deregistration was successful, 1 otherwise
+int unload() {
+ LOG_INFO(lease_cmds_logger, LEASE_CMDS_DEINIT_OK);
+ return (0);
+}
+
+/// @brief This function is called to retrieve the multi-threading compatibility.
+///
+/// @return 1 which means compatible with multi-threading.
+int multi_threading_compatible() {
+ return (1);
+}
+
+} // end extern "C"
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc
new file mode 100644
index 0000000..642bfb1
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_log.cc
@@ -0,0 +1,18 @@
+// Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <lease_cmds_log.h>
+
+namespace isc {
+namespace lease_cmds {
+
+isc::log::Logger lease_cmds_logger("lease-cmds-hooks");
+
+}
+}
+
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_log.h b/src/hooks/dhcp/lease_cmds/lease_cmds_log.h
new file mode 100644
index 0000000..56172b0
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_log.h
@@ -0,0 +1,23 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef LEASE_CMD_LOG_H
+#define LEASE_CMD_LOG_H
+
+#include <log/logger_support.h>
+#include <log/macros.h>
+#include <lease_cmds_messages.h>
+
+namespace isc {
+namespace lease_cmds {
+
+extern isc::log::Logger lease_cmds_logger;
+
+} // end of isc::lease_cmds
+} // end of isc namespace
+
+
+#endif
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_messages.cc b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.cc
new file mode 100644
index 0000000..5e04bd1
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.cc
@@ -0,0 +1,49 @@
+// File created from ../../../../src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+extern const isc::log::MessageID LEASE_CMDS_ADD4 = "LEASE_CMDS_ADD4";
+extern const isc::log::MessageID LEASE_CMDS_ADD4_FAILED = "LEASE_CMDS_ADD4_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_ADD6 = "LEASE_CMDS_ADD6";
+extern const isc::log::MessageID LEASE_CMDS_ADD6_FAILED = "LEASE_CMDS_ADD6_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_DEINIT_FAILED = "LEASE_CMDS_DEINIT_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_DEINIT_OK = "LEASE_CMDS_DEINIT_OK";
+extern const isc::log::MessageID LEASE_CMDS_DEL4 = "LEASE_CMDS_DEL4";
+extern const isc::log::MessageID LEASE_CMDS_DEL4_FAILED = "LEASE_CMDS_DEL4_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_DEL6 = "LEASE_CMDS_DEL6";
+extern const isc::log::MessageID LEASE_CMDS_DEL6_FAILED = "LEASE_CMDS_DEL6_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_INIT_FAILED = "LEASE_CMDS_INIT_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_INIT_OK = "LEASE_CMDS_INIT_OK";
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4 = "LEASE_CMDS_RESEND_DDNS4";
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED = "LEASE_CMDS_RESEND_DDNS4_FAILED";
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6 = "LEASE_CMDS_RESEND_DDNS6";
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6_FAILED = "LEASE_CMDS_RESEND_DDNS6_FAILED";
+
+namespace {
+
+const char* values[] = {
+ "LEASE_CMDS_ADD4", "lease4-add command successful (parameters: %1)",
+ "LEASE_CMDS_ADD4_FAILED", "lease4-add command failed (parameters: %1, reason: %2)",
+ "LEASE_CMDS_ADD6", "lease6-add command successful (parameters: %1)",
+ "LEASE_CMDS_ADD6_FAILED", "Lease6-add command failed (parameters: %1, reason: %2)",
+ "LEASE_CMDS_DEINIT_FAILED", "unloading Lease Commands hooks library failed: %1",
+ "LEASE_CMDS_DEINIT_OK", "unloading Lease Commands hooks library successful",
+ "LEASE_CMDS_DEL4", "lease4-del command successful (parameters: %1)",
+ "LEASE_CMDS_DEL4_FAILED", "lease4-del command failed (parameters: %1, reason: %2)",
+ "LEASE_CMDS_DEL6", "lease4-del command successful (parameters: %1)",
+ "LEASE_CMDS_DEL6_FAILED", "lease6-del command failed (parameters: %1, reason: %2)",
+ "LEASE_CMDS_INIT_FAILED", "loading Lease Commands hooks library failed: %1",
+ "LEASE_CMDS_INIT_OK", "loading Lease Commands hooks library successful",
+ "LEASE_CMDS_RESEND_DDNS4", "lease4-resend-ddns command successful: %1",
+ "LEASE_CMDS_RESEND_DDNS4_FAILED", "lease4-resend-ddns command failed: %1",
+ "LEASE_CMDS_RESEND_DDNS6", "lease6-resend-ddns command successful: %1",
+ "LEASE_CMDS_RESEND_DDNS6_FAILED", "lease6-resend-ddns command failed: %1",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_messages.h b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.h
new file mode 100644
index 0000000..869a8c2
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.h
@@ -0,0 +1,25 @@
+// File created from ../../../../src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
+
+#ifndef LEASE_CMDS_MESSAGES_H
+#define LEASE_CMDS_MESSAGES_H
+
+#include <log/message_types.h>
+
+extern const isc::log::MessageID LEASE_CMDS_ADD4;
+extern const isc::log::MessageID LEASE_CMDS_ADD4_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_ADD6;
+extern const isc::log::MessageID LEASE_CMDS_ADD6_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_DEINIT_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_DEINIT_OK;
+extern const isc::log::MessageID LEASE_CMDS_DEL4;
+extern const isc::log::MessageID LEASE_CMDS_DEL4_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_DEL6;
+extern const isc::log::MessageID LEASE_CMDS_DEL6_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_INIT_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_INIT_OK;
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4;
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS4_FAILED;
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6;
+extern const isc::log::MessageID LEASE_CMDS_RESEND_DDNS6_FAILED;
+
+#endif // LEASE_CMDS_MESSAGES_H
diff --git a/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
new file mode 100644
index 0000000..3107456
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_cmds_messages.mes
@@ -0,0 +1,67 @@
+# Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC")
+
+% LEASE_CMDS_ADD4 lease4-add command successful (parameters: %1)
+The lease4-add command has been successful. Parameters of the host
+added are logged.
+
+% LEASE_CMDS_ADD4_FAILED lease4-add command failed (parameters: %1, reason: %2)
+The lease4-add command has failed. Both the reason as well as the
+parameters passed are logged.
+
+% LEASE_CMDS_ADD6 lease6-add command successful (parameters: %1)
+The lease6-add command has been successful. Parameters of the host
+added are logged.
+
+% LEASE_CMDS_ADD6_FAILED Lease6-add command failed (parameters: %1, reason: %2)
+The lease6-add command has failed. Both the reason as well as the
+parameters passed are logged.
+
+% LEASE_CMDS_DEINIT_FAILED unloading Lease Commands hooks library failed: %1
+This error message indicates an error during unloading the Lease Commands
+hooks library. The details of the error are provided as argument of
+the log message.
+
+% LEASE_CMDS_DEINIT_OK unloading Lease Commands hooks library successful
+This info message indicates that the Lease Commands hooks library has been
+removed successfully.
+
+% LEASE_CMDS_DEL4 lease4-del command successful (parameters: %1)
+The attempt to delete an IPv4 lease (lease4-del command) has been successful.
+Parameters of the host removed are logged.
+
+% LEASE_CMDS_DEL4_FAILED lease4-del command failed (parameters: %1, reason: %2)
+The attempt to delete an IPv4 lease (lease4-del command) has failed. Both the
+reason as well as the parameters passed are logged.
+
+% LEASE_CMDS_DEL6 lease4-del command successful (parameters: %1)
+The attempt to delete an IPv4 lease (lease4-del command) has been successful.
+Parameters of the host removed are logged.
+
+% LEASE_CMDS_DEL6_FAILED lease6-del command failed (parameters: %1, reason: %2)
+The attempt to delete an IPv6 lease (lease4-del command) has failed. Both the
+reason as well as the parameters passed are logged.
+
+% LEASE_CMDS_INIT_FAILED loading Lease Commands hooks library failed: %1
+This error message indicates an error during loading the Lease Commands
+hooks library. The details of the error are provided as argument of
+the log message.
+
+% LEASE_CMDS_INIT_OK loading Lease Commands hooks library successful
+This info message indicates that the Lease Commands hooks library has been
+loaded successfully. Enjoy!
+
+% LEASE_CMDS_RESEND_DDNS4 lease4-resend-ddns command successful: %1
+A request to update DNS for the requested IPv4 lease has been
+successfully queued for transmission to kea-dhcp-ddns.
+
+% LEASE_CMDS_RESEND_DDNS4_FAILED lease4-resend-ddns command failed: %1
+A request to update DNS for the requested IPv4 lease has failed. The
+reason for the failure is logged.
+
+% LEASE_CMDS_RESEND_DDNS6 lease6-resend-ddns command successful: %1
+A request to update DNS for the requested IPv6 lease has been
+successfully queued for transmission to kea-dhcp-ddns.
+
+% LEASE_CMDS_RESEND_DDNS6_FAILED lease6-resend-ddns command failed: %1
+A request to update DNS for the requested IPv6 lease has failed. The
+reason for the failure is logged.
diff --git a/src/hooks/dhcp/lease_cmds/lease_parser.cc b/src/hooks/dhcp/lease_cmds/lease_parser.cc
new file mode 100644
index 0000000..139f6ae
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_parser.cc
@@ -0,0 +1,383 @@
+// Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <cc/data.h>
+#include <dhcp/hwaddr.h>
+#include <asiolink/io_address.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/cfg_consistency.h>
+#include <lease_parser.h>
+
+#include <config.h>
+
+using namespace std;
+using namespace isc::dhcp;
+using namespace isc::data;
+using namespace isc::asiolink;
+
+namespace isc {
+namespace lease_cmds {
+
+Lease4Ptr
+Lease4Parser::parse(ConstSrvConfigPtr& cfg,
+ const ConstElementPtr& lease_info,
+ bool& force_create) {
+ if (!lease_info) {
+ isc_throw(BadValue, "lease information missing");
+ }
+
+ // These are mandatory parameters.
+ IOAddress addr = getAddress(lease_info, "ip-address");
+ if (!addr.isV4()) {
+ isc_throw(BadValue, "Non-IPv4 address specified: " << addr);
+ }
+
+ // Not a most straightforward conversion, but it works.
+ string hwaddr_txt = getString(lease_info, "hw-address");
+ HWAddr hwaddr = HWAddr::fromText(hwaddr_txt);
+ HWAddrPtr hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr));
+
+ // Now sort out the subnet-id. If specified, it must have correct value.
+ // If not specified, Kea will try to sort it out.
+ SubnetID subnet_id = 0;
+ if (lease_info->contains("subnet-id")) {
+ subnet_id = getUint32(lease_info, "subnet-id");
+ }
+ ConstSubnet4Ptr subnet;
+ if (subnet_id) {
+ // If subnet-id is specified, it has to match.
+ subnet = cfg->getCfgSubnets4()->getBySubnetId(subnet_id);
+ if (!subnet) {
+ isc_throw(BadValue, "Invalid subnet-id: No IPv4 subnet with subnet-id="
+ << subnet_id << " currently configured.");
+ }
+
+ if (!subnet->inRange(addr)) {
+ isc_throw(BadValue, "The address " << addr.toText() << " does not belong "
+ "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id);
+ }
+
+ } else {
+ // Subnet-id was not specified. Let's try to figure it out on our own.
+ subnet = cfg->getCfgSubnets4()->selectSubnet(addr);
+ if (!subnet) {
+ isc_throw(BadValue, "subnet-id not specified and failed to find a"
+ << " subnet for address " << addr);
+ }
+ subnet_id = subnet->getID();
+ }
+
+ // Client-id is optional.
+ ClientIdPtr client_id;
+ if (lease_info->contains("client-id")) {
+ string txt = getString(lease_info, "client-id");
+ client_id = ClientId::fromText(txt);
+ }
+
+ // These parameters are optional. If not specified, we'll derive them from
+ // the current subnet configuration, if possible.
+ uint32_t valid_lft = 0;
+ if (lease_info->contains("valid-lft")) {
+ valid_lft = getUint32(lease_info, "valid-lft");
+ } else {
+ valid_lft = subnet->getValid();
+ }
+
+ /// Let's calculate client last transmission time (cltt). If expiration
+ /// timestamp is specified explicitly, we will use that. Note there are no
+ /// checks whether this is in the past. There may be valid cases when user
+ /// wants to insert expired leases, e.g. when migrating from one DHCP server
+ /// to another and wants to migrate the database as is, without discarding
+ /// any leases.
+ time_t cltt;
+ if (lease_info->contains("expire")) {
+ int64_t expire_time = getInteger(lease_info, "expire");
+ if (expire_time <= 0) {
+ isc_throw(BadValue , "expiration time must be positive for address "
+ << addr);
+
+ } else if (expire_time < valid_lft) {
+ isc_throw(BadValue, "expiration time must be greater than valid lifetime"
+ " for address " << addr);
+ }
+ cltt = static_cast<time_t>(expire_time - valid_lft);
+ } else {
+ cltt = time(NULL);
+ }
+
+ bool fqdn_fwd = false;
+ if (lease_info->contains("fqdn-fwd")) {
+ fqdn_fwd = getBoolean(lease_info, "fqdn-fwd");
+ }
+ bool fqdn_rev = false;
+ if (lease_info->contains("fqdn-rev")) {
+ fqdn_rev = getBoolean(lease_info, "fqdn-rev");
+ }
+ string hostname;
+ if (lease_info->contains("hostname")) {
+ hostname = getString(lease_info, "hostname");
+ }
+ if (hostname.empty() && (fqdn_fwd || fqdn_rev)) {
+ isc_throw(BadValue, "No hostname specified and either forward or reverse"
+ " fqdn was set to true.");
+ }
+
+ uint32_t state = 0;
+ if (lease_info->contains("state")) {
+ state = getUint8(lease_info, "state");
+ }
+
+ // Check if the state value is sane.
+ if (state > Lease::STATE_EXPIRED_RECLAIMED) {
+ isc_throw(BadValue, "Invalid state value: " << state << ", supported "
+ "values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)");
+ }
+
+ // Handle user context.
+ ConstElementPtr ctx = lease_info->get("user-context");
+ if (ctx && (ctx->getType() != Element::map)) {
+ isc_throw(BadValue, "Invalid user context '" << ctx->str()
+ << "' is not a JSON map.");
+ }
+
+ // Handle comment.
+ ConstElementPtr comment = lease_info->get("comment");
+ if (comment) {
+ if (ctx && ctx->contains("comment")) {
+ isc_throw(BadValue, "Duplicated comment entry '" << comment->str()
+ << "' in user context '" << ctx->str() << "'");
+ }
+ ElementPtr copied;
+ if (ctx) {
+ copied = copy(ctx, 0);
+ } else {
+ copied = Element::createMap();
+ }
+ copied->set("comment", comment);
+ ctx = copied;
+ }
+
+ // Let's fabricate some data and we're ready to go.
+
+ Lease4Ptr l(new Lease4(addr, hwaddr_ptr, client_id, valid_lft,
+ cltt, subnet_id,
+ fqdn_fwd, fqdn_rev, hostname));
+ l->state_ = state;
+ l->setContext(ctx);
+
+ // Retrieve the optional flag indicating if the lease must be created when it
+ // doesn't exist during the update.
+ force_create = false;
+ if (lease_info->contains("force-create")) {
+ force_create = getBoolean(lease_info, "force-create");
+ }
+
+ return (l);
+}
+
+Lease6Ptr
+Lease6Parser::parse(ConstSrvConfigPtr& cfg,
+ const ConstElementPtr& lease_info,
+ bool& force_create) {
+ if (!lease_info) {
+ isc_throw(BadValue, "lease information missing");
+ }
+
+ // These are mandatory parameters.
+ IOAddress addr = getAddress(lease_info, "ip-address");
+ if (addr.isV4()) {
+ isc_throw(BadValue, "Non-IPv6 address specified: " << addr);
+ }
+
+ // Not a most straightforward conversion, but it works.
+ string duid_txt = getString(lease_info, "duid");
+ DUID duid = DUID::fromText(duid_txt);
+ DuidPtr duid_ptr = DuidPtr(new DUID(duid));
+
+ Lease::Type type = Lease::TYPE_NA;
+ uint8_t prefix_len = 128;
+ if (lease_info->contains("type")) {
+ string txt = getString(lease_info, "type");
+ if (txt == "IA_NA") {
+ type = Lease::TYPE_NA;
+ } else if (txt == "IA_TA") {
+ type = Lease::TYPE_TA;
+ } else if (txt == "IA_PD") {
+ type = Lease::TYPE_PD;
+
+ prefix_len = getUint8(lease_info, "prefix-len");
+ } else {
+ isc_throw(BadValue, "Incorrect lease type: " << txt << ", the only "
+ "supported values are: na, ta and pd");
+ }
+ }
+
+ // Now sort out the subnet-id. If specified, it must have correct value.
+ // If not specified, Kea will try to sort it out.
+ SubnetID subnet_id = 0;
+ if (lease_info->contains("subnet-id")) {
+ subnet_id = getUint32(lease_info, "subnet-id");
+ }
+
+ // Check if the subnet-id specified is sane.
+ ConstSubnet6Ptr subnet;
+ if (subnet_id) {
+ // If subnet-id is specified, it has to match.
+ subnet = cfg->getCfgSubnets6()->getBySubnetId(subnet_id);
+ if (!subnet) {
+ isc_throw(BadValue, "Invalid subnet-id: No IPv6 subnet with subnet-id="
+ << subnet_id << " currently configured.");
+ }
+
+ // Check if the address specified really belongs to the subnet.
+ if ((type == Lease::TYPE_NA) && !subnet->inRange(addr)) {
+ isc_throw(BadValue, "The address " << addr.toText() << " does not belong "
+ "to subnet " << subnet->toText() << ", subnet-id=" << subnet_id);
+ }
+
+ } else {
+ if (type != Lease::TYPE_NA) {
+ isc_throw(BadValue, "Subnet-id is 0 or not specified. This is allowed for"
+ " address leases only, not prefix leases.");
+ }
+ subnet = cfg->getCfgSubnets6()->selectSubnet(addr);
+ if (!subnet) {
+ isc_throw(BadValue, "subnet-id not specified and failed to find a "
+ "subnet for address " << addr);
+ }
+ subnet_id = subnet->getID();
+ }
+
+ uint32_t iaid = getUint32(lease_info, "iaid");
+
+ // Hw-address is optional in v6 leases.
+ HWAddrPtr hwaddr_ptr;
+ if (lease_info->contains("hw-address")) {
+ string hwaddr_txt = getString(lease_info, "hw-address");
+ HWAddr hwaddr = HWAddr::fromText(hwaddr_txt);
+ hwaddr_ptr = HWAddrPtr(new HWAddr(hwaddr));
+ }
+
+ // These parameters are optional. If not specified, we'll derive them
+ // from the current subnet configuration, if possible.
+ uint32_t valid_lft = 0;
+ if (lease_info->contains("valid-lft")) {
+ valid_lft = getUint32(lease_info, "valid-lft");
+ } else {
+ valid_lft = subnet->getValid();
+ }
+
+ // These parameters are optional. If not specified, we'll derive them
+ // from the current subnet configuration, if possible.
+ uint32_t pref_lft = 0;
+ if (lease_info->contains("preferred-lft")) {
+ pref_lft = getUint32(lease_info, "preferred-lft");
+ } else {
+ pref_lft = subnet->getValid();
+ }
+
+ /// Let's calculate client last transmission time (cltt). If expiration
+ /// timestamp is specified explicitly, we will use that. Note there are
+ /// no checks whether this is in the past. There may be valid cases when
+ /// user wants to insert expired leases, e.g. when migrating from one
+ /// DHCP server to another and wants to migrate the database as is, without
+ /// discarding any leases.
+ time_t cltt;
+ if (lease_info->contains("expire")) {
+ int64_t expire_time = getInteger(lease_info, "expire");
+ if (expire_time <= 0) {
+ isc_throw(BadValue , "expiration time must be positive for address "
+ << addr);
+
+ } else if (expire_time < valid_lft) {
+ isc_throw(BadValue, "expiration time must be greater than valid lifetime"
+ " for address " << addr);
+ }
+
+ cltt = static_cast<time_t>(expire_time - valid_lft);
+ } else {
+ cltt = time(NULL);
+ }
+
+ bool fqdn_fwd = false;
+ if (lease_info->contains("fqdn-fwd")) {
+ fqdn_fwd = getBoolean(lease_info, "fqdn-fwd");
+ }
+ bool fqdn_rev = false;
+ if (lease_info->contains("fqdn-rev")) {
+ fqdn_rev = getBoolean(lease_info, "fqdn-rev");
+ }
+ string hostname;
+ if (lease_info->contains("hostname")) {
+ hostname = getString(lease_info, "hostname");
+ }
+ if (hostname.empty() && (fqdn_fwd || fqdn_rev)) {
+ isc_throw(BadValue, "No hostname specified and either forward or reverse"
+ " fqdn was set to true.");
+ }
+
+ uint32_t state = 0;
+ if (lease_info->contains("state")) {
+ state = getUint8(lease_info, "state");
+ }
+
+ // Check if the state value is sane.
+ if (state > Lease::STATE_EXPIRED_RECLAIMED) {
+ isc_throw(BadValue, "Invalid state value: " << state << ", supported "
+ "values are: 0 (default), 1 (declined) and 2 (expired-reclaimed)");
+ }
+
+ if ((state == Lease::STATE_DECLINED) && (type == Lease::TYPE_PD)) {
+ isc_throw(isc::InvalidOperation,
+ "Invalid declined state for PD prefix.");
+ }
+
+ // Handle user context.
+ ConstElementPtr ctx = lease_info->get("user-context");
+ if (ctx && (ctx->getType() != Element::map)) {
+ isc_throw(BadValue, "Invalid user context '" << ctx->str()
+ << "' is not a JSON map.");
+ }
+
+ // Handle comment.
+ ConstElementPtr comment = lease_info->get("comment");
+ if (comment) {
+ if (ctx && ctx->contains("comment")) {
+ isc_throw(BadValue, "Duplicated comment entry '" << comment->str()
+ << "' in user context '" << ctx->str() << "'");
+ }
+ ElementPtr copied;
+ if (ctx) {
+ copied = copy(ctx, 0);
+ } else {
+ copied = Element::createMap();
+ }
+ copied->set("comment", comment);
+ ctx = copied;
+ }
+
+ // Let's fabricate some data and we're ready to go.
+
+ Lease6Ptr l(new Lease6(type, addr, duid_ptr, iaid, pref_lft, valid_lft,
+ subnet_id, fqdn_fwd, fqdn_rev, hostname,
+ hwaddr_ptr, prefix_len));
+ l->cltt_ = cltt;
+ l->state_ = state;
+ l->setContext(ctx);
+
+ // Retrieve the optional flag indicating if the lease must be created when it
+ // doesn't exist during the update.
+ force_create = false;
+ if (lease_info->contains("force-create")) {
+ force_create = getBoolean(lease_info, "force-create");
+ }
+
+ return (l);
+}
+
+} // end of namespace lease_cmds
+} // end of namespace isc
diff --git a/src/hooks/dhcp/lease_cmds/lease_parser.h b/src/hooks/dhcp/lease_cmds/lease_parser.h
new file mode 100644
index 0000000..2220d49
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/lease_parser.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2017-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/.
+
+#ifndef LEASE_PARSER_H
+#define LEASE_PARSER_H
+
+#include <cc/data.h>
+#include <cc/simple_parser.h>
+#include <dhcpsrv/lease.h>
+#include <dhcpsrv/srv_config.h>
+
+namespace isc {
+namespace lease_cmds {
+
+/// @brief Parser for Lease4 structure
+///
+/// It expects the data in the following format:
+/// {
+/// "ip-address": "192.0.2.1",
+/// "hw-address": "00:01:02:03:04:05",
+/// "client-id": "this-is-a-client",
+/// "valid-lft": 3600,
+/// "cltt": 12345678,
+/// "expire": 1499282530,
+/// "subnet-id": 1,
+/// "fqdn-fwd": true,
+/// "fqdn-rev": true,
+/// "hostname": "myhost.example.org",
+/// "state": 0,
+/// "user-context": { \"version\": 1 }
+/// }
+class Lease4Parser : public isc::data::SimpleParser {
+public:
+
+ /// @brief Parses Element tree and tries to convert to Lease4
+ ///
+ /// See @ref Lease6Parser class description for expected format.
+ ///
+ /// @param cfg Currently running config (used for sanity checks and defaults)
+ /// @param lease_info structure to be parsed
+ /// @param [out] force_create indicates if the lease should be created when it
+ /// doesn't exist.
+ /// @return A pointer to Lease4
+ /// @throw BadValue if any of the parameters is invalid
+ /// @throw DhcpConfigError if mandatory parameter is missing
+ virtual isc::dhcp::Lease4Ptr parse(isc::dhcp::ConstSrvConfigPtr& cfg,
+ const isc::data::ConstElementPtr& lease_info,
+ bool& force_create);
+
+ /// @brief virtual dtor (does nothing)
+ virtual ~Lease4Parser() {}
+};
+
+/// @brief Parser for Lease6 structure
+///
+/// {
+/// "address": "2001:db8::1",
+/// "duid": "00:01:02:03:04:05",
+/// "type": "IA_NA",
+/// "cltt": 12345678,
+/// "preferred-lft": 3600,
+/// "valid-lft": 3600,
+/// "expire": 1499282530,
+/// "subnet-id": 1,
+/// "fqdn-fwd": true,
+/// "fqdn-rev": true,
+/// "hostname": "myhost.example.org",
+/// "state": 0,
+/// "user-context": { \"version\": 1 }
+/// }
+
+/// It expects the input data to use the following format:
+class Lease6Parser : public isc::data::SimpleParser {
+public:
+ /// @brief Parses Element tree and tries to convert to Lease4
+ ///
+ /// See @ref Lease6Parser class description for expected format.
+ ///
+ /// @param cfg Currently running config (used for sanity checks and defaults)
+ /// @param lease_info structure to be parsed
+ /// @param [out] force_create indicates if the lease should be created when it
+ /// doesn't exist.
+ /// @return A pointer to Lease4
+ /// @throw BadValue if any of the parameters is invalid
+ /// @throw DhcpConfigError if mandatory parameter is missing
+ virtual isc::dhcp::Lease6Ptr parse(isc::dhcp::ConstSrvConfigPtr& cfg,
+ const isc::data::ConstElementPtr& lease_info,
+ bool& force_create);
+
+ /// @brief virtual dtor (does nothing)
+ virtual ~Lease6Parser() {}
+};
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif
diff --git a/src/hooks/dhcp/lease_cmds/tests/Makefile.am b/src/hooks/dhcp/lease_cmds/tests/Makefile.am
new file mode 100644
index 0000000..8b2efcc
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/Makefile.am
@@ -0,0 +1,63 @@
+SUBDIRS = .
+
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += -I$(top_builddir)/src/hooks/dhcp/lease_cmds -I$(top_srcdir)/src/hooks/dhcp/lease_cmds
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CPPFLAGS += -DLEASE_CMDS_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/lease_cmds/.libs/libdhcp_lease_cmds.so\"
+AM_CPPFLAGS += -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+# Unit test data files need to get installed.
+EXTRA_DIST =
+
+CLEANFILES = *.gcno *.gcda
+
+# TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+LOG_COMPILER = $(LIBTOOL)
+AM_LOG_FLAGS = --mode=execute
+
+TESTS =
+if HAVE_GTEST
+TESTS += lease_cmds_unittests
+
+lease_cmds_unittests_SOURCES = run_unittests.cc
+lease_cmds_unittests_SOURCES += lease_cmds_unittest.h lease_cmds_unittest.cc
+lease_cmds_unittests_SOURCES += lease_cmds4_unittest.cc
+lease_cmds_unittests_SOURCES += lease_cmds6_unittest.cc
+
+lease_cmds_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+
+lease_cmds_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+
+lease_cmds_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+
+lease_cmds_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/database/libkea-database.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+lease_cmds_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+lease_cmds_unittests_LDADD += $(LOG4CPLUS_LIBS)
+lease_cmds_unittests_LDADD += $(CRYPTO_LIBS)
+lease_cmds_unittests_LDADD += $(BOOST_LIBS)
+lease_cmds_unittests_LDADD += $(GTEST_LDADD)
+
+endif
+noinst_PROGRAMS = $(TESTS)
diff --git a/src/hooks/dhcp/lease_cmds/tests/Makefile.in b/src/hooks/dhcp/lease_cmds/tests/Makefile.in
new file mode 100644
index 0000000..3498156
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/Makefile.in
@@ -0,0 +1,1072 @@
+# Makefile.in generated by automake 1.16.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2018 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+TESTS = $(am__EXEEXT_1)
+@HAVE_GTEST_TRUE@am__append_1 = lease_cmds_unittests
+noinst_PROGRAMS = $(am__EXEEXT_2)
+subdir = src/hooks/dhcp/lease_cmds/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_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_sysrepo.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+@HAVE_GTEST_TRUE@am__EXEEXT_1 = lease_cmds_unittests$(EXEEXT)
+am__EXEEXT_2 = $(am__EXEEXT_1)
+PROGRAMS = $(noinst_PROGRAMS)
+am__lease_cmds_unittests_SOURCES_DIST = run_unittests.cc \
+ lease_cmds_unittest.h lease_cmds_unittest.cc \
+ lease_cmds4_unittest.cc lease_cmds6_unittest.cc
+@HAVE_GTEST_TRUE@am_lease_cmds_unittests_OBJECTS = \
+@HAVE_GTEST_TRUE@ lease_cmds_unittests-run_unittests.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ lease_cmds_unittests-lease_cmds_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ lease_cmds_unittests-lease_cmds4_unittest.$(OBJEXT) \
+@HAVE_GTEST_TRUE@ lease_cmds_unittests-lease_cmds6_unittest.$(OBJEXT)
+lease_cmds_unittests_OBJECTS = $(am_lease_cmds_unittests_OBJECTS)
+am__DEPENDENCIES_1 =
+@HAVE_GTEST_TRUE@lease_cmds_unittests_DEPENDENCIES = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+lease_cmds_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) \
+ $(lease_cmds_unittests_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = \
+ ./$(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Po \
+ ./$(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Po \
+ ./$(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Po \
+ ./$(DEPDIR)/lease_cmds_unittests-run_unittests.Po
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(lease_cmds_unittests_SOURCES)
+DIST_SOURCES = $(am__lease_cmds_unittests_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_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_SYSREPO = @HAVE_SYSREPO@
+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@
+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_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = .
+AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib \
+ -I$(top_builddir)/src/hooks/dhcp/lease_cmds \
+ -I$(top_srcdir)/src/hooks/dhcp/lease_cmds $(BOOST_INCLUDES) \
+ -DLEASE_CMDS_LIB_SO=\"$(abs_top_builddir)/src/hooks/dhcp/lease_cmds/.libs/libdhcp_lease_cmds.so\" \
+ -DINSTALL_PROG=\"$(abs_top_srcdir)/install-sh\"
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+
+# Unit test data files need to get installed.
+EXTRA_DIST =
+CLEANFILES = *.gcno *.gcda
+
+# TESTS_ENVIRONMENT = $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND)
+LOG_COMPILER = $(LIBTOOL)
+AM_LOG_FLAGS = --mode=execute
+@HAVE_GTEST_TRUE@lease_cmds_unittests_SOURCES = run_unittests.cc \
+@HAVE_GTEST_TRUE@ lease_cmds_unittest.h lease_cmds_unittest.cc \
+@HAVE_GTEST_TRUE@ lease_cmds4_unittest.cc \
+@HAVE_GTEST_TRUE@ lease_cmds6_unittest.cc
+@HAVE_GTEST_TRUE@lease_cmds_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) $(LOG4CPLUS_INCLUDES)
+@HAVE_GTEST_TRUE@lease_cmds_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
+@HAVE_GTEST_TRUE@lease_cmds_unittests_CXXFLAGS = $(AM_CXXFLAGS)
+@HAVE_GTEST_TRUE@lease_cmds_unittests_LDADD = $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/eval/libkea-eval.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/stats/libkea-stats.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/http/libkea-http.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/database/libkea-database.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/dns/libkea-dns++.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/util/libkea-util.la \
+@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+@HAVE_GTEST_TRUE@ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) \
+@HAVE_GTEST_TRUE@ $(BOOST_LIBS) $(GTEST_LDADD)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/hooks/dhcp/lease_cmds/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/hooks/dhcp/lease_cmds/tests/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+lease_cmds_unittests$(EXEEXT): $(lease_cmds_unittests_OBJECTS) $(lease_cmds_unittests_DEPENDENCIES) $(EXTRA_lease_cmds_unittests_DEPENDENCIES)
+ @rm -f lease_cmds_unittests$(EXEEXT)
+ $(AM_V_CXXLD)$(lease_cmds_unittests_LINK) $(lease_cmds_unittests_OBJECTS) $(lease_cmds_unittests_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lease_cmds_unittests-run_unittests.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+lease_cmds_unittests-run_unittests.o: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-run_unittests.Tpo -c -o lease_cmds_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-run_unittests.Tpo $(DEPDIR)/lease_cmds_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='lease_cmds_unittests-run_unittests.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc
+
+lease_cmds_unittests-run_unittests.obj: run_unittests.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-run_unittests.Tpo -c -o lease_cmds_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-run_unittests.Tpo $(DEPDIR)/lease_cmds_unittests-run_unittests.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='lease_cmds_unittests-run_unittests.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.cc'; fi`
+
+lease_cmds_unittests-lease_cmds_unittest.o: lease_cmds_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-lease_cmds_unittest.o -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Tpo -c -o lease_cmds_unittests-lease_cmds_unittest.o `test -f 'lease_cmds_unittest.cc' || echo '$(srcdir)/'`lease_cmds_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Tpo $(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds_unittest.cc' object='lease_cmds_unittests-lease_cmds_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-lease_cmds_unittest.o `test -f 'lease_cmds_unittest.cc' || echo '$(srcdir)/'`lease_cmds_unittest.cc
+
+lease_cmds_unittests-lease_cmds_unittest.obj: lease_cmds_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-lease_cmds_unittest.obj -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Tpo -c -o lease_cmds_unittests-lease_cmds_unittest.obj `if test -f 'lease_cmds_unittest.cc'; then $(CYGPATH_W) 'lease_cmds_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/lease_cmds_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Tpo $(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds_unittest.cc' object='lease_cmds_unittests-lease_cmds_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-lease_cmds_unittest.obj `if test -f 'lease_cmds_unittest.cc'; then $(CYGPATH_W) 'lease_cmds_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/lease_cmds_unittest.cc'; fi`
+
+lease_cmds_unittests-lease_cmds4_unittest.o: lease_cmds4_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-lease_cmds4_unittest.o -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Tpo -c -o lease_cmds_unittests-lease_cmds4_unittest.o `test -f 'lease_cmds4_unittest.cc' || echo '$(srcdir)/'`lease_cmds4_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Tpo $(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds4_unittest.cc' object='lease_cmds_unittests-lease_cmds4_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-lease_cmds4_unittest.o `test -f 'lease_cmds4_unittest.cc' || echo '$(srcdir)/'`lease_cmds4_unittest.cc
+
+lease_cmds_unittests-lease_cmds4_unittest.obj: lease_cmds4_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-lease_cmds4_unittest.obj -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Tpo -c -o lease_cmds_unittests-lease_cmds4_unittest.obj `if test -f 'lease_cmds4_unittest.cc'; then $(CYGPATH_W) 'lease_cmds4_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/lease_cmds4_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Tpo $(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds4_unittest.cc' object='lease_cmds_unittests-lease_cmds4_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-lease_cmds4_unittest.obj `if test -f 'lease_cmds4_unittest.cc'; then $(CYGPATH_W) 'lease_cmds4_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/lease_cmds4_unittest.cc'; fi`
+
+lease_cmds_unittests-lease_cmds6_unittest.o: lease_cmds6_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-lease_cmds6_unittest.o -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Tpo -c -o lease_cmds_unittests-lease_cmds6_unittest.o `test -f 'lease_cmds6_unittest.cc' || echo '$(srcdir)/'`lease_cmds6_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Tpo $(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds6_unittest.cc' object='lease_cmds_unittests-lease_cmds6_unittest.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-lease_cmds6_unittest.o `test -f 'lease_cmds6_unittest.cc' || echo '$(srcdir)/'`lease_cmds6_unittest.cc
+
+lease_cmds_unittests-lease_cmds6_unittest.obj: lease_cmds6_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -MT lease_cmds_unittests-lease_cmds6_unittest.obj -MD -MP -MF $(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Tpo -c -o lease_cmds_unittests-lease_cmds6_unittest.obj `if test -f 'lease_cmds6_unittest.cc'; then $(CYGPATH_W) 'lease_cmds6_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/lease_cmds6_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Tpo $(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='lease_cmds6_unittest.cc' object='lease_cmds_unittests-lease_cmds6_unittest.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(lease_cmds_unittests_CPPFLAGS) $(CPPFLAGS) $(lease_cmds_unittests_CXXFLAGS) $(CXXFLAGS) -c -o lease_cmds_unittests-lease_cmds6_unittest.obj `if test -f 'lease_cmds6_unittest.cc'; then $(CYGPATH_W) 'lease_cmds6_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/lease_cmds6_unittest.cc'; fi`
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ fi; \
+ echo "$${col}$$dashes$${std}"; \
+ echo "$${col}$$banner$${std}"; \
+ test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \
+ test -z "$$report" || echo "$${col}$$report$${std}"; \
+ echo "$${col}$$dashes$${std}"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Po
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Po
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Po
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-run_unittests.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-lease_cmds4_unittest.Po
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-lease_cmds6_unittest.Po
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-lease_cmds_unittest.Po
+ -rm -f ./$(DEPDIR)/lease_cmds_unittests-run_unittests.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: $(am__recursive_targets) check-am install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-TESTS check-am clean clean-generic \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-man install-pdf install-pdf-am \
+ install-ps install-ps-am install-strip installcheck \
+ installcheck-am installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds4_unittest.cc b/src/hooks/dhcp/lease_cmds/tests/lease_cmds4_unittest.cc
new file mode 100644
index 0000000..30e0a81
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds4_unittest.cc
@@ -0,0 +1,3826 @@
+// Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
+#include <config/command_mgr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/ncr_generator.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/resource_handler.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <lease_cmds_unittest.h>
+#include <stats/stats_mgr.h>
+#include <testutils/user_context_utils.h>
+#include <testutils/multi_threading_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <set>
+
+using namespace std;
+using namespace isc;
+using namespace isc::hooks;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp_ddns;
+using namespace isc::asiolink;
+using namespace isc::stats;
+using namespace isc::test;
+
+namespace {
+
+/// @brief Class dedicated to testing v4 part of lease_cmds library.
+///
+/// Provides convenience methods for loading, testing all commands and
+/// unloading the lease_cmds library.
+class Lease4CmdsTest : public LeaseCmdsTest {
+public:
+ /// @brief Checks if specified response contains IPv4 lease
+ ///
+ /// @param lease Element tree that represents a lease
+ /// @param ip expected IP address
+ /// @param subnet_id expected subnet-id
+ /// @param hwaddr expected value of hardware address
+ /// @param client_id_required true if client-id is expected
+ void checkLease4(isc::data::ConstElementPtr l, std::string ip,
+ uint32_t subnet_id, std::string hwaddr,
+ bool client_id_required) {
+ ASSERT_TRUE(l);
+
+ // If the element is a list we need to retrieve the lease that
+ // we're interested in.
+ if (l->getType() == isc::data::Element::list) {
+ std::vector<isc::data::ElementPtr> e = l->listValue();
+ for (auto it = e.begin(); it != e.end(); ++it) {
+ isc::data::ConstElementPtr ip_address = (*it)->get("ip-address");
+ if (ip_address && ip_address->stringValue() == ip) {
+ l = (*it);
+ break;
+ }
+ }
+
+ ASSERT_TRUE(l);
+ }
+
+ ASSERT_TRUE(l->get("ip-address"));
+ EXPECT_EQ(ip, l->get("ip-address")->stringValue());
+
+ ASSERT_TRUE(l->get("subnet-id"));
+ EXPECT_EQ(subnet_id, l->get("subnet-id")->intValue());
+
+ ASSERT_TRUE(l->get("hw-address"));
+ EXPECT_EQ(hwaddr, l->get("hw-address")->stringValue());
+
+ // client-id may or may not appear
+ if (client_id_required) {
+ EXPECT_TRUE(l->get("client-id"));
+ }
+
+ // Check that other parameters are there.
+ ASSERT_TRUE(l->contains("valid-lft"));
+ ASSERT_TRUE(l->contains("cltt"));
+ ASSERT_TRUE(l->contains("subnet-id"));
+ ASSERT_TRUE(l->contains("state"));
+ ASSERT_TRUE(l->contains("fqdn-fwd"));
+ ASSERT_TRUE(l->contains("fqdn-rev"));
+ ASSERT_TRUE(l->contains("hostname"));
+ ASSERT_TRUE(l->contains("state"));
+
+ // Check that there are no v6 specific fields
+ ASSERT_FALSE(l->contains("prefix"));
+ ASSERT_FALSE(l->contains("duid"));
+ ASSERT_FALSE(l->contains("preferred-lft"));
+
+ // Assuming that these values were used to create the lease.
+ // If we ever want to test different values they will need to
+ // be added as parameters to this function.
+ EXPECT_EQ(HIGH_VALID_LIFETIME,
+ static_cast<uint32_t>(l->get("valid-lft")->intValue()));
+ EXPECT_EQ(DEC_2030_TIME, l->get("cltt")->intValue());
+ }
+
+ /// @brief Check lease4 statistics.
+ ///
+ /// @param id Subnet id of the stats to check.
+ /// @assigned The expected value of assigned addresses in subnet.
+ /// @declined The expected value of declined addresses in subnet.
+ void checkLease4Stats(isc::dhcp::SubnetID id, int32_t assigned, int32_t declined) {
+ ASSERT_EQ(isc::stats::StatsMgr::instance().getObservation(
+ isc::stats::StatsMgr::generateName("subnet", id,
+ "assigned-addresses"))->getInteger().first, assigned);
+
+ ASSERT_EQ(isc::stats::StatsMgr::instance().getObservation(
+ isc::stats::StatsMgr::generateName("subnet", id,
+ "declined-addresses"))->getInteger().first, declined);
+ }
+
+ /// @brief Check that lease4-add with missing parameters will fail.
+ void testLease4AddMissingParams();
+
+ /// @brief Verify that lease4-add can be rejected if parameters are
+ /// specified, but have incorrect values.
+ void testLease4AddBadParams();
+
+ /// @brief Check that a simple, well formed lease4 can be added.
+ void testLease4Add();
+
+ /// @brief Check that a simple, well formed lease4 can be added.
+ void testLease4AddDeclinedLeases();
+
+ /// @brief Check that a lease4 is not added when it already exists.
+ void testLease4AddExisting();
+
+ /// @brief Check that subnet-id is optional. If not specified, Kea should
+ /// select it on its own.
+ void testLease4AddSubnetIdMissing();
+
+ /// @brief Check that subnet-id is optional. If not specified, Kea should
+ /// select it on its own.
+ void testLease4AddSubnetIdMissingDeclinedLeases();
+
+ /// @brief Check that subnet-id is optional. If not specified, Kea should
+ /// select it on its own, but if there's no subnet for address being added,
+ /// it should fail.
+ void testLease4AddSubnetIdMissingBadAddr();
+
+ /// @brief Check that the lease with negative expiration time is rejected.
+ void testLease4AddNegativeExpireTime();
+
+ /// @brief Check that the lease with negative cltt is rejected.
+ void testLease4AddNegativeCltt();
+
+ /// @brief Check that a well formed lease4 with tons of parameters can be
+ /// added.
+ void testLease4AddFullAddr();
+
+ /// @brief Check that a well formed lease4 with a comment can be added.
+ void testLease4AddComment();
+
+ /// @brief Check that lease4-get can handle a situation when the query is
+ /// broken (some required parameters are missing).
+ void testLease4GetMissingParams();
+
+ /// @brief Check that lease4-get sanitizes its input.
+ void testLease4GetByAddrBadParam();
+
+ /// @brief Check that lease4-get can handle a situation when the query is
+ /// correctly formed, but the lease is not there.
+ void testLease4GetByAddrNotFound();
+
+ /// @brief Check that lease4-get can return a lease by address.
+ void testLease4GetByAddr();
+
+ /// @brief Check that lease4-get can handle a situation when the query is
+ /// correctly formed, but the lease is not there.
+ void testLease4GetByHWAddrNotFound();
+
+ /// @brief Check that lease4-get can find a lease by hardware address.
+ void testLease4GetByHWAddr();
+
+ /// @brief Check that lease4-get can handle a situation when the query is
+ /// correctly formed, but the lease is not there.
+ void testLease4GetByClientIdNotFound();
+
+ /// @brief Check that lease4-get can find a lease by client identifier.
+ void testLease4GetByClientId();
+
+ /// @brief Check that lease4-get-all returns all leases.
+ void testLease4GetAll();
+
+ /// @brief Check that lease4-get-all returns empty set if no leases are
+ /// found.
+ void testLease4GetAllNoLeases();
+
+ /// @brief Check that lease4-get-all returns all leases for a subnet.
+ void testLease4GetAllBySubnetId();
+
+ /// @brief Check that lease4-get-all returns empty set when no leases are
+ /// found.
+ void testLease4GetAllBySubnetIdNoLeases();
+
+ /// @brief Check that lease4-get-all returns leases from multiple subnets.
+ void testLease4GetAllByMultipleSubnetIds();
+
+ /// @brief Check that lease4-get-all checks its input arguments.
+ void testLease4GetBySubnetIdInvalidArguments();
+
+ /// @brief Check that multiple calls to lease4-get-page return all leases.
+ void testLease4GetPaged();
+
+ /// @brief Verifies that first page of IPv4 leases can be retrieved by
+ /// specifying zero IPv4 address.
+ void testLease4GetPagedZeroAddress();
+
+ /// @brief Verifies that IPv6 address as a start address is rejected.
+ void testLease4GetPagedIPv6Address();
+
+ /// @brief Verifies that value of 'from' parameter other than 'start' or an
+ /// IPv4 address is rejected.
+ void testLease4GetPagedInvalidFrom();
+
+ /// @brief Verifies that limit is mandatory.
+ void testLease4GetPagedNoLimit();
+
+ /// @brief Verifies that the limit must be a number.
+ void testLease4GetPagedLimitNotNumber();
+
+ /// @brief Check that lease4-get-by-hw-address can handle a situation when
+ /// the query is broken (required parameter is missing).
+ void testLease4GetByHwAddressParams();
+
+ /// @brief Check that lease4-get-by-hw-address works as expected (find no
+ /// lease).
+ void testLease4GetByHwAddressFind0();
+
+ /// @brief Check that lease4-get-by-hw-address works as expected (find two
+ /// leases).
+ void testLease4GetByHwAddressFind2();
+
+ /// @brief Check that lease4-get-by-client-id can handle a situation when
+ /// the query is broken (required parameter is missing).
+ void testLease4GetByClientIdParams();
+
+ /// @brief Check that lease4-get-by-client-id works as expected (find no
+ /// lease).
+ void testLease4GetByClientIdFind0();
+
+ /// @brief Check that lease4-get-by-client-id works as expected (find two
+ /// leases).
+ void testLease4GetByClientIdFind2();
+
+ /// @brief Verifies that the limit of 0 is rejected.
+ void testLease4GetPagedLimitIsZero();
+
+ /// @brief Check that lease4-get-by-hostname can handle a situation when
+ /// the query is broken (required parameter is missing).
+ void testLease4GetByHostnameParams();
+
+ /// @brief Check that lease4-get-by-hostname works as expected (find no
+ /// lease).
+ void testLease4GetByHostnameFind0();
+
+ /// @brief Check that lease4-get-by-hostname works as expected (find two
+ /// leases).
+ void testLease4GetByHostnameFind2();
+
+ /// @brief Test checks if lease4-update handler refuses calls with missing
+ /// parameters.
+ void testLease4UpdateMissingParams();
+
+ /// @brief Verify that lease4-update can be rejected if parameters are
+ /// specified, but have incorrect values.
+ void testLease4UpdateBadParams();
+
+ /// @brief Check that lease4-update correctly handles case when there is no
+ /// lease to be updated.
+ void testLease4UpdateNoLease();
+
+ /// @brief Check that a lease4 can be updated. We're changing hw-address and
+ /// a hostname.
+ void testLease4Update();
+
+ /// @brief Check that a lease4 can be updated. We're changing hw-address and
+ /// a hostname.
+ void testLease4UpdateDeclinedLeases();
+
+ /// @brief Check that a lease4 can be updated. We're changing hw-address and
+ /// a hostname. The subnet-id is not specified.
+ void testLease4UpdateNoSubnetId();
+
+ /// @brief Check that a lease4 can be updated. We're changing hw-address and
+ /// a hostname. The subnet-id is not specified.
+ void testLease4UpdateNoSubnetIdDeclinedLeases();
+
+ /// @brief Check that a lease4 is created if it doesn't exist during the
+ /// update. To trigger this behavior 'force-create' boolean parameter must
+ /// be included in the command.
+ void testLease4UpdateForceCreate();
+
+ /// @brief Check that a lease4 is created if it doesn't exist during the
+ /// update. To trigger this behavior 'force-create' boolean parameter must
+ /// be included in the command. The subnet-id is not specified, Kea will
+ /// figure it out.
+ void testLease4UpdateForceCreateNoSubnetId();
+
+ /// @brief Check that lease4-update correctly handles case when the
+ /// 'force-create' parameter is explicitly set to false.
+ void testLease4UpdateDoNotForceCreate();
+
+ /// @brief Check that a lease4 can be updated. We're adding a comment and an
+ /// user context.
+ void testLease4UpdateComment();
+
+ /// @brief Check that lease4-del can handle a situation when the query is
+ /// broken (some required parameters are missing).
+ void testLease4DelMissingParams();
+
+ /// @brief Check that lease4-del can handle a situation when the query is
+ /// valid, but the lease is not there.
+ void testLease4DelByAddrNotFound();
+
+ /// @brief Check that lease4-del sanitizes its input.
+ void testLease4DelByAddrBadParam();
+
+ /// @brief Check that lease4-del can handle a situation when the query is
+ /// correctly formed and the lease is deleted.
+ void testLease4DelByAddr();
+
+ /// @brief Check that lease4-del can handle a situation when the query is
+ /// correctly formed and the lease is deleted.
+ void testLease4DelByAddrDeclinedLeases();
+
+ /// @brief Check that lease4-del can handle a situation when the query is
+ /// correctly formed, but the lease is not there.
+ void testLease4DelByHWAddrNotFound();
+
+ /// @brief Check that lease4-del can find and delete a lease by hardware
+ /// address.
+ void testLease4DelByHWAddr();
+
+ /// @brief Check that lease4-del can handle a situation when the query is
+ /// correctly formed, but the lease is not there.
+ void testLease4DelByClientIdNotFound();
+
+ /// @brief Check that lease4-del can find and delete a lease by client
+ /// identifier.
+ void testLease4DelByClientId();
+
+ /// @brief Check that lease4-wipe can remove leases.
+ void testLease4Wipe();
+
+ /// @brief Check that lease4-wipe can remove leases from all subnets at
+ /// once.
+ void testLease4WipeAll();
+
+ /// @brief Check that lease4-wipe can remove leases from all subnets at
+ /// once (when no parameters are specified).
+ void testLease4WipeAllNoArgs();
+
+ /// @brief Check that lease4-wipe properly reports when no leases were
+ /// deleted.
+ void testLease4WipeNoLeases();
+
+ /// @brief Check that lease4-wipe properly reports when no leases were
+ /// deleted.
+ void testLease4WipeNoLeasesAll();
+
+ /// @brief Check that an attempt to update a lease (set incorrect
+ /// subnet-id) will fail.
+ void testLease4BrokenUpdate();
+
+ /// @brief Check that lease4-resend-ddns sanitizes its input.
+ void testLease4ResendDdnsBadParam();
+
+ /// @brief Check that lease4-resend-ddns does not generate an NCR for given
+ /// lease when DDNS updating is disabled.
+ void testLease4ResendDdnsDisabled();
+
+ /// @brief Check that lease4-resend-ddns does not generate an NCR for when
+ /// there is no matching lease.
+ void testLease4ResendDdnsNoLease();
+
+ /// @brief Check that lease4-resend-ddns does not generate an NCR for given
+ /// lease when updates are enabled but Lease::hostname_ is blank.
+ void testLease4ResendNoHostname();
+
+ /// @brief Check that lease4-resend-ddns does not generate an NCR for given
+ /// lease when updates are enabled, Lease::hostname_ is not blank, but both
+ /// Lease::fqdn_fwd_ and fdqn_rev_ are false.
+ void testLease4ResendNoDirectionsEnabled();
+
+ /// @brief Check that lease4-resend-ddns can generate an NCR for given
+ /// lease when updates are enabled, Lease::hostname_ is not blank, and at
+ /// least one of Lease::fqdn_fwd_ or fdqn_rev_ are true.
+ void testLease4ResendDdnsEnabled();
+
+ /// @brief Check that lease4-del does (or does not) generate an NCR to
+ /// remove DNS for a given lease based on lease content when DDNS updates
+ /// are enabled.
+ void testLease4DnsRemoveD2Enabled();
+
+ /// @brief Check that lease4-del does not generate an NCR to remove DNS for
+ /// a given lease based on lease content when DDNS updates are disabled.
+ void testLease4DnsRemoveD2Disabled();
+
+ /// @brief Verify that v4 lease add handles conflict as expected.
+ void testLease4ConflictingAdd();
+
+ /// @brief Verify that v4 lease update handles conflict as expected.
+ void testLease4ConflictingUpdate();
+};
+
+void Lease4CmdsTest::testLease4AddMissingParams() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Everything missing. What sort of crap is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and hwaddr missing, although
+ // subnet-id can now be figured out by Kea code)
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.123\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'hw-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Better, but still no luck. (hwaddr missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'hw-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Close, but no cigars. (ip-address missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4AddBadParams() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // All params are there, but there's no subnet-id 123 configured.
+ // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6).
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv4 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"10.0.0.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 10.0.0.1 does not belong to subnet 192.0.2.0/24, subnet-id=44";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We don't use any of that bleeding edge nonsense in this museum. v4 only.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv4 address specified: 2001:db8:1::1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // currently defined states are 0,1 and 2. 123 is junk.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"state\": 123\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 "
+ "(declined) and 2 (expired-reclaimed)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Duplicated comment.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"user-context\": { \"comment\": \"in user context\" },\n"
+ " \"comment\": \"direct\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Duplicated comment entry '\"direct\"' in user context "
+ "'{ \"comment\": \"in user context\" }'";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4Add() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 192.0.2.202, subnet-id 44 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease has proper value set.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ(3, l->valid_lft_); // taken from subnet configuration
+ EXPECT_FALSE(l->fqdn_fwd_);
+ EXPECT_FALSE(l->fqdn_rev_);
+ EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+
+ // Test execution is fast. The cltt should be set to now. In some rare
+ // cases we could have the seconds counter to tick, so having a value off
+ // by one is ok.
+ EXPECT_LE(abs(l->cltt_ - time(NULL)), 1);
+ EXPECT_EQ(0, l->state_);
+}
+
+void Lease4CmdsTest::testLease4AddDeclinedLeases() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"state\": 1,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 192.0.2.202, subnet-id 44 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 1);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease have proper value set.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ(3, l->valid_lft_); // taken from subnet configuration
+ EXPECT_FALSE(l->fqdn_fwd_);
+ EXPECT_FALSE(l->fqdn_rev_);
+ EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+
+ // Test execution is fast. The cltt should be set to now. In some rare
+ // cases we could have the seconds counter to tick, so having a value off
+ // by one is ok.
+ EXPECT_LE(abs(l->cltt_ - time(NULL)), 1);
+ EXPECT_EQ(1, l->state_);
+}
+
+void Lease4CmdsTest::testLease4AddExisting() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease already exists.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+}
+
+void Lease4CmdsTest::testLease4AddSubnetIdMissing() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command without subnet-id. Kea should select
+ // the subnet id on its own.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 192.0.2.202, subnet-id 44 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(44, l->subnet_id_);
+}
+
+void Lease4CmdsTest::testLease4AddSubnetIdMissingDeclinedLeases() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command without subnet-id. Kea should select
+ // the subnet id on its own.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"state\": 1,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 192.0.2.202, subnet-id 44 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 1);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(44, l->subnet_id_);
+}
+
+void Lease4CmdsTest::testLease4AddSubnetIdMissingBadAddr() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command without subnet-id. Kea should select
+ // the subnet id on its own.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.55.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "subnet-id not specified and failed to find a subnet for "
+ "address 192.0.55.1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease was not added.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.55.1"));
+ ASSERT_FALSE(l);
+}
+
+void Lease4CmdsTest::testLease4AddNegativeExpireTime() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Add a lease with negative expiration time.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"expire\": -6218189367\n"
+ " }\n"
+ "}";
+ string exp_rsp = "expiration time must be positive for address 192.0.2.202";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease was not added.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_FALSE(l);
+}
+
+void Lease4CmdsTest::testLease4AddNegativeCltt() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Add a lease with negative cltt (expiration time - valid lifetime)
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"expire\": 123456,\n"
+ " \"valid-lft\": 123457"
+ " }\n"
+ "}";
+ string exp_rsp = "expiration time must be greater than valid lifetime for "
+ "address 192.0.2.202";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease was not added.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_FALSE(l);
+}
+
+void Lease4CmdsTest::testLease4AddFullAddr() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"client-id\": \"01:02:03:04:05:06:07:08\",\n"
+ " \"valid-lft\": 1000,\n"
+ " \"expire\": 6218189367,\n"
+ " \"fqdn-fwd\": true,\n"
+ " \"fqdn-rev\": true,\n"
+ " \"hostname\": \"urania.example.org\",\n"
+ " \"user-context\": { \"foobar\": true }\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 192.0.2.202, subnet-id 44 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ("192.0.2.202", l->addr_.toText());
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ ASSERT_TRUE(l->client_id_);
+ EXPECT_EQ("01:02:03:04:05:06:07:08", l->client_id_->toText());
+ // expire (6218189367) - valid_lft(1000)
+ EXPECT_EQ(static_cast<time_t>(6218189367 - 1000), l->cltt_);
+ EXPECT_EQ(true, l->fqdn_fwd_);
+ EXPECT_EQ(true, l->fqdn_rev_);
+ EXPECT_EQ("urania.example.org", l->hostname_);
+ ASSERT_TRUE(l->getContext());
+ EXPECT_EQ("{ \"foobar\": true }", l->getContext()->str());
+}
+
+void Lease4CmdsTest::testLease4AddComment() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"comment\": \"a comment\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 192.0.2.202, subnet-id 44 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.202"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease have proper value set.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ ASSERT_TRUE(l->getContext());
+ EXPECT_EQ("{ \"comment\": \"a comment\" }", l->getContext()->str());
+}
+
+void Lease4CmdsTest::testLease4GetMissingParams() {
+ // No parameters whatsoever. You want just a lease, any lease?
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "Mandatory 'subnet-id' parameter missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just the subnet-id won't cut it, either.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We can't identify your laptop by color. Sorry, buddy.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"color\",\n"
+ " \"identifier\": \"blue\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Incorrect identifier type: color, the only supported values are: "
+ "address, hw-address, duid";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Query by DUID is not supported in v4. Sorry.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"duid\",\n"
+ " \"identifier\": \"01:01:01:01:01:01\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Query by duid is not allowed in v4.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier value is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"hw-address\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier-type is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier\": \"01:02:03:04:05\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByAddrBadParam() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv4 address specified: 2001:db8:1::1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByAddrNotFound() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.5\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByAddr() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Let's check if the response makes any sense.
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", true);
+}
+
+void Lease4CmdsTest::testLease4GetByHWAddrNotFound() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"01:02:03:04:05:06\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByHWAddr() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"08:08:08:08:08:08\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Let's check if the response makes any sense.
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
+}
+
+void Lease4CmdsTest::testLease4GetByClientIdNotFound() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"client-id\","
+ " \"identifier\": \"01:02:03:04\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByClientId() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"client-id\","
+ " \"identifier\": \"42:42:42:42:42:42:42:42\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Let's check if the response makes any sense.
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
+}
+
+void Lease4CmdsTest::testLease4GetAll() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for all leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\"\n"
+ "}";
+ string exp_rsp = "4 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ // Let's check if the response contains desired leases.
+ checkLease4(leases, "192.0.2.1", 44, "08:08:08:08:08:08", true);
+ checkLease4(leases, "192.0.2.2", 44, "09:09:09:09:09:09", true);
+ checkLease4(leases, "192.0.3.1", 88, "08:08:08:08:08:08", true);
+ checkLease4(leases, "192.0.3.2", 88, "09:09:09:09:09:09", true);
+}
+
+void Lease4CmdsTest::testLease4GetAllNoLeases() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Query for all leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\"\n"
+ "}";
+ string exp_rsp = "0 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ EXPECT_EQ(0, leases->size());
+}
+
+void Lease4CmdsTest::testLease4GetAllBySubnetId() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for leases from subnet 44. Subnet 127 will be ignored because
+ // it doesn't contain any leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\",\n"
+ " \"arguments\": {\n"
+ " \"subnets\": [ 44, 127 ]"
+ " }\n"
+ "}";
+ string exp_rsp = "2 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ // Let's check if the response contains desired leases.
+ checkLease4(leases, "192.0.2.1", 44, "08:08:08:08:08:08", true);
+ checkLease4(leases, "192.0.2.2", 44, "09:09:09:09:09:09", true);
+}
+
+void Lease4CmdsTest::testLease4GetAllBySubnetIdNoLeases() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Query for leases from subnet 44. Subnet 127 will be ignored because
+ // it doesn't contain any leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\",\n"
+ " \"arguments\": {\n"
+ " \"subnets\": [ 44, 127 ]"
+ " }\n"
+ "}";
+ string exp_rsp = "0 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ EXPECT_EQ(0, leases->size());
+}
+
+void Lease4CmdsTest::testLease4GetAllByMultipleSubnetIds() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for leases from subnet 44 and 88.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\",\n"
+ " \"arguments\": {\n"
+ " \"subnets\": [ 44, 88 ]"
+ " }\n"
+ "}";
+ string exp_rsp = "4 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ // Let's check if the response contains desired leases.
+ checkLease4(leases, "192.0.2.1", 44, "08:08:08:08:08:08", true);
+ checkLease4(leases, "192.0.2.2", 44, "09:09:09:09:09:09", true);
+ checkLease4(leases, "192.0.3.1", 88, "08:08:08:08:08:08", true);
+ checkLease4(leases, "192.0.3.2", 88, "09:09:09:09:09:09", true);
+}
+
+void Lease4CmdsTest::testLease4GetBySubnetIdInvalidArguments() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Subnets not specified in arguments.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\",\n"
+ " \"arguments\": {"
+ " \"foo\": 1\n"
+ " }\n"
+ "}";
+ string exp_rsp = "'subnets' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Subnets are not a list.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\",\n"
+ " \"arguments\": {"
+ " \"subnets\": 1\n"
+ " }\n"
+ "}";
+ exp_rsp = "'subnets' parameter must be a list";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Subnets list must contain numbers.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-all\",\n"
+ " \"arguments\": {"
+ " \"subnets\": [ \"x\", \"y\" ]\n"
+ " }\n"
+ "}";
+ exp_rsp = "listed subnet identifiers must be numbers";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetPaged() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Gather all returned addresses to verify that all were returned.
+ std::set<std::string> lease_addresses;
+
+ // Keyword start indicates that we want to retrieve the first page.
+ std::string last_address = "start";
+
+ // There are 4 leases in the database, so the first two pages should
+ // include leases and the 3 page should be empty.
+ for (auto i = 0; i < 3; ++i) {
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"" + last_address + "\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ // For the first two pages we should get success. For the last
+ // one an empty status code.
+ ConstElementPtr rsp;
+ if (i < 2) {
+ string exp_rsp = "2 IPv4 lease(s) found.";
+ rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ } else {
+ string exp_rsp = "0 IPv4 lease(s) found.";
+ rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ }
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ // Arguments must exist.
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ // For convenience, we return the number of returned leases,
+ // so as the client can check whether there was anything returned
+ // before parsing the leases structure.
+ ConstElementPtr page_count = args->get("count");
+ ASSERT_TRUE(page_count);
+ ASSERT_EQ(Element::integer, page_count->getType());
+
+ // leases must exist, but may be empty.
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ if (!leases->empty()) {
+ EXPECT_EQ(2, page_count->intValue());
+
+ // Go over each lease and verify its correctness.
+ for (ConstElementPtr lease : leases->listValue()) {
+ ASSERT_EQ(Element::map, lease->getType());
+ ASSERT_TRUE(lease->contains("ip-address"));
+ ConstElementPtr ip_address = lease->get("ip-address");
+ ASSERT_EQ(Element::string, ip_address->getType());
+ last_address = ip_address->stringValue();
+
+ lease_addresses.insert(last_address);
+
+ // The easiest way to retrieve the subnet id and HW address is to
+ // ask the Lease Manager.
+ Lease4Ptr from_mgr = LeaseMgrFactory::instance().getLease4(IOAddress(last_address));
+ ASSERT_TRUE(from_mgr);
+ checkLease4(leases, last_address, from_mgr->subnet_id_,
+ from_mgr->hwaddr_->toText(false), true);
+ }
+
+ } else {
+ // In the third iteration the page should be empty.
+ EXPECT_EQ(0, page_count->intValue());
+ }
+ }
+
+ // Check if all addresses were returned.
+ EXPECT_EQ(1, lease_addresses.count("192.0.2.1"));
+ EXPECT_EQ(1, lease_addresses.count("192.0.2.2"));
+ EXPECT_EQ(1, lease_addresses.count("192.0.3.1"));
+ EXPECT_EQ(1, lease_addresses.count("192.0.3.2"));
+}
+
+void Lease4CmdsTest::testLease4GetPagedZeroAddress() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"0.0.0.0\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ string exp_rsp = "2 IPv4 lease(s) found.";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetPagedIPv6Address() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"2001:db8::1\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ string exp_rsp = "'from' parameter value 2001:db8::1 is not an IPv4 address";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetPagedInvalidFrom() {
+ // Initialize lease manager (false = v6, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"foo\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ string exp_rsp = "'from' parameter value is neither 'start' keyword "
+ "nor a valid IPv4 address";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetPagedNoLimit() {
+ // Initialize lease manager (false = v6, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"start\""
+ " }"
+ "}";
+
+ string exp_rsp = "'limit' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetPagedLimitNotNumber() {
+ // Initialize lease manager (false = v6, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"start\","
+ " \"limit\": false"
+ " }"
+ "}";
+
+ string exp_rsp = "'limit' parameter must be a number";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetPagedLimitIsZero() {
+ // Initialize lease manager (false = v6, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"start\","
+ " \"limit\": 0"
+ " }"
+ "}";
+
+ string exp_rsp = "page size of retrieved leases must not be 0";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByHwAddressParams() {
+ // No parameters whatsoever.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hw-address\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "'hw-address' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // hw-address must be a string.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hw-address\",\n"
+ " \"arguments\": {"
+ " \"hw-address\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "'hw-address' parameter must be a string";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Simply bad value.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hw-address\",\n"
+ " \"arguments\": {"
+ " \"hw-address\": \"00::01:00:bc:0d:67\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "two consecutive separators (':') specified in a decoded string";
+ exp_rsp += " '00::01:00:bc:0d:67'";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByHwAddressFind0() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hw-address\",\n"
+ " \"arguments\": {"
+ " \"hw-address\": \"01:02:03:04:05:06\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "0 IPv4 lease(s) found.";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByHwAddressFind2() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Get the lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hw-address\",\n"
+ " \"arguments\": {"
+ " \"hw-address\": \"08:08:08:08:08:08\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "2 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr map = rsp->get("arguments");
+ ASSERT_TRUE(map);
+ ASSERT_EQ(Element::map, map->getType());
+ ConstElementPtr leases = map->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+ ASSERT_EQ(2, leases->size());
+
+ // Let's check if the response makes any sense.
+ ConstElementPtr lease = leases->get(0);
+ ASSERT_TRUE(lease);
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
+ lease = leases->get(1);
+ ASSERT_TRUE(lease);
+ checkLease4(lease, "192.0.3.1", 88, "08:08:08:08:08:08", false);
+}
+
+void Lease4CmdsTest::testLease4GetByClientIdParams() {
+ // No parameters whatsoever.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-client-id\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "'client-id' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // client-id must be a string.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-client-id\",\n"
+ " \"arguments\": {"
+ " \"client-id\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "'client-id' parameter must be a string";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Simply bad value.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-client-id\",\n"
+ " \"arguments\": {"
+ " \"client-id\": \"00::01:00:bc:0d:67\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "two consecutive separators (':') specified in a decoded string";
+ exp_rsp += " '00::01:00:bc:0d:67'";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByClientIdFind0() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-client-id\",\n"
+ " \"arguments\": {"
+ " \"client-id\": \"01:02:03:04\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "0 IPv4 lease(s) found.";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByClientIdFind2() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Get the lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-client-id\",\n"
+ " \"arguments\": {"
+ " \"client-id\": \"42:42:42:42:42:42:42:42\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "2 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr map = rsp->get("arguments");
+ ASSERT_TRUE(map);
+ ASSERT_EQ(Element::map, map->getType());
+ ConstElementPtr leases = map->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+ ASSERT_EQ(2, leases->size());
+
+ // Let's check if the response makes any sense.
+ ConstElementPtr lease = leases->get(0);
+ ASSERT_TRUE(lease);
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
+ lease = leases->get(1);
+ ASSERT_TRUE(lease);
+ checkLease4(lease, "192.0.3.1", 88, "08:08:08:08:08:08", false);
+}
+
+void Lease4CmdsTest::testLease4GetByHostnameParams() {
+ // No parameters whatsoever.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "'hostname' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // hostname must be a string.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "'hostname' parameter must be a string";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // hostname must be not empty.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": \"\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "'hostname' parameter is empty";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByHostnameFind0() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": \"foo.bar\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "0 IPv4 lease(s) found.";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4GetByHostnameFind2() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Get the lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": \"Myhost.Example.Com.\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "4 IPv4 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr map = rsp->get("arguments");
+ ASSERT_TRUE(map);
+ ASSERT_EQ(Element::map, map->getType());
+ ConstElementPtr leases = map->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+ ASSERT_EQ(4, leases->size());
+
+ // Let's check if the response makes any sense.
+ ConstElementPtr lease = leases->get(0);
+ ASSERT_TRUE(lease);
+ checkLease4(lease, "192.0.2.1", 44, "08:08:08:08:08:08", false);
+ lease = leases->get(2);
+ ASSERT_TRUE(lease);
+ checkLease4(lease, "192.0.3.1", 88, "08:08:08:08:08:08", false);
+}
+
+void Lease4CmdsTest::testLease4UpdateMissingParams() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Everything missing. What sort of crap is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and hwaddr missing, although
+ // Kea can now figure out subnet-id on its own).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.123\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'hw-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Better, but still no luck. (hwaddr missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.202\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'hw-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Close, but no cigars. (ip-address missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4UpdateBadParams() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // All params are there, but there's no subnet-id 123 configured.
+ // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6).
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv4 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the new IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"10.0.0.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 10.0.0.1 does not belong to subnet 192.0.2.0/24, subnet-id=44";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We don't use any of that bleeding edge nonsense in this museum. v4 only.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv4 address specified: 2001:db8:1::1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Duplicated comment.
+ txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"user-context\": { \"comment\": \"in user context\" },\n"
+ " \"comment\": \"direct\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Duplicated comment entry '\"direct\"' in user context "
+ "'{ \"comment\": \"in user context\" }'";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4UpdateNoLease() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "failed to update the lease with address 192.0.2.1 "
+ "either because the lease has been deleted or it has changed in the "
+ "database, in both cases a retry might succeed";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4Update() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure it's been updated.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease4CmdsTest::testLease4UpdateDeclinedLeases() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true, true);
+
+ checkLease4Stats(44, 2, 2);
+
+ checkLease4Stats(88, 2, 2);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 2, 1);
+
+ checkLease4Stats(88, 2, 2);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure it's been updated.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease4CmdsTest::testLease4UpdateNoSubnetId() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure it's been updated.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease4CmdsTest::testLease4UpdateNoSubnetIdDeclinedLeases() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true, true);
+
+ checkLease4Stats(44, 2, 2);
+
+ checkLease4Stats(88, 2, 2);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 2, 1);
+
+ checkLease4Stats(88, 2, 2);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure it's been updated.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease4CmdsTest::testLease4UpdateForceCreate() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\","
+ " \"force-create\": true"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure it contains expected values..
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease4CmdsTest::testLease4UpdateForceCreateNoSubnetId() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\","
+ " \"force-create\": true"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the subnet-id is figured out correctly.
+ EXPECT_EQ(44, l->subnet_id_);
+
+ // Make sure it contains expected values..
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease4CmdsTest::testLease4UpdateDoNotForceCreate() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"hostname\": \"newhostname.example.org\","
+ " \"force-create\": false"
+ " }\n"
+ "}";
+ string exp_rsp = "failed to update the lease with address 192.0.2.1 "
+ "either because the lease has been deleted or it has changed in the "
+ "database, in both cases a retry might succeed";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+}
+
+void Lease4CmdsTest::testLease4UpdateComment() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"42:42:42:42:42:42:42:42\",\n"
+ " \"comment\": \"a comment\",\n"
+ " \"user-context\": { \"foobar\": true }\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Now check that the lease is still there.
+ Lease4Ptr l = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(l);
+
+ // Make sure it's been updated.
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("42:42:42:42:42:42:42:42", l->hwaddr_->toText(false));
+
+ // Check user context.
+ ConstElementPtr ctx = l->getContext();
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(2, ctx->size());
+ ASSERT_TRUE(ctx->contains("comment"));
+ EXPECT_EQ("\"a comment\"", ctx->get("comment")->str());
+ ASSERT_TRUE(ctx->contains("foobar"));
+ EXPECT_EQ("true", ctx->get("foobar")->str());
+}
+
+void Lease4CmdsTest::testLease4DelMissingParams() {
+ // No parameters whatsoever. You want just a lease, any lease?
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "Mandatory 'subnet-id' parameter missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just the subnet-id won't cut it, either.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We can't identify your laptop by color. Sorry, buddy.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"color\",\n"
+ " \"identifier\": \"blue\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Incorrect identifier type: color, the only supported values are: "
+ "address, hw-address, duid";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Query by DUID is not supported in v4. Sorry.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"duid\",\n"
+ " \"identifier\": \"01:01:01:01:01:01\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Delete by duid is not allowed in v4.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier value is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"hw-address\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier-type is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier\": \"01:02:03:04:05\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4DelByAddrNotFound() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.5\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+}
+
+void Lease4CmdsTest::testLease4DelByAddr() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease deleted.";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+void Lease4CmdsTest::testLease4DelByAddrDeclinedLeases() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true, true);
+
+ checkLease4Stats(44, 2, 2);
+
+ checkLease4Stats(88, 2, 2);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease deleted.";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 1);
+
+ checkLease4Stats(88, 2, 2);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+void Lease4CmdsTest::testLease4DelByAddrBadParam() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv4 address specified: 2001:db8:1::1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4DelByHWAddrNotFound() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"01:02:03:04:05:06\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Make sure the lease is still there.
+ EXPECT_TRUE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+void Lease4CmdsTest::testLease4DelByHWAddr() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"hw-address\","
+ " \"identifier\": \"08:08:08:08:08:08\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease deleted.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+void Lease4CmdsTest::testLease4DelByClientIdNotFound() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"client-id\","
+ " \"identifier\": \"01:02:03:04\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease not found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Make sure the lease is still there.
+ EXPECT_TRUE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+void Lease4CmdsTest::testLease4DelByClientId() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"client-id\","
+ " \"identifier\": \"42:42:42:42:42:42:42:42\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv4 lease deleted.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 1, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+}
+
+void Lease4CmdsTest::testLease4Wipe() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 2 IPv4 lease(s) from subnet(s) 44";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Make sure the leases in subnet 44 are really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.2")));
+
+ // Make sure the leases from subnet 88 are still there.
+ EXPECT_TRUE(lmptr_->getLease4(IOAddress("192.0.3.1")));
+ EXPECT_TRUE(lmptr_->getLease4(IOAddress("192.0.3.2")));
+}
+
+void Lease4CmdsTest::testLease4WipeAll() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 0"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 4 IPv4 lease(s) from subnet(s) 44 88";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Make sure the leases in subnet 44 are really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.2")));
+
+ // Make sure the leases from subnet 88 are gone, too.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.3.1")));
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.3.2")));
+}
+
+void Lease4CmdsTest::testLease4WipeAllNoArgs() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ checkLease4Stats(44, 2, 0);
+
+ checkLease4Stats(88, 2, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\"\n"
+ "}";
+ string exp_rsp = "Deleted 4 IPv4 lease(s) from subnet(s) 44 88";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Make sure the leases in subnet 44 are really gone.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.1")));
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.2.2")));
+
+ // Make sure the leases from subnet 88 are gone, too.
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.3.1")));
+ EXPECT_FALSE(lmptr_->getLease4(IOAddress("192.0.3.2")));
+}
+
+void Lease4CmdsTest::testLease4WipeNoLeases() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 0 IPv4 lease(s) from subnet(s) 44";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+}
+
+void Lease4CmdsTest::testLease4WipeNoLeasesAll() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 0"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 0 IPv4 lease(s) from subnet(s) 44 88";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease4Stats(44, 0, 0);
+
+ checkLease4Stats(88, 0, 0);
+}
+
+void Lease4CmdsTest::testLease4BrokenUpdate() {
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Set the sanity checks level.
+ CfgMgr::instance().getCurrentCfg()->getConsistency()
+ ->setLeaseSanityCheck(CfgConsistency::LEASE_CHECK_FIX);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 444,\n"
+ " \"ip-address\": \"192.0.2.202\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"force-create\": true\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv4 subnet with "
+ "subnet-id=444 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4ResendDdnsBadParam() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Missing address parameter.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " }\n"
+ "}\n";
+
+ string exp_rsp = "'ip-address' parameter is missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Invalid address family.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"2001:db8:1::1\"\n"
+ " }\n"
+ "}\n";
+
+ exp_rsp = "Invalid IPv4 address specified: 2001:db8:1::1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // ip-address is not an address at all.
+ cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"221B Baker St.\"\n"
+ " }\n"
+ "}\n";
+
+ exp_rsp = "'221B Baker St.' is not a valid IP address.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4ResendDdnsDisabled() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ disableD2();
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.5\""
+ " }\n"
+ "}";
+
+ string exp_rsp = "DDNS updating is not enabled";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+ // With D2 disabled there is no queue, size should come back as -1.
+ EXPECT_EQ(ncrQueueSize(), -1);
+}
+
+void Lease4CmdsTest::testLease4ResendDdnsNoLease() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"192.0.2.5\"\n"
+ " }\n"
+ "}\n";
+ string exp_rsp = "No lease found for: 192.0.2.5";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease4CmdsTest::testLease4ResendNoHostname() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // NCR sender queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+
+ // Fetch the lease so we can replace the hostname with "".
+ Lease4Ptr lease = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(lease);
+ lease->hostname_ = "";
+ lmptr_->updateLease4(lease);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+
+ string exp_rsp = "Lease for: 192.0.2.1, has no hostname, nothing to update";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // There should not any NCRs queued.
+ EXPECT_EQ(ncrQueueSize(), 0);
+}
+
+void Lease4CmdsTest::testLease4ResendNoDirectionsEnabled() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // NCR sender queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+
+ // Fetch the lease so we can replace the hostname with "".
+ Lease4Ptr lease = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(lease);
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = false;
+ lmptr_->updateLease4(lease);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+
+ string exp_rsp = "Neither forward nor reverse updates enabled for lease for: 192.0.2.1";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // There should not any NCRs queued.
+ EXPECT_EQ(ncrQueueSize(), 0);
+}
+
+void Lease4CmdsTest::testLease4ResendDdnsEnabled() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Structure detailing a test scenario.
+ struct Scenario {
+ std::string description_;
+ bool fqdn_fwd_;
+ bool fqdn_rev_;
+ };
+
+ // Three test scenarios to verify each combination of true flags.
+ std::vector<Scenario> scenarios = {
+ { "fwd_only", true, false },
+ { "rev_only", false, true},
+ { "both", true, true},
+ };
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+
+ // Expected response string.
+ string exp_rsp = "NCR generated for: 192.0.2.1, hostname: myhost.example.com.";
+
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+
+ // Fetch the lease so we can update the DDNS direction flags.
+ Lease4Ptr lease = lmptr_->getLease4(IOAddress("192.0.2.1"));
+ ASSERT_TRUE(lease);
+ lease->fqdn_rev_ = scenario.fqdn_rev_;
+ lease->fqdn_fwd_ = scenario.fqdn_fwd_;
+ lmptr_->updateLease4(lease);
+
+ // Queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // We should have one entry in the queue.
+ ASSERT_EQ(ncrQueueSize(), 1);
+ verifyNameChangeRequest(CHG_ADD, scenario.fqdn_rev_, scenario.fqdn_fwd_,
+ "192.0.2.1", "myhost.example.com.");
+ }
+}
+
+void Lease4CmdsTest::testLease4DnsRemoveD2Enabled() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Structure detailing a test scenario.
+ struct Scenario {
+ std::string description_;
+ std::string hostname_;
+ bool fqdn_fwd_;
+ bool fqdn_rev_;
+ std::string update_ddns_;
+ bool exp_ncr_;
+ };
+
+ bool fwd = true;
+ bool rev = true;
+ bool ncr = true;
+
+ // Three test scenarios to verify each combination of true flags.
+ std::vector<Scenario> scenarios = {
+ {
+ "no_host",
+ "",
+ fwd, rev,
+ "\"update-ddns\": true",
+ !ncr
+ },
+ {
+ "no directions",
+ "myhost.example.com.",
+ !fwd, !rev,
+ "\"update-ddns\": true",
+ !ncr
+ },
+ {
+ "fwd_only",
+ "myhost.example.com.",
+ fwd, !rev,
+ "\"update-ddns\": true",
+ ncr
+ },
+ {
+ "rev_only",
+ "myhost.example.com.",
+ !fwd, rev,
+ "\"update-ddns\": true",
+ ncr
+ },
+ {
+ "both directions",
+ "myhost.example.com.",
+ fwd, rev,
+ "\"update-ddns\": true",
+ ncr
+ },
+ {
+ "default update-ddns",
+ "myhost.example.com.",
+ fwd, rev,
+ "",
+ !ncr
+ },
+ {
+ "update-ddns = false",
+ "myhost.example.com.",
+ fwd, rev,
+ "\"update-ddns\": false",
+ !ncr
+ },
+ };
+
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+
+ // Let's create a lease with scenario attributes.
+ Lease4Ptr lease = createLease4("192.0.2.8", 44, 0x08, 0x42);
+ lease->hostname_ = scenario.hostname_;
+ lease->fqdn_rev_ = scenario.fqdn_rev_;
+ lease->fqdn_fwd_ = scenario.fqdn_fwd_;
+ ASSERT_TRUE(lmptr_->addLease(lease));
+
+ // NCR Queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+
+ // Build the command
+ std::stringstream cmd;
+ cmd <<
+ "{"
+ " \"command\": \"lease4-del\","
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.8\"";
+
+ if (!scenario.update_ddns_.empty()) {
+ cmd << "," << scenario.update_ddns_;
+ }
+
+ cmd << "}}";
+
+ // Execute the delete command.
+ static_cast<void>(testCommand(cmd.str(), CONTROL_RESULT_SUCCESS, "IPv4 lease deleted."));
+
+ if (!scenario.exp_ncr_) {
+ // Should not have an ncr.
+ ASSERT_EQ(ncrQueueSize(), 0);
+ } else {
+ // We should have an ncr, verify it.
+ ASSERT_EQ(ncrQueueSize(), 1);
+ verifyNameChangeRequest(CHG_REMOVE, scenario.fqdn_rev_, scenario.fqdn_fwd_,
+ lease->addr_.toText(), lease->hostname_);
+ }
+
+ // Lease should have been deleted.
+ lease = lmptr_->getLease4(lease->addr_);
+ ASSERT_FALSE(lease);
+ }
+}
+
+void Lease4CmdsTest::testLease4DnsRemoveD2Disabled() {
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ disableD2();
+
+ // Delete for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"192.0.2.8\",\n"
+ " \"update-ddns\": true\n"
+ " }\n"
+ "}";
+
+ // Let's create a lease with scenario attributes.
+ Lease4Ptr lease = createLease4("192.0.2.8", 44, 0x08, 0x42);
+ lease->hostname_ = "myhost.example.com.";
+ lease->fqdn_rev_ = true;
+ lease->fqdn_fwd_ = true;
+ ASSERT_TRUE(lmptr_->addLease(lease));
+
+ // NCR Queue is not enabled.
+ ASSERT_EQ(ncrQueueSize(), -1);
+
+ // Execute the delete command.
+ static_cast<void>(testCommand(cmd, CONTROL_RESULT_SUCCESS, "IPv4 lease deleted."));
+
+ // NCR Queue is not enabled.
+ ASSERT_EQ(ncrQueueSize(), -1);
+
+ // Lease should have been deleted.
+ lease = lmptr_->getLease4(lease->addr_);
+ ASSERT_FALSE(lease);
+}
+
+// Verify that v4 lease add handles conflict as expected.
+void Lease4CmdsTest::testLease4ConflictingAdd() {
+ MultiThreadingTest mt(true);
+
+ // Initialize lease manager (false = v4, false = don't add leases)
+ initLeaseMgr(false, false);
+
+ // Make sure the lease doesn't exist.
+ IOAddress addr("192.0.2.1");
+ Lease4Ptr lease = lmptr_->getLease4(addr);
+ ASSERT_FALSE(lease);
+
+ // Verify stats show no leases.
+ checkLease4Stats(44, 0, 0);
+
+ // Lock the address.
+ ResourceHandler4 resource_handler;
+ ASSERT_TRUE(resource_handler.tryLock4(addr));
+
+ // Now let's try to add the lease.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"comment\": \"a comment\"\n"
+ " }\n"
+ "}";
+
+ string exp_rsp = "ResourceBusy: IP address:192.0.2.1 could not be added.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Lease should not have been added.
+ lease = lmptr_->getLease4(addr);
+ ASSERT_FALSE(lease);
+
+ // Stats should not have changed.
+ checkLease4Stats(44, 0, 0);
+}
+
+void Lease4CmdsTest::testLease4ConflictingUpdate() {
+ MultiThreadingTest mt(true);
+
+ // Initialize lease manager (false = v4, true = add leases)
+ initLeaseMgr(false, true);
+
+ // Verify stats show no leases.
+ checkLease4Stats(44, 2, 0);
+
+ // Make sure the lease exists.
+ IOAddress addr("192.0.2.1");
+ Lease4Ptr lease = lmptr_->getLease4(addr);
+ ASSERT_TRUE(lease);
+ // Save a copy of the original lease.
+ Lease4 original_lease = *lease;
+
+ // Lock the address.
+ ResourceHandler4 resource_handler;
+ ASSERT_TRUE(resource_handler.tryLock4(addr));
+
+ // Now let's try to update the lease.
+ string txt =
+ "{\n"
+ " \"command\": \"lease4-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 44,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"hw-address\": \"2a:2b:2c:2d:2e:2f\",\n"
+ " \"comment\": \"a comment\"\n"
+ " }\n"
+ "}";
+
+ string exp_rsp = "ResourceBusy: IP address:192.0.2.1 could not be updated.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Fetch the lease again.
+ lease = lmptr_->getLease4(addr);
+ ASSERT_TRUE(lease);
+
+ // Lease should not have been changed.
+ EXPECT_EQ(original_lease, *lease);
+}
+
+TEST_F(Lease4CmdsTest, lease4AddMissingParams) {
+ testLease4AddMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddBadParams) {
+ testLease4AddBadParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddBadParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddBadParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4Add) {
+ testLease4Add();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4Add();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddDeclinedLeases) {
+ testLease4AddDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddExisting) {
+ testLease4AddExisting();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddExistingMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddExisting();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddSubnetIdMissing) {
+ testLease4AddSubnetIdMissing();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddSubnetIdMissingMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddSubnetIdMissing();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddSubnetIdMissingDeclinedLeases) {
+ testLease4AddSubnetIdMissingDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddSubnetIdMissingDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddSubnetIdMissingDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddSubnetIdMissingBadAddr) {
+ testLease4AddSubnetIdMissingBadAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddSubnetIdMissingBadAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddSubnetIdMissingBadAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddNegativeExpireTime) {
+ testLease4AddNegativeExpireTime();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddNegativeExpireTimeMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddNegativeExpireTime();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddNegativeCltt) {
+ testLease4AddNegativeCltt();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddNegativeClttMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddNegativeCltt();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddFullAddr) {
+ testLease4AddFullAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddFullAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddFullAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddComment) {
+ testLease4AddComment();
+}
+
+TEST_F(Lease4CmdsTest, lease4AddCommentMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4AddComment();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetMissingParams) {
+ testLease4GetMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByAddrBadParam) {
+ testLease4GetByAddrBadParam();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByAddrBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByAddrBadParam();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByAddrNotFound) {
+ testLease4GetByAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByAddrNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByAddr) {
+ testLease4GetByAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHWAddrNotFound) {
+ testLease4GetByHWAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHWAddrNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHWAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHWAddr) {
+ testLease4GetByHWAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHWAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHWAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdNotFound) {
+ testLease4GetByClientIdNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByClientIdNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientId) {
+ testLease4GetByClientId();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByClientId();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAll) {
+ testLease4GetAll();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetAll();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllNoLeases) {
+ testLease4GetAllNoLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllNoLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetAllNoLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllBySubnetId) {
+ testLease4GetAllBySubnetId();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllBySubnetIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetAllBySubnetId();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllBySubnetIdNoLeases) {
+ testLease4GetAllBySubnetIdNoLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllBySubnetIdNoLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetAllBySubnetIdNoLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllByMultipleSubnetIds) {
+ testLease4GetAllByMultipleSubnetIds();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetAllByMultipleSubnetIdsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetAllByMultipleSubnetIds();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetBySubnetIdInvalidArguments) {
+ testLease4GetBySubnetIdInvalidArguments();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetBySubnetIdInvalidArgumentsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetBySubnetIdInvalidArguments();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPaged) {
+ testLease4GetPaged();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetPaged();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedZeroAddress) {
+ testLease4GetPagedZeroAddress();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedZeroAddressMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetPagedZeroAddress();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedIPv6Address) {
+ testLease4GetPagedIPv6Address();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedIPv6AddressMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetPagedIPv6Address();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedInvalidFrom) {
+ testLease4GetPagedInvalidFrom();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedInvalidFromMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetPagedInvalidFrom();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedNoLimit) {
+ testLease4GetPagedNoLimit();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedNoLimitMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetPagedNoLimit();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedLimitNotNumber) {
+ testLease4GetPagedLimitNotNumber();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedLimitNotNumberMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetPagedLimitNotNumber();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedLimitIsZero) {
+ testLease4GetPagedLimitIsZero();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetPagedLimitIsZeroMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetPagedLimitIsZero();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHwAddressParams) {
+ testLease4GetByHwAddressParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHwAddressParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHwAddressParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHwAddressFind0) {
+ testLease4GetByHwAddressFind0();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHwAddressFind0MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHwAddressFind0();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHwAddressFind2) {
+ testLease4GetByHwAddressFind2();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHwAddressFind2MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHwAddressFind2();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdParams) {
+ testLease4GetByClientIdParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByClientIdParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdFind0) {
+ testLease4GetByClientIdFind0();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdFind0MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByClientIdFind0();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdFind2) {
+ testLease4GetByClientIdFind2();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByClientIdFind2MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByClientIdFind2();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHostnameParams) {
+ testLease4GetByHostnameParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHostnameParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHostnameParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHostnameFind0) {
+ testLease4GetByHostnameFind0();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHostnameFind0MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHostnameFind0();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHostnameFind2) {
+ testLease4GetByHostnameFind2();
+}
+
+TEST_F(Lease4CmdsTest, lease4GetByHostnameFind2MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4GetByHostnameFind2();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateMissingParams) {
+ testLease4UpdateMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateBadParams) {
+ testLease4UpdateBadParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateBadParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateBadParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateNoLease) {
+ testLease4UpdateNoLease();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateNoLeaseMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateNoLease();
+}
+
+TEST_F(Lease4CmdsTest, lease4Update) {
+ testLease4Update();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4Update();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateDeclinedLeases) {
+ testLease4UpdateDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateNoSubnetId) {
+ testLease4UpdateNoSubnetId();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateNoSubnetIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateNoSubnetId();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateNoSubnetIdDeclinedLeases) {
+ testLease4UpdateNoSubnetIdDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateNoSubnetIdDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateNoSubnetIdDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateForceCreate) {
+ testLease4UpdateForceCreate();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateForceCreateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateForceCreate();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateForceCreateNoSubnetId) {
+ testLease4UpdateForceCreateNoSubnetId();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateForceCreateNoSubnetIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateForceCreateNoSubnetId();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateDoNotForceCreate) {
+ testLease4UpdateDoNotForceCreate();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateDoNotForceCreateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateDoNotForceCreate();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateComment) {
+ testLease4UpdateComment();
+}
+
+TEST_F(Lease4CmdsTest, lease4UpdateCommentMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4UpdateComment();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelMissingParams) {
+ testLease4DelMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelMissingParams();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddrNotFound) {
+ testLease4DelByAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddrNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddr) {
+ testLease4DelByAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddrDeclinedLeases) {
+ testLease4DelByAddrDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddrDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByAddrDeclinedLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddrBadParam) {
+ testLease4DelByAddrBadParam();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByAddrBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByAddrBadParam();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByHWAddrNotFound) {
+ testLease4DelByHWAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByHWAddrNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByHWAddrNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByHWAddr) {
+ testLease4DelByHWAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByHWAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByHWAddr();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByClientIdNotFound) {
+ testLease4DelByClientIdNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByClientIdNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByClientIdNotFound();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByClientId) {
+ testLease4DelByClientId();
+}
+
+TEST_F(Lease4CmdsTest, lease4DelByClientIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DelByClientId();
+}
+
+TEST_F(Lease4CmdsTest, lease4Wipe) {
+ testLease4Wipe();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4Wipe();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeAll) {
+ testLease4WipeAll();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeAllMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4WipeAll();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeAllNoArgs) {
+ testLease4WipeAllNoArgs();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeAllNoArgsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4WipeAllNoArgs();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeNoLeases) {
+ testLease4WipeNoLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeNoLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4WipeNoLeases();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeNoLeasesAll) {
+ testLease4WipeNoLeasesAll();
+}
+
+TEST_F(Lease4CmdsTest, lease4WipeNoLeasesAllMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4WipeNoLeasesAll();
+}
+
+
+TEST_F(Lease4CmdsTest, lease4BrokenUpdate) {
+ testLease4BrokenUpdate();
+}
+
+TEST_F(Lease4CmdsTest, lease4BrokenUpdateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4BrokenUpdate();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsBadParam) {
+ testLease4ResendDdnsBadParam();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4ResendDdnsBadParam();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsDisabled) {
+ testLease4ResendDdnsDisabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsDisabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4ResendDdnsDisabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsNoLease) {
+ testLease4ResendDdnsNoLease();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsNoLeaseMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4ResendDdnsNoLease();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendNoHostname) {
+ testLease4ResendNoHostname();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendNoHostnameMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4ResendNoHostname();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendNoDirectionsEnabled) {
+ testLease4ResendNoDirectionsEnabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendNoDirectionsEnabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4ResendNoDirectionsEnabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsEnabled) {
+ testLease4ResendDdnsEnabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4ResendDdnsEnabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4ResendDdnsEnabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4DnsRemoveD2Enabled) {
+ testLease4DnsRemoveD2Enabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4DnsRemoveD2EnabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DnsRemoveD2Enabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4DnsRemoveD2Disabled) {
+ testLease4DnsRemoveD2Disabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4DnsRemoveD2DisabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease4DnsRemoveD2Disabled();
+}
+
+TEST_F(Lease4CmdsTest, lease4ConflictingAddMultiThreading) {
+ testLease4ConflictingAdd();
+}
+
+TEST_F(Lease4CmdsTest, lease4ConflictingUpdateMultiThreading) {
+ testLease4ConflictingUpdate();
+}
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds6_unittest.cc b/src/hooks/dhcp/lease_cmds/tests/lease_cmds6_unittest.cc
new file mode 100644
index 0000000..84c6e61
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds6_unittest.cc
@@ -0,0 +1,4466 @@
+// Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
+#include <config/command_mgr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/ncr_generator.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/resource_handler.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <lease_cmds_unittest.h>
+#include <stats/stats_mgr.h>
+#include <testutils/user_context_utils.h>
+#include <testutils/multi_threading_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <set>
+
+using namespace std;
+using namespace isc;
+using namespace isc::hooks;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp_ddns;
+using namespace isc::asiolink;
+using namespace isc::stats;
+using namespace isc::test;
+
+namespace {
+
+/// @brief Class dedicated to testing v4 part of lease_cmds library.
+///
+/// Provides convenience methods for loading, testing all commands and
+/// unloading the lease_cmds library.
+class Lease6CmdsTest : public LeaseCmdsTest {
+public:
+ /// @brief Checks if specified response contains IPv6 lease
+ ///
+ /// @param lease Element tree that represents a lease
+ /// @param ip expected IP address (or prefix)
+ /// @param prefixlen prefix length (0 = expect address)
+ /// @param subnet_id expected subnet-id
+ /// @param duid expected value of DUID
+ /// @param hwaddr_required true if hwaddr is expected
+ void checkLease6(isc::data::ConstElementPtr l, std::string ip,
+ uint8_t prefixlen,
+ uint32_t subnet_id, std::string duid,
+ bool hwaddr_required) {
+ ASSERT_TRUE(l);
+
+ // If the element is a list we need to retrieve the lease that
+ // we're interested in.
+ if (l->getType() == isc::data::Element::list) {
+ std::vector<isc::data::ElementPtr> e = l->listValue();
+ for (auto it = e.begin(); it != e.end(); ++it) {
+ isc::data::ConstElementPtr ip_address = (*it)->get("ip-address");
+ if (ip_address && ip_address->stringValue() == ip) {
+ l = (*it);
+ break;
+ }
+ }
+
+ ASSERT_TRUE(l);
+ }
+
+ ASSERT_TRUE(l->contains("ip-address"));
+ EXPECT_EQ(ip, l->get("ip-address")->stringValue());
+ if (prefixlen != 0) {
+ ASSERT_TRUE(l->get("prefix-len"));
+ EXPECT_EQ(prefixlen, l->get("prefix-len")->intValue());
+ }
+
+ ASSERT_TRUE(l->contains("subnet-id"));
+ EXPECT_EQ(subnet_id, l->get("subnet-id")->intValue());
+
+ ASSERT_TRUE(l->contains("duid"));
+ EXPECT_EQ(duid, l->get("duid")->stringValue());
+
+ // hwaddr may or may not appear
+ if (hwaddr_required) {
+ EXPECT_TRUE(l->get("hwaddr"));
+ }
+
+ // Check that there are expected fields
+ ASSERT_TRUE(l->contains("preferred-lft"));
+ ASSERT_TRUE(l->contains("valid-lft"));
+ ASSERT_TRUE(l->contains("cltt"));
+ ASSERT_TRUE(l->contains("subnet-id"));
+ ASSERT_TRUE(l->contains("fqdn-fwd"));
+ ASSERT_TRUE(l->contains("fqdn-rev"));
+ ASSERT_TRUE(l->contains("hostname"));
+ ASSERT_TRUE(l->contains("state"));
+
+ // Check that there are no v4 specific fields.
+ ASSERT_FALSE(l->contains("client-id"));
+
+ // Assuming that these values were used to create the lease.
+ // If we ever want to test different values they will need to
+ // be added as parameters to this function.
+ EXPECT_EQ(HIGH_VALID_LIFETIME,
+ static_cast<uint32_t>(l->get("valid-lft")->intValue()));
+ EXPECT_EQ(DEC_2030_TIME, l->get("cltt")->intValue());
+ }
+
+ /// @brief Check lease4 statistics.
+ ///
+ /// @param id Subnet id of the stats to check.
+ /// @assigned_nas The expected value of assigned nas addresses in subnet.
+ /// @declined The expected value of declined addresses in subnet.
+ /// @assigned_pds The expected value of assigned pds addresses in subnet.
+ void checkLease6Stats(isc::dhcp::SubnetID id, int32_t assigned_nas,
+ int32_t declined, int32_t assigned_pds) {
+ ASSERT_EQ(isc::stats::StatsMgr::instance().getObservation(
+ isc::stats::StatsMgr::generateName("subnet", id,
+ "assigned-nas"))->getInteger().first, assigned_nas);
+
+ ASSERT_EQ(isc::stats::StatsMgr::instance().getObservation(
+ isc::stats::StatsMgr::generateName("subnet", id,
+ "declined-addresses"))->getInteger().first, declined);
+
+ ASSERT_EQ(isc::stats::StatsMgr::instance().getObservation(
+ isc::stats::StatsMgr::generateName("subnet", id,
+ "assigned-pds"))->getInteger().first, assigned_pds);
+ }
+
+ /// @brief Check that lease6-add with missing parameters will fail.
+ void testLease6AddMissingParams();
+
+ /// @brief Verify that lease6-add can be rejected if parameters are
+ /// specified, but have incorrect values.
+ void testLease6AddBadParams();
+
+ /// @brief Check that a simple, well formed lease6 can be added.
+ void testLease6Add();
+
+ /// @brief Check that a simple, well formed lease6 can be added.
+ void testLease6AddDeclinedLeases();
+
+ /// @brief Check that a lease6 is not added when it already exists.
+ void testLease6AddExisting();
+
+ /// @brief Check that subnet-id is optional. If not specified, Kea should
+ /// select it on its own.
+ void testLease6AddSubnetIdMissing();
+
+ /// @brief Check that subnet-id is optional. If not specified, Kea should
+ /// select it on its own.
+ void testLease6AddSubnetIdMissingDeclinedLeases();
+
+ /// @brief Check that subnet-id is optional. If not specified, Kea should
+ /// select it on its own, but if there's no subnet for address being added,
+ /// it should fail.
+ void testLease6AddSubnetIdMissingBadAddr();
+
+ /// @brief Check that the lease with negative expiration time is rejected.
+ void testLease6AddNegativeExpireTime();
+
+ /// @brief Check that the lease with negative cltt is rejected.
+ void testLease6AddNegativeCltt();
+
+ /// @brief Check that a simple, well formed prefix lease can be added.
+ void testLease6AddPrefix();
+
+ /// @brief Check that a well formed lease6 with tons of parameters can be
+ /// added.
+ void testLease6AddFullAddr();
+
+ /// @brief Check that a well formed lease6 with a comment can be added.
+ void testLease6AddComment();
+
+ /// @brief Check that lease6-get can handle a situation when the query is
+ /// broken (some required parameters are missing).
+ void testLease6GetMissingParams();
+
+ /// @brief Check that lease6-get sanitizes its input.
+ void testLease6GetByAddrBadParam();
+
+ /// @brief Check that lease6-get can handle a situation when the query is
+ /// correctly formed, but the lease is not there.
+ void testLease6GetByAddrNotFound();
+
+ /// @brief Check that lease6-get can return a lease by address.
+ void testLease6GetByAddr();
+
+ /// @brief Check that lease6-get can return a lease by prefix.
+ void testLease6GetByAddrPrefix();
+
+ /// @bfief Check that lease6-get rejects queries by client-id.
+ void testLease6GetByClientIdInvalidType();
+
+ /// @brief Check that lease6-get can handle a situation when the query is
+ /// correctly formed, but the lease is not there.
+ void testLease6GetByDuidNotFound();
+
+ /// @bfief Check that lease6-get can find a lease by duid.
+ void testLease6GetByDuid();
+
+ /// @brief Check that lease6-get-all returns all leases.
+ void testLease6GetAll();
+
+ /// @brief Check that lease6-get-all returns empty set if no leases are
+ /// found.
+ void testLease6GetAllNoLeases();
+
+ /// @brief Check that lease6-get-all returns all leases for a subnet.
+ void testLease6GetAllBySubnetId();
+
+ /// @brief Check that lease6-get-all returns empty set when no leases are
+ /// found.
+ void testLease6GetAllBySubnetIdNoLeases();
+
+ /// @brief Check that lease6-get-all returns leases from multiple subnets.
+ void testLease6GetAllByMultipleSubnetIds();
+
+ /// @brief Check that lease6-get-all checks its input arguments.
+ void testLease6GetBySubnetIdInvalidArguments();
+
+ /// @brief Check that multiple calls to lease6-get-page return all leases.
+ void testLease6GetPaged();
+
+ /// @brief Verifies that first page of IPv6 leases can be retrieved by
+ /// specifying zero IPv6 address.
+ void testLease6GetPagedZeroAddress();
+
+ /// @brief Verifies that IPv4 address as a start address is rejected.
+ void testLease6GetPagedIPv4Address();
+
+ /// @brief Verifies that value of 'from' parameter other than 'start' or an
+ /// IPv6 address is rejected.
+ void testLease6GetPagedInvalidFrom();
+
+ /// @brief Verifies that limit is mandatory.
+ void testLease6GetPagedNoLimit();
+
+ /// @brief Verifies that the limit must be a number.
+ void testLease6GetPagedLimitNotNumber();
+
+ /// @brief Verifies that the limit of 0 is rejected.
+ void testLease6GetPagedLimitIsZero();
+
+ /// @brief Check that lease6-get-by-duid can handle a situation when the
+ /// query is broken (required parameter is missing).
+ void testLease6GetByDuidParams();
+
+ /// @brief Check that lease6-get-by-duid works as expected (find no lease).
+ void testLease6GetByDuidFind0();
+
+ /// @brief Check that lease6-get-by-duid works as expected (find two
+ /// leases).
+ void testLease6GetByDuidFind2();
+
+ /// @brief Check that lease6-get-by-hostname can handle a situation when
+ /// the query is broken (required parameter is missing).
+ void testLease6GetByHostnameParams();
+
+ /// @brief Check that lease6-get-by-hostname works as expected (find no
+ /// lease).
+ void testLease6GetByHostnameFind0();
+
+ /// @brief Check that lease6-get-by-hostname works as expected (find two
+ /// leases).
+ void testLease6GetByHostnameFind2();
+
+ /// @brief Test checks if lease6-update handler refuses calls with missing
+ /// parameters.
+ void testLease6UpdateMissingParams();
+
+ /// @brief Verify that lease6-update can be rejected if parameters are
+ /// specified, but have incorrect values.
+ void testLease6UpdateBadParams();
+
+ /// @brief Check that lease6-update correctly handles case when there is no
+ /// lease to be updated.
+ void testLease6UpdateNoLease();
+
+ /// @brief Check that a lease6 can be updated. We're changing hw-address and
+ /// a hostname.
+ void testLease6Update();
+
+ /// @brief Check that a lease6 can be updated. We're changing hw-address and
+ /// a hostname.
+ void testLease6UpdateDeclinedLeases();
+
+ /// @brief Check that a lease6 can be updated. We're changing hw-address and
+ /// a hostname. The subnet-id is not specified.
+ void testLease6UpdateNoSubnetId();
+
+ /// @brief Check that a lease6 can be updated. We're changing hw-address and
+ /// a hostname. The subnet-id is not specified.
+ void testLease6UpdateNoSubnetIdDeclinedLeases();
+
+ /// @brief Check that a lease6 is created if it doesn't exist during the
+ /// update. To trigger this behavior 'force-create' boolean parameter must
+ /// be included in the command.
+ void testLease6UpdateForceCreate();
+
+ /// @brief Check that a lease6 is created if it doesn't exist during the
+ /// update. To trigger this behavior 'force-create' boolean parameter must
+ /// be included in the command. The subnet-id is not specified, Kea will
+ /// figure it out.
+ void testLease6UpdateForceCreateNoSubnetId();
+
+ /// @brief Check that lease6-update correctly handles case when the
+ /// 'force-create' parameter is explicitly set to false.
+ void testLease6UpdateDoNotForceCreate();
+
+ /// @brief Check that a lease6 can be updated. We're adding a comment and an
+ /// user context.
+ void testLease6UpdateComment();
+
+ /// @brief Check that lease6-del can handle a situation when the query is
+ /// broken (some required parameters are missing).
+ void testLease6DelMissingParams();
+
+ /// @brief Check that lease6-del(subnet-id, addr6) can handle a situation
+ /// when the query is correctly formed, but the lease is not there.
+ void testLease6DelByAddrNotFound();
+
+ /// @brief Check that lease6-del sanitizes its input.
+ void testLease6DelByAddrBadParam();
+
+ /// @brief Check that lease6-del(subnet-id, addr6) can handle a situation
+ /// when the query is correctly formed and the lease is deleted.
+ void testLease6DelByAddr();
+
+ /// @brief Check that lease6-del(subnet-id, addr6) can handle a situation
+ /// when the query is correctly formed and the lease is deleted.
+ void testLease6DelByAddrDeclinedLeases();
+
+ /// @brief Check that lease6-del(type, addr6) can handle a
+ /// situation when the query is correctly formed and the lease is deleted.
+ void testLease6DelByAddrPrefix();
+
+ /// @brief Check that lease6-del(subnet-id, addr) can handle a situation
+ /// when the query is correctly formed, but the lease is not there.
+ void testLease6DelByDuidNotFound();
+
+ /// @brief Check that lease6-del(subnet-id, iaid, identifier-type,
+ /// identifier) can find and delete a lease by duid.
+ void testLease6DelByDuid();
+
+ /// @brief Check that lease6-wipe can remove leases.
+ void testLease6Wipe();
+
+ /// @brief Check that lease6-wipe can remove leases from all subnets at
+ /// once.
+ void testLease6WipeAll();
+
+ /// @brief Check that lease6-wipe can remove leases from all subnets at
+ /// once (when no parameters are specified).
+ void testLease6WipeAllNoArgs();
+
+ /// @brief Check that lease6-wipe properly reports when no leases were
+ /// deleted.
+ void testLease6WipeNoLeases();
+
+ /// @brief Check that lease6-wipe properly reports when no leases were
+ /// deleted.
+ void testLease6WipeNoLeasesAll();
+
+ /// @brief Check that an attempt to update a lease (set incorrect
+ /// subnet-id) will fail.
+ void testLease6BrokenUpdate();
+
+ /// @brief This test verifies that it is possible to add two leases and
+ /// delete two leases as a result of the single lease6-bulk-apply command.
+ void testLease6BulkApply();
+
+ /// @brief This test verifies that the lease parameters are checked when
+ /// adding new leases only with the lease6-bulk-apply.
+ void testLease6BulkApplyAddsOnlyBadParam();
+
+ /// @brief This test verifies that it is possible to send new leases only
+ /// with the lease6-bulk-apply.
+ void testLease6BulkApplyAddsOnly();
+
+ /// @brief This test verifies that the lease parameters are checked when
+ /// updating leases only with the lease6-bulk-apply.
+ void testLease6BulkApplyUpdatesOnlyBadParam();
+
+ /// @brief This test verifies that it is possible to update leases with the
+ /// lease6-bulk-apply.
+ void testLease6BulkApplyUpdatesOnly();
+
+ /// @brief This test verifies that it is possible to only delete leases with
+ /// the lease6-bulk-apply.
+ void testLease6BulkApplyDeletesOnly();
+
+ /// @brief This test verifies that deleting non existing leases returns an
+ /// 'empty' result.
+ void testLease6BulkApplyDeleteNonExiting();
+
+ /// @brief Check that changes for other leases are not applied if one of the
+ /// leases is malformed.
+ void testLease6BulkApplyRollback();
+
+ /// @brief Check that lease6-resend-ddns sanitizes its input.
+ void testLease6ResendDdnsBadParam();
+
+ /// @brief Check that lease6-resend-ddns does not generate an NCR for given
+ /// lease when DDNS updating is disabled.
+ void testLease6ResendDdnsDisabled();
+
+ /// @brief Check that lease6-resend-ddns does not generate an NCR for when
+ /// there is no matching lease.
+ void testLease6ResendDdnsNoLease();
+
+ /// @brief Check that lease6-resend-ddns does not generate an NCR for given
+ /// lease when updates are enabled but Lease::hostname_ is blank.
+ void testLease6ResendNoHostname();
+
+ /// @brief Check that lease6-resend-ddns does not generate an NCR for given
+ /// lease when updates are enabled, Lease::hostname_ is not blank, but both
+ /// Lease::fqdn_fwd_ and fdqn_rev_ are false.
+ void testLease6ResendNoDirectionsEnabled();
+
+ /// @brief Check that lease6-resend-ddns can generate an NCR for given
+ /// lease when updates are enabled, Lease::hostname_ is not blank, and at
+ /// least one of Lease::fqdn_fwd_ or fdqn_rev_ are true.
+ void testLease6ResendDdnsEnabled();
+
+ /// @brief Check that lease6-del does (or does not) generate an NCR to
+ /// remove DNS for a given lease based on lease content when DDNS updates
+ /// are enabled.
+ void testLease6DnsRemoveD2Enabled();
+
+ /// @brief Check that lease6-del does not generate an NCR to remove DNS for
+ /// a given lease based on lease content when DDNS updates are disabled.
+ void testLease6DnsRemoveD2Disabled();
+
+ /// @brief Verify that v6 lease add handles conflict as expected.
+ void testLease6ConflictingAdd();
+
+ /// @brief Verify that v6 lease update handles conflict as expected.
+ void testLease6ConflictingUpdate();
+
+ /// @brief Verify that v6 lease bulk update handles conflict as expected.
+ void testLease6ConflictingBulkApplyAdd();
+};
+
+void Lease6CmdsTest::testLease6AddMissingParams() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Everything missing. What sort of nonsense is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and duid missing, but subnet-id
+ // can now be figured out by kea)
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::3\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'duid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just subnet-id and ip is not enough (duid missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::3\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'duid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just subnet-id and duid is not enough (ip, iaid missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just subnet-id, duid and iaid is not enough (ip missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Close, but no cigars. Still missing iaid.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"ip-address\": \"2001:db8:1::3\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'iaid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6AddBadParams() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // All params are there, but there's no subnet-id 123 configured.
+ // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6).
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv6 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"3000::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 3000::3 does not belong to subnet 2001:db8:1::/48, subnet-id=66";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // v4? You're a time traveler from early 80s or what?
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv6 address specified: 192.0.2.1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Invalid state: the only supported values are 0,1,2.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"state\": 123\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid state value: 123, supported values are: 0 (default), 1 "
+ "(declined) and 2 (expired-reclaimed)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Duplicated comment.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": { \"comment\": \"in user context\" },\n"
+ " \"comment\": \"direct\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Duplicated comment entry '\"direct\"' in user context "
+ "'{ \"comment\": \"in user context\" }'";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Negative expiration time.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": { \"comment\": \"in user context\" },\n"
+ " \"expire\": -6218189367\n"
+ " }\n"
+ "}";
+ exp_rsp = "expiration time must be positive for address 2001:db8:1::1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Negative cltt
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": { \"comment\": \"in user context\" },\n"
+ " \"expire\": 123456,\n"
+ " \"valid-lft\": 123457"
+ " }\n"
+ "}";
+ exp_rsp = "expiration time must be greater than valid lifetime for address "
+ "2001:db8:1::1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Invalid declined state (1) for PD prefix.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"prefix-len\": 48,\n"
+ " \"type\": \"IA_PD\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234,\n"
+ " \"state\": 1"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid declined state for PD prefix.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6Add() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 2001:db8:1::3, subnet-id 66 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+
+ // Test execution is fast. The cltt should be set to now. In some rare
+ // cases we could have the seconds counter to tick, so having a value off
+ // by one is ok.
+ EXPECT_LE(abs(l->cltt_ - time(NULL)), 1);
+ EXPECT_EQ(0, l->state_);
+}
+
+void Lease6CmdsTest::testLease6AddDeclinedLeases() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"state\": 1,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 2001:db8:1::3, subnet-id 66 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 1, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+
+ // Test execution is fast. The cltt should be set to now. In some rare
+ // cases we could have the seconds counter to tick, so having a value off
+ // by one is ok.
+ EXPECT_LE(abs(l->cltt_ - time(NULL)), 1);
+ EXPECT_EQ(1, l->state_);
+}
+
+void Lease6CmdsTest::testLease6AddExisting() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease already exists.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+}
+
+void Lease6CmdsTest::testLease6AddSubnetIdMissing() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command (without subnet-id)
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 2001:db8:1::3, subnet-id 66 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there and has correct subnet-id.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(66, l->subnet_id_);
+}
+
+void Lease6CmdsTest::testLease6AddSubnetIdMissingDeclinedLeases() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command (without subnet-id)
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"state\": 1,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 2001:db8:1::3, subnet-id 66 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 1, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there and has correct subnet-id.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(66, l->subnet_id_);
+}
+
+void Lease6CmdsTest::testLease6AddSubnetIdMissingBadAddr() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command (without subnet-id)
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:ffff::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "subnet-id not specified and failed to find a subnet for "
+ "address 2001:ffff::1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease was not added.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:ffff::1"));
+ ASSERT_FALSE(l);
+}
+
+void Lease6CmdsTest::testLease6AddNegativeExpireTime() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Add a lease with negative expiration time.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234,\n"
+ " \"expire\": -6218189367\n"
+ " }\n"
+ "}";
+ string exp_rsp = "expiration time must be positive for address 2001:db8:1::3";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease was not added.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_FALSE(l);
+}
+
+void Lease6CmdsTest::testLease6AddNegativeCltt() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Add a lease with negative cltt (expiration time - valid lifetime)
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"expire\": 123456,\n"
+ " \"iaid\": 1234,\n"
+ " \"valid-lft\": 123457"
+ " }\n"
+ "}";
+ string exp_rsp = "expiration time must be greater than valid lifetime for "
+ "address 2001:db8:1::3";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease was not added.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_FALSE(l);
+}
+
+void Lease6CmdsTest::testLease6AddPrefix() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:abcd::\",\n"
+ " \"prefix-len\": 48,\n"
+ " \"type\": \"IA_PD\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for prefix 2001:db8:abcd::/48, subnet-id 66 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 1);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_PD, IOAddress("2001:db8:abcd::"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(Lease::TYPE_PD, l->type_);
+ EXPECT_EQ(48, l->prefixlen_);
+ EXPECT_EQ("", l->hostname_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease6CmdsTest::testLease6AddFullAddr() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"duid\": \"01:02:03:04:05:06:07:08\",\n"
+ " \"iaid\": 1234,\n"
+ " \"hw-address\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"preferred-lft\": 500,\n"
+ " \"valid-lft\": 1000,\n"
+ " \"expire\": 6218189367,\n"
+ " \"fqdn-fwd\": true,\n"
+ " \"fqdn-rev\": true,\n"
+ " \"hostname\": \"urania.example.org\",\n"
+ " \"user-context\": { \"foobar\": true }\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 2001:db8:1::3, subnet-id 66 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_TRUE(l);
+ EXPECT_EQ(Lease::TYPE_NA, l->type_);
+ EXPECT_EQ("2001:db8:1::3", l->addr_.toText());
+ ASSERT_TRUE(l->hwaddr_);
+ EXPECT_EQ("1a:1b:1c:1d:1e:1f", l->hwaddr_->toText(false));
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("01:02:03:04:05:06:07:08", l->duid_->toText());
+ // expire (6218189367) - valid_lft(1000)
+ EXPECT_EQ(static_cast<time_t>(6218189367 - 1000), l->cltt_);
+ EXPECT_EQ(true, l->fqdn_fwd_);
+ EXPECT_EQ(true, l->fqdn_rev_);
+ EXPECT_EQ("urania.example.org", l->hostname_);
+ ASSERT_TRUE(l->getContext());
+ EXPECT_EQ("{ \"foobar\": true }", l->getContext()->str());
+}
+
+void Lease6CmdsTest::testLease6AddComment() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::3\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234,\n"
+ " \"comment\": \"a comment\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease for address 2001:db8:1::3, subnet-id 66 added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::3"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease have proper value set.
+ ASSERT_TRUE(l->getContext());
+ EXPECT_EQ("{ \"comment\": \"a comment\" }", l->getContext()->str());
+}
+
+void Lease6CmdsTest::testLease6GetByAddrNotFound() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"ip-address\": \"2001:db8:1::10\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByClientIdInvalidType() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // client-id query is allowed in v4 only.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"identifier-type\": \"client-id\","
+ " \"identifier\": \"01:02:03:04\","
+ " \"subnet-id\": 44"
+ " }\n"
+ "}";
+ string exp_rsp = "Query by client-id is not allowed in v6.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByDuidNotFound() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"00:01:02:03:04:05:06:07\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByAddr() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"2001:db8:1::1\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease found.";
+
+ // The status expected is success. The lease should be returned.
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Now check that the lease was indeed returned.
+ checkLease6(lease, "2001:db8:1::1", 0, 66, "42:42:42:42:42:42:42:42", false);
+}
+
+void Lease6CmdsTest::testLease6GetMissingParams() {
+ // No parameters whatsoever. You want just a lease, any lease?
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "Mandatory 'subnet-id' parameter missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just the subnet-id won't cut it, either.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We can't identify your laptop by color. Sorry, buddy.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"color\",\n"
+ " \"identifier\": \"blue\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Incorrect identifier type: color, the only supported values are: "
+ "address, hw-address, duid";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Query by hw-address is not supported in v6. Sorry.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"hw-address\",\n"
+ " \"identifier\": \"01:01:01:01:01:01\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Query by hw-address is not allowed in v6.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier value is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"duid\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier-type is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier\": \"01:02:03:04:05\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByAddrBadParam() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv6 address specified: 192.0.2.1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByAddrPrefix() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // We need to get a prefix lease. We need to create it by hand.
+ // Let's start with regular address lease and make it a prefix lease.
+ Lease6Ptr l = createLease6("2001:db8:1::1", 66, 0x77);
+ l->addr_ = IOAddress("2001:db8:1234:ab::");
+ l->type_ = Lease::TYPE_PD;
+ l->prefixlen_ = 56;
+ lmptr_->addLease(l);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"type\": \"IA_PD\","
+ " \"ip-address\": \"2001:db8:1234:ab::\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease found.";
+
+ // The status expected is success. The lease should be returned.
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Now check that the lease was indeed returned.
+ checkLease6(lease, "2001:db8:1234:ab::", 56, 66, "77:77:77:77:77:77:77:77", false);
+}
+
+void Lease6CmdsTest::testLease6GetByDuid() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"iaid\": 42,"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"42:42:42:42:42:42:42:42\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease found.";
+
+ // The status expected is success. The lease should be returned.
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr lease = rsp->get("arguments");
+ ASSERT_TRUE(lease);
+
+ // Now check that the lease was indeed returned.
+ checkLease6(lease, "2001:db8:1::1", 0, 66, "42:42:42:42:42:42:42:42", false);
+}
+
+void Lease6CmdsTest::testLease6GetAll() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for all leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\"\n"
+ "}";
+ string exp_rsp = "4 IPv6 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ // Let's check if the response contains desired leases.
+ checkLease6(leases, "2001:db8:1::1", 0, 66, "42:42:42:42:42:42:42:42", false);
+ checkLease6(leases, "2001:db8:1::2", 0, 66, "56:56:56:56:56:56:56:56", false);
+ checkLease6(leases, "2001:db8:2::1", 0, 99, "42:42:42:42:42:42:42:42", false);
+ checkLease6(leases, "2001:db8:2::2", 0, 99, "56:56:56:56:56:56:56:56", false);
+}
+
+void Lease6CmdsTest::testLease6GetAllNoLeases() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Query for all leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\"\n"
+ "}";
+ string exp_rsp = "0 IPv6 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ EXPECT_EQ(0, leases->size());
+}
+
+void Lease6CmdsTest::testLease6GetAllBySubnetId() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for leases from subnet 66. Subnet 127 will be ignored because
+ // it doesn't contain any leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\",\n"
+ " \"arguments\": {\n"
+ " \"subnets\": [ 66, 127 ]"
+ " }\n"
+ "}";
+ string exp_rsp = "2 IPv6 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ // Let's check if the response contains desired leases.
+ checkLease6(leases, "2001:db8:1::1", 0, 66, "42:42:42:42:42:42:42:42", false);
+ checkLease6(leases, "2001:db8:1::2", 0, 66, "56:56:56:56:56:56:56:56", false);
+}
+
+void Lease6CmdsTest::testLease6GetAllBySubnetIdNoLeases() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Query for leases from subnet 66. Subnet 127 will be ignored because
+ // it doesn't contain any leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\",\n"
+ " \"arguments\": {\n"
+ " \"subnets\": [ 66, 127 ]"
+ " }\n"
+ "}";
+ string exp_rsp = "0 IPv6 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ EXPECT_EQ(0, leases->size());
+}
+
+void Lease6CmdsTest::testLease6GetAllByMultipleSubnetIds() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for leases from subnet 66 and 99.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\",\n"
+ " \"arguments\": {\n"
+ " \"subnets\": [ 66, 99 ]"
+ " }\n"
+ "}";
+ string exp_rsp = "4 IPv6 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ // Let's check if the response contains desired leases.
+ checkLease6(leases, "2001:db8:1::1", 0, 66, "42:42:42:42:42:42:42:42", false);
+ checkLease6(leases, "2001:db8:1::2", 0, 66, "56:56:56:56:56:56:56:56", false);
+ checkLease6(leases, "2001:db8:2::1", 0, 99, "42:42:42:42:42:42:42:42", false);
+ checkLease6(leases, "2001:db8:2::2", 0, 99, "56:56:56:56:56:56:56:56", false);
+}
+
+void Lease6CmdsTest::testLease6GetBySubnetIdInvalidArguments() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Subnets not specified in arguments.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\",\n"
+ " \"arguments\": {"
+ " \"foo\": 1\n"
+ " }\n"
+ "}";
+ string exp_rsp = "'subnets' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Subnets are not a list.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\",\n"
+ " \"arguments\": {"
+ " \"subnets\": 1\n"
+ " }\n"
+ "}";
+ exp_rsp = "'subnets' parameter must be a list";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Subnets list must contain numbers.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get-all\",\n"
+ " \"arguments\": {"
+ " \"subnets\": [ \"x\", \"y\" ]\n"
+ " }\n"
+ "}";
+ exp_rsp = "listed subnet identifiers must be numbers";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetPaged() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Gather all returned addresses to verify that all were returned.
+ std::set<std::string> lease_addresses;
+
+ // Keyword start indicates that we want to retrieve the first page.
+ std::string last_address = "start";
+
+ // There are 4 leases in the database, so the first two pages should
+ // include leases and the 3 page should be empty.
+ for (auto i = 0; i < 3; ++i) {
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"" + last_address + "\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ // For the first two pages we should get success. For the last
+ // one an empty status code.
+ ConstElementPtr rsp;
+ if (i < 2) {
+ string exp_rsp = "2 IPv6 lease(s) found.";
+ rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ } else {
+ string exp_rsp = "0 IPv6 lease(s) found.";
+ rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ }
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+
+ // Arguments must exist.
+ ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ // For convenience, we return the number of returned leases,
+ // so as the client can check whether there was anything returned
+ // before parsing the leases structure.
+ ConstElementPtr page_count = args->get("count");
+ ASSERT_TRUE(page_count);
+ ASSERT_EQ(Element::integer, page_count->getType());
+
+ // leases must exist, but may be empty.
+ ConstElementPtr leases = args->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+
+ if (!leases->empty()) {
+ EXPECT_EQ(2, page_count->intValue());
+
+ // Go over each lease and verify its correctness.
+ for (ConstElementPtr lease : leases->listValue()) {
+ ASSERT_EQ(Element::map, lease->getType());
+ ASSERT_TRUE(lease->contains("ip-address"));
+ ConstElementPtr ip_address = lease->get("ip-address");
+ ASSERT_EQ(Element::string, ip_address->getType());
+ last_address = ip_address->stringValue();
+
+ lease_addresses.insert(last_address);
+
+ // The easiest way to retrieve the subnet id and HW address is to
+ // ask the Lease Manager.
+ Lease6Ptr from_mgr = LeaseMgrFactory::instance().getLease6(Lease::TYPE_NA,
+ IOAddress(last_address));
+ ASSERT_TRUE(from_mgr);
+ checkLease6(leases, last_address, 0, from_mgr->subnet_id_,
+ from_mgr->duid_->toText(), false);
+ }
+
+ } else {
+ // In the third iteration the page should be empty.
+ EXPECT_EQ(0, page_count->intValue());
+ }
+ }
+
+ // Check if all addresses were returned.
+ EXPECT_EQ(1, lease_addresses.count("2001:db8:1::1"));
+ EXPECT_EQ(1, lease_addresses.count("2001:db8:1::2"));
+ EXPECT_EQ(1, lease_addresses.count("2001:db8:2::1"));
+ EXPECT_EQ(1, lease_addresses.count("2001:db8:2::2"));
+}
+
+void Lease6CmdsTest::testLease6GetPagedZeroAddress() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"::\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ string exp_rsp = "2 IPv6 lease(s) found.";
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetPagedIPv4Address() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"192.0.2.3\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ string exp_rsp = "'from' parameter value 192.0.2.3 is not an IPv6 address";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetPagedInvalidFrom() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"foo\","
+ " \"limit\": 2"
+ " }"
+ "}";
+
+ string exp_rsp = "'from' parameter value is neither 'start' keyword "
+ "nor a valid IPv6 address";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetPagedNoLimit() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"start\""
+ " }"
+ "}";
+
+ string exp_rsp = "'limit' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetPagedLimitNotNumber() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"start\","
+ " \"limit\": false"
+ " }"
+ "}";
+
+ string exp_rsp = "'limit' parameter must be a number";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetPagedLimitIsZero() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Query for a page of leases.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-page\",\n"
+ " \"arguments\": {"
+ " \"from\": \"start\","
+ " \"limit\": 0"
+ " }"
+ "}";
+
+ string exp_rsp = "page size of retrieved leases must not be 0";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByDuidParams() {
+ // No parameters whatsoever.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-duid\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "'duid' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // duid must be a string.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-duid\",\n"
+ " \"arguments\": {"
+ " \"duid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "'duid' parameter must be a string";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Simply bad value.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-duid\",\n"
+ " \"arguments\": {"
+ " \"duid\": \"00::01:00:bc:0d:67\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "two consecutive separators (':') specified in a decoded string";
+ exp_rsp += " '00::01:00:bc:0d:67'";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByDuidFind0() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-duid\",\n"
+ " \"arguments\": {"
+ " \"duid\": \"00:01:02:03:04:05:06:07\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "0 IPv6 lease(s) found.";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByDuidFind2() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Get the lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-duid\",\n"
+ " \"arguments\": {"
+ " \"duid\": \"42:42:42:42:42:42:42:42\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "2 IPv6 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr map = rsp->get("arguments");
+ ASSERT_TRUE(map);
+ ASSERT_EQ(Element::map, map->getType());
+ ConstElementPtr leases = map->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+ ASSERT_EQ(2, leases->size());
+
+ // Let's check if the response makes any sense.
+ ConstElementPtr lease = leases->get(0);
+ ASSERT_TRUE(lease);
+ checkLease6(lease, "2001:db8:1::1", 0, 66, "42:42:42:42:42:42:42:42", false);
+ lease = leases->get(1);
+ ASSERT_TRUE(lease);
+ checkLease6(lease, "2001:db8:2::1", 0, 99, "42:42:42:42:42:42:42:42", false);
+}
+
+void Lease6CmdsTest::testLease6GetByHostnameParams() {
+ // No parameters whatsoever.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "'hostname' parameter not specified";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // hostname must be a string.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "'hostname' parameter must be a string";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // hostname must be not empty.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": \"\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "'hostname' parameter is empty";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByHostnameFind0() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // No such lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": \"foo.bar\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "0 IPv6 lease(s) found.";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6GetByHostnameFind2() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Get the lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-get-by-hostname\",\n"
+ " \"arguments\": {"
+ " \"hostname\": \"Myhost.Example.Com.\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "4 IPv6 lease(s) found.";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // Now check that the lease parameters were indeed returned.
+ ASSERT_TRUE(rsp);
+ ConstElementPtr map = rsp->get("arguments");
+ ASSERT_TRUE(map);
+ ASSERT_EQ(Element::map, map->getType());
+ ConstElementPtr leases = map->get("leases");
+ ASSERT_TRUE(leases);
+ ASSERT_EQ(Element::list, leases->getType());
+ ASSERT_EQ(4, leases->size());
+
+ // Let's check if the response makes any sense.
+ ConstElementPtr lease = leases->get(0);
+ ASSERT_TRUE(lease);
+ checkLease6(lease, "2001:db8:1::1", 0, 66, "42:42:42:42:42:42:42:42", false);
+ lease = leases->get(2);
+ ASSERT_TRUE(lease);
+ checkLease6(lease, "2001:db8:2::1", 0, 99, "42:42:42:42:42:42:42:42", false);
+}
+
+void Lease6CmdsTest::testLease6UpdateMissingParams() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Everything missing. What sort of crap is that?
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just ip is not enough (subnet-id and duid missing, although
+ // kea should be able to figure out the subnet-id on its own.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'duid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Better, but still no luck. (duid missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'duid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // ip-address and identifier-type missing.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just subnet-id, duid and iaid is not enough (ip missing).
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'ip-address' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Close, but no cigars. Still missing iaid.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"ip-address\": \"2001:db8:1::1\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "missing parameter 'iaid' (<string>:3:19)";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6UpdateBadParams() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // All params are there, but there's no subnet-id 123 configured.
+ // (initLeaseMgr initialized subnet-id 44 for v4 and subnet-id 66 for v6).
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv6 subnet with subnet-id=123 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This time the new IP address does not belong to the subnet.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"3000::1\",\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "The address 3000::1 does not belong to subnet 2001:db8:1::/48, subnet-id=66";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Nope, can't do v4 address in v6 lease.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"192.0.2.1\",\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Non-IPv6 address specified: 192.0.2.1";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Bad user context: not a map.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": \"bad value\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid user context '\"bad value\"' is not a JSON map.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Duplicated comment.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234\n,"
+ " \"user-context\": { \"comment\": \"in user context\" },\n"
+ " \"comment\": \"direct\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Duplicated comment entry '\"direct\"' in user context "
+ "'{ \"comment\": \"in user context\" }'";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Invalid declined state (1) for PD prefix.
+ txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"prefix-len\": 48,\n"
+ " \"type\": \"IA_PD\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234,\n"
+ " \"state\": 1"
+ " }\n"
+ "}";
+ exp_rsp = "Invalid declined state for PD prefix.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6Update() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease has been updated.
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease6CmdsTest::testLease6UpdateDeclinedLeases() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true, true);
+
+ checkLease6Stats(66, 2, 2, 0);
+
+ checkLease6Stats(99, 2, 2, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 2, 1, 0);
+
+ checkLease6Stats(99, 2, 2, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease has been updated.
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease6CmdsTest::testLease6UpdateNoSubnetId() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the subnet-id is correct.
+ EXPECT_EQ(66, l->subnet_id_);
+
+ // Make sure the lease has been updated.
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease6CmdsTest::testLease6UpdateNoSubnetIdDeclinedLeases() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true, true);
+
+ checkLease6Stats(66, 2, 2, 0);
+
+ checkLease6Stats(99, 2, 2, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 2, 1, 0);
+
+ checkLease6Stats(99, 2, 2, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the subnet-id is correct.
+ EXPECT_EQ(66, l->subnet_id_);
+
+ // Make sure the lease has been updated.
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease6CmdsTest::testLease6UpdateComment() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 42,\n"
+ " \"duid\": \"42:42:42:42:42:42:42:42\",\n"
+ " \"comment\": \"a comment\",\n"
+ " \"user-context\": { \"foobar\": true }\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease updated.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease has been updated.
+ ASSERT_TRUE(l->duid_);
+
+ // Check user context.
+ ConstElementPtr ctx = l->getContext();
+ ASSERT_TRUE(ctx);
+ EXPECT_EQ(2, ctx->size());
+ ASSERT_TRUE(ctx->contains("comment"));
+ EXPECT_EQ("\"a comment\"", ctx->get("comment")->str());
+ ASSERT_TRUE(ctx->contains("foobar"));
+ EXPECT_EQ("true", ctx->get("foobar")->str());
+}
+
+void Lease6CmdsTest::testLease6UpdateNoLease() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\""
+ " }\n"
+ "}";
+ string exp_rsp = "failed to update the lease with address 2001:db8:1::1 "
+ "either because the lease has been deleted or it has changed in the "
+ "database, in both cases a retry might succeed";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+}
+
+void Lease6CmdsTest::testLease6UpdateForceCreate() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\","
+ " \"force-create\": true"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the lease is correct.
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease6CmdsTest::testLease6UpdateForceCreateNoSubnetId() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\","
+ " \"force-create\": true"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease added.";
+ testCommand(txt, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now check that the lease is really there.
+ Lease6Ptr l = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(l);
+
+ // Make sure the subnet-id is figured out correctly.
+ EXPECT_EQ(66, l->subnet_id_);
+
+ // Make sure the lease is correct.
+ ASSERT_TRUE(l->duid_);
+ EXPECT_EQ("88:88:88:88:88:88:88:88", l->duid_->toText());
+ EXPECT_EQ("newhostname.example.org", l->hostname_);
+ EXPECT_EQ(7654321, l->iaid_);
+ EXPECT_FALSE(l->getContext());
+}
+
+void Lease6CmdsTest::testLease6UpdateDoNotForceCreate() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"iaid\": 7654321,\n"
+ " \"duid\": \"88:88:88:88:88:88:88:88\",\n"
+ " \"hostname\": \"newhostname.example.org\","
+ " \"force-create\": false"
+ " }\n"
+ "}";
+ string exp_rsp = "failed to update the lease with address 2001:db8:1::1 "
+ "either because the lease has been deleted or it has changed in the "
+ "database, in both cases a retry might succeed";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+}
+
+void Lease6CmdsTest::testLease6DelMissingParams() {
+ // No parameters whatsoever. You want just a lease, any lease?
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " }\n"
+ "}";
+ string exp_rsp = "Mandatory 'subnet-id' parameter missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Just the subnet-id won't cut it, either.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // We can't identify your laptop by color. Sorry, buddy.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"color\",\n"
+ " \"identifier\": \"blue\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Incorrect identifier type: color, the only supported values are: "
+ "address, hw-address, duid";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Query by hw-address is not supported in v6. Sorry.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"hw-address\",\n"
+ " \"identifier\": \"01:01:01:01:01:01\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "Delete by hw-address is not allowed in v6.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier value is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier-type\": \"hw-address\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Identifier-type is missing.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 123,\n"
+ " \"identifier\": \"01:02:03:04:05\"\n"
+ " }\n"
+ "}";
+ exp_rsp = "No 'ip-address' provided and 'identifier-type' is either missing or not a string.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6DelByAddrNotFound() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"ip-address\": \"2001:db8:1::10\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+}
+
+void Lease6CmdsTest::testLease6DelByDuidNotFound() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 1,\n"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"00:01:02:03:04:05:06:07\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease not found.";
+
+ // Note the status expected is empty. The query completed correctly,
+ // just didn't found the lease.
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Make sure the lease is still there.
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+}
+
+void Lease6CmdsTest::testLease6DelByAddr() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease deleted.";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+}
+
+void Lease6CmdsTest::testLease6DelByAddrDeclinedLeases() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true, true);
+
+ checkLease6Stats(66, 2, 2, 0);
+
+ checkLease6Stats(99, 2, 2, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease deleted.";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 1, 0);
+
+ checkLease6Stats(99, 2, 2, 0);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+}
+
+void Lease6CmdsTest::testLease6DelByAddrBadParam() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Invalid family
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.2.1\""
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid IPv6 address specified: 192.0.2.1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // This is way off
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"221B Baker St.\""
+ " }\n"
+ "}";
+ exp_rsp = "Failed to convert string to address '221B Baker St.': Invalid argument";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6DelByAddrPrefix() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Let's start with regular address lease and make it a prefix lease.
+ Lease6Ptr l = createLease6("2001:db8:1::1", 66, 0x77);
+ l->addr_ = IOAddress("2001:db8:1234:ab::");
+ l->type_ = Lease::TYPE_PD;
+ l->prefixlen_ = 56;
+ lmptr_->addLease(l);
+
+ StatsMgr::instance().setValue(StatsMgr::generateName("subnet", 66,
+ "assigned-pds" ),
+ int64_t(1));
+
+ checkLease6Stats(66, 0, 0, 1);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"type\": \"IA_PD\","
+ " \"ip-address\": \"2001:db8:1234:ab::\""
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease deleted.";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_PD, IOAddress("2001:db8:1234:ab::")));
+}
+
+void Lease6CmdsTest::testLease6DelByDuid() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"iaid\": 42,"
+ " \"identifier-type\": \"duid\","
+ " \"identifier\": \"42:42:42:42:42:42:42:42\"\n"
+ " }\n"
+ "}";
+ string exp_rsp = "IPv6 lease deleted.";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Make sure the lease is really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+}
+
+void Lease6CmdsTest::testLease6Wipe() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 2 IPv6 lease(s) from subnet(s) 66";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Make sure the leases in subnet 66 are really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::2")));
+
+ // Make sure the leases from subnet 99 are still there.
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::1")));
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::2")));
+}
+
+void Lease6CmdsTest::testLease6WipeAll() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 0\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 4 IPv6 lease(s) from subnet(s) 66 99";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Make sure the leases in subnet 66 are really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::2")));
+
+ // Make sure the leases from subnet 99 are gone, too.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::1")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::2")));
+}
+
+void Lease6CmdsTest::testLease6WipeAllNoArgs() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\"\n"
+ "}";
+ string exp_rsp = "Deleted 4 IPv6 lease(s) from subnet(s) 66 99";
+
+ // The status expected is success. The lease should be deleted.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Make sure the leases in subnet 66 are really gone.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::2")));
+
+ // Make sure the leases from subnet 99 are gone, too.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::1")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::2")));
+}
+
+void Lease6CmdsTest::testLease6WipeNoLeases() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 0 IPv6 lease(s) from subnet(s) 66";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+}
+
+void Lease6CmdsTest::testLease6WipeNoLeasesAll() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-wipe\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 0"
+ " }\n"
+ "}";
+ string exp_rsp = "Deleted 0 IPv6 lease(s) from subnet(s) 66 99";
+ testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+}
+
+void Lease6CmdsTest::testLease6BrokenUpdate() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Set the sanity checks level.
+ CfgMgr::instance().getCurrentCfg()->getConsistency()
+ ->setLeaseSanityCheck(CfgConsistency::LEASE_CHECK_FIX);
+
+ // Now send the command.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 444,\n"
+ " \"ip-address\": \"2001:db8:1::23\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"force-create\": true\n"
+ " }\n"
+ "}";
+ string exp_rsp = "Invalid subnet-id: No IPv6 subnet with "
+ "subnet-id=444 currently configured.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6BulkApply() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"deleted-leases\": ["
+ " {"
+ " \"ip-address\": \"2001:db8:1::1\","
+ " \"type\": \"IA_NA\""
+ " },"
+ " {"
+ " \"ip-address\": \"2001:db8:1::2\","
+ " \"type\": \"IA_NA\""
+ " }"
+ " ],"
+ " \"leases\": ["
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::123\",\n"
+ " \"duid\": \"11:11:11:11:11:11\",\n"
+ " \"iaid\": 1234\n"
+ " },"
+ " {"
+ " \"subnet-id\": 99,\n"
+ " \"ip-address\": \"2001:db8:2::123\",\n"
+ " \"duid\": \"22:22:22:22:22:22\",\n"
+ " \"iaid\": 1234\n"
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Bulk apply of 4 IPv6 leases completed.";
+
+ // The status expected is success.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 3, 0, 0);
+
+ // Check that the leases we inserted are stored.
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::123")));
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::123")));
+
+ // Check that the leases we deleted are gone,
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::2")));
+}
+
+void Lease6CmdsTest::testLease6BulkApplyAddsOnlyBadParam() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Now send the command. The command uses an invalid state declined (1) for
+ // PD prefix.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"leases\": ["
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::123\",\n"
+ " \"prefix-len\": 48,\n"
+ " \"type\": \"IA_PD\",\n"
+ " \"duid\": \"11:11:11:11:11:11\",\n"
+ " \"iaid\": 1234,\n"
+ " \"state\": 1"
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Invalid declined state for PD prefix.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Check that the lease was not inserted.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::123")));
+}
+
+void Lease6CmdsTest::testLease6BulkApplyAddsOnly() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"leases\": ["
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::123\",\n"
+ " \"duid\": \"11:11:11:11:11:11\",\n"
+ " \"iaid\": 1234\n"
+ " },"
+ " {"
+ " \"subnet-id\": 99,\n"
+ " \"ip-address\": \"2001:db8:2::123\",\n"
+ " \"duid\": \"22:22:22:22:22:22\",\n"
+ " \"iaid\": 1234\n"
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Bulk apply of 2 IPv6 leases completed.";
+
+ // The status expected is success.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 1, 0, 0);
+
+ checkLease6Stats(99, 1, 0, 0);
+
+ // Check that the leases we inserted are stored.
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::123")));
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::123")));
+}
+
+void Lease6CmdsTest::testLease6BulkApplyUpdatesOnlyBadParam() {
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Let's start with regular address lease and make it a prefix lease.
+ Lease6Ptr l = createLease6("2001:db8:1::1", 66, 0x77);
+ l->addr_ = IOAddress("2001:db8:1234:ab::");
+ l->type_ = Lease::TYPE_PD;
+ l->prefixlen_ = 56;
+ lmptr_->addLease(l);
+
+ // Now send the command. The command uses an invalid state declined (1) for
+ // PD prefix.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"leases\": ["
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1234:ab::\",\n"
+ " \"prefix-len\": 56,\n"
+ " \"type\": \"IA_PD\",\n"
+ " \"duid\": \"77:77:77:77:77:77\",\n"
+ " \"iaid\": 1234,\n"
+ " \"state\": 1"
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Invalid declined state for PD prefix.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Check that the lease we inserted is stored.
+ Lease6Ptr lease1 = lmptr_->getLease6(Lease::TYPE_PD, IOAddress("2001:db8:1234:ab::"));
+ ASSERT_TRUE(lease1);
+
+ // The IAID should have not been updated for the existing lease.
+ EXPECT_EQ(42, lease1->iaid_);
+}
+
+void Lease6CmdsTest::testLease6BulkApplyUpdatesOnly() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"leases\": ["
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"11:11:11:11:11:11\",\n"
+ " \"iaid\": 1234\n"
+ " },"
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::2\",\n"
+ " \"duid\": \"22:22:22:22:22:22\",\n"
+ " \"iaid\": 1234\n"
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Bulk apply of 2 IPv6 leases completed.";
+
+ // The status expected is success.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Check that the leases we inserted are stored.
+ Lease6Ptr lease1 = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ Lease6Ptr lease2 = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::2"));
+ ASSERT_TRUE(lease1);
+ ASSERT_TRUE(lease2);
+
+ // The IAIDs should have been updated for the existing leases.
+ EXPECT_EQ(1234, lease1->iaid_);
+ EXPECT_EQ(1234, lease2->iaid_);
+}
+
+void Lease6CmdsTest::testLease6BulkApplyDeletesOnly() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"deleted-leases\": ["
+ " {"
+ " \"ip-address\": \"2001:db8:1::1\","
+ " \"type\": \"IA_NA\""
+ " },"
+ " {"
+ " \"ip-address\": \"2001:db8:1::2\","
+ " \"type\": \"IA_NA\""
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Bulk apply of 2 IPv6 leases completed.";
+
+ // The status expected is success.
+ testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 0, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Check that the leases we deleted are gone,
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::2")));
+}
+
+void Lease6CmdsTest::testLease6BulkApplyDeleteNonExiting() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"deleted-leases\": ["
+ " {"
+ " \"ip-address\": \"2001:db8:1::123\","
+ " \"type\": \"IA_NA\""
+ " },"
+ " {"
+ " \"ip-address\": \"2001:db8:1::234\","
+ " \"type\": \"IA_NA\""
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Bulk apply of 0 IPv6 leases completed.";
+
+ // The status expected is success.
+ auto resp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+ ASSERT_TRUE(resp);
+ ASSERT_EQ(Element::map, resp->getType());
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ auto args = resp->get("arguments");
+ ASSERT_TRUE(args);
+ ASSERT_EQ(Element::map, args->getType());
+
+ auto failed_deleted_leases = args->get("failed-deleted-leases");
+ ASSERT_TRUE(failed_deleted_leases);
+ ASSERT_EQ(Element::list, failed_deleted_leases->getType());
+ ASSERT_EQ(2, failed_deleted_leases->size());
+
+ {
+ SCOPED_TRACE("lease address 2001:db8:1::123");
+ checkFailedLease(failed_deleted_leases, "IA_NA", "2001:db8:1::123",
+ CONTROL_RESULT_EMPTY, "lease not found");
+ }
+
+ {
+ SCOPED_TRACE("lease address 2001:db8:1::234");
+ checkFailedLease(failed_deleted_leases, "IA_NA", "2001:db8:1::234",
+ CONTROL_RESULT_EMPTY, "lease not found");
+ }
+}
+
+void Lease6CmdsTest::testLease6BulkApplyRollback() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"deleted-leases\": ["
+ " {"
+ " \"ip-address\": \"2001:db8:1::1\","
+ " \"type\": \"IA_NA\""
+ " },"
+ " {"
+ " \"ip-address\": \"2001:db8:1::2\","
+ " \"type\": \"IA_NA\""
+ " }"
+ " ],"
+ " \"leases\": ["
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::123\","
+ " \"duid\": \"11:11:11:11:11:11\","
+ " \"iaid\": 1234\n"
+ " },"
+ " {"
+ " \"subnet-id\": -1,"
+ " \"ip-address\": \"2001:db8:2::123\","
+ " \"duid\": \"22:22:22:22:22:22\","
+ " \"iaid\": 1234"
+ " }"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "out of range value (-1) specified for parameter 'subnet-id' (<string>:5:57)";
+
+ // The status expected is success.
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+
+ checkLease6Stats(99, 2, 0, 0);
+
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::123")));
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:2::123")));
+
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1")));
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::2")));
+}
+
+void Lease6CmdsTest::testLease6ResendDdnsBadParam() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Missing address parameter.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " }\n"
+ "}\n";
+
+ string exp_rsp = "'ip-address' parameter is missing.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Invalid address family.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"192.0.2.1\"\n"
+ " }\n"
+ "}\n";
+
+ exp_rsp = "Invalid IPv6 address specified: 192.0.2.1";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // ip-address is not an address at all.
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"221B Baker St.\"\n"
+ " }\n"
+ "}\n";
+
+ exp_rsp = "'221B Baker St.' is not a valid IP address.";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6ResendDdnsDisabled() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Disable DDNS updating.
+ disableD2();
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001::dead:beef\"\n"
+ " }\n"
+ "}";
+
+ string exp_rsp = "DDNS updating is not enabled";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+ // With D2 disabled there is no queue, size should come back as -1.
+ EXPECT_EQ(ncrQueueSize(), -1);
+}
+
+void Lease6CmdsTest::testLease6ResendDdnsNoLease() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Invalid
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"2001::dead:beef\"\n"
+ " }\n"
+ "}\n";
+ string exp_rsp = "No lease found for: 2001::dead:beef";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_EMPTY, exp_rsp);
+}
+
+void Lease6CmdsTest::testLease6ResendNoHostname() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // NCR sender queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+
+ // Fetch the lease so we can replace the hostname with "".
+ Lease6Ptr lease = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(lease);
+ lease->hostname_ = "";
+ lmptr_->updateLease6(lease);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\""
+ " }\n"
+ "}";
+
+ string exp_rsp = "Lease for: 2001:db8:1::1, has no hostname, nothing to update";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // There should not any NCRs queued.
+ EXPECT_EQ(ncrQueueSize(), 0);
+}
+
+void Lease6CmdsTest::testLease6ResendNoDirectionsEnabled() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // NCR sender queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+
+ // Fetch the lease so we can replace the hostname with "".
+ Lease6Ptr lease = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(lease);
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = false;
+ lmptr_->updateLease6(lease);
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\""
+ " }\n"
+ "}";
+
+ string exp_rsp = "Neither forward nor reverse updates enabled for lease for: 2001:db8:1::1";
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // There should not any NCRs queued.
+ EXPECT_EQ(ncrQueueSize(), 0);
+}
+
+void Lease6CmdsTest::testLease6ResendDdnsEnabled() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Structure detailing a test scenario.
+ struct Scenario {
+ std::string description_;
+ bool fqdn_fwd_;
+ bool fqdn_rev_;
+ };
+
+ // Three test scenarios to verify each combination of true flags.
+ std::vector<Scenario> scenarios = {
+ { "fwd_only", true, false },
+ { "rev_only", false, true},
+ { "both", true, true},
+ };
+
+ // Query for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-resend-ddns\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\""
+ " }\n"
+ "}";
+
+ // Expected response string.
+ string exp_rsp = "NCR generated for: 2001:db8:1::1, hostname: myhost.example.com.";
+
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+
+ // Fetch the lease so we can update the DDNS direction flags.
+ Lease6Ptr lease = lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::1"));
+ ASSERT_TRUE(lease);
+ lease->fqdn_rev_ = scenario.fqdn_rev_;
+ lease->fqdn_fwd_ = scenario.fqdn_fwd_;
+ lmptr_->updateLease6(lease);
+
+ // Queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+ ConstElementPtr rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ // We should have one entry in the queue.
+ ASSERT_EQ(ncrQueueSize(), 1);
+ verifyNameChangeRequest(CHG_ADD, scenario.fqdn_rev_, scenario.fqdn_fwd_,
+ "2001:db8:1::1", "myhost.example.com.");
+ }
+}
+
+void Lease6CmdsTest::testLease6DnsRemoveD2Enabled() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Structure detailing a test scenario.
+ struct Scenario {
+ std::string description_;
+ std::string hostname_;
+ bool fqdn_fwd_;
+ bool fqdn_rev_;
+ std::string update_ddns_;
+ bool exp_ncr_;
+ };
+
+ bool fwd = true;
+ bool rev = true;
+ bool ncr = true;
+
+ // Three test scenarios to verify each combination of true flags.
+ std::vector<Scenario> scenarios = {
+ {
+ "no_host",
+ "",
+ fwd, rev,
+ "\"update-ddns\": true",
+ !ncr
+ },
+ {
+ "no directions",
+ "myhost.example.com.",
+ !fwd, !rev,
+ "\"update-ddns\": true",
+ !ncr
+ },
+ {
+ "fwd_only",
+ "myhost.example.com.",
+ fwd, !rev,
+ "\"update-ddns\": true",
+ ncr
+ },
+ {
+ "rev_only",
+ "myhost.example.com.",
+ !fwd, rev,
+ "\"update-ddns\": true",
+ ncr
+ },
+ {
+ "both directions",
+ "myhost.example.com.",
+ fwd, rev,
+ "\"update-ddns\": true",
+ ncr
+ },
+ {
+ "default update-ddns",
+ "myhost.example.com.",
+ fwd, rev,
+ "",
+ !ncr
+ },
+ {
+ "update-ddns = false",
+ "myhost.example.com.",
+ fwd, rev,
+ "\"update-ddns\": false",
+ !ncr
+ },
+ };
+
+ for (auto scenario : scenarios) {
+ SCOPED_TRACE(scenario.description_);
+
+ // Let's create a lease with scenario attributes.
+ Lease6Ptr lease = createLease6("2001:db8:1::8", 66, 0x77);
+ lease->hostname_ = scenario.hostname_;
+ lease->fqdn_rev_ = scenario.fqdn_rev_;
+ lease->fqdn_fwd_ = scenario.fqdn_fwd_;
+ ASSERT_TRUE(lmptr_->addLease(lease));
+
+ // NCR Queue should be empty.
+ ASSERT_EQ(ncrQueueSize(), 0);
+
+ // Build the command
+ std::stringstream cmd;
+ cmd <<
+ "{"
+ " \"command\": \"lease6-del\","
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::8\"\n";
+
+ if (!scenario.update_ddns_.empty()) {
+ cmd << "," << scenario.update_ddns_;
+ }
+
+ cmd << "}}";
+
+ // Execute the delete command.
+ static_cast<void>(testCommand(cmd.str(), CONTROL_RESULT_SUCCESS, "IPv6 lease deleted."));
+
+ if (!scenario.exp_ncr_) {
+ // Should not have an ncr.
+ ASSERT_EQ(ncrQueueSize(), 0);
+ } else {
+ // We should have an ncr, verify it.
+ ASSERT_EQ(ncrQueueSize(), 1);
+ verifyNameChangeRequest(CHG_REMOVE, scenario.fqdn_rev_, scenario.fqdn_fwd_,
+ lease->addr_.toText(), lease->hostname_);
+ }
+
+ // Lease should have been deleted.
+ lease = lmptr_->getLease6(Lease::TYPE_NA, lease->addr_);
+ ASSERT_FALSE(lease);
+ }
+}
+
+void Lease6CmdsTest::testLease6DnsRemoveD2Disabled() {
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ disableD2();
+
+ // Delete for valid, existing lease.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {\n"
+ " \"ip-address\": \"2001:db8:1::8\",\n"
+ " \"update-ddns\": true\n"
+ " }\n"
+ "}";
+
+ // Let's create a lease with scenario attributes.
+ Lease6Ptr lease = createLease6("2001:db8:1::8", 66, 0x77);
+ lease->hostname_ = "myhost.example.com.";
+ lease->fqdn_rev_ = true;
+ lease->fqdn_fwd_ = true;
+ ASSERT_TRUE(lmptr_->addLease(lease));
+
+ // NCR Queue is not enabled.
+ ASSERT_EQ(ncrQueueSize(), -1);
+
+ // Execute the delete command.
+ static_cast<void>(testCommand(cmd, CONTROL_RESULT_SUCCESS, "IPv6 lease deleted."));
+
+ // NCR Queue is not enabled.
+ ASSERT_EQ(ncrQueueSize(), -1);
+
+ // Lease should have been deleted.
+ lease = lmptr_->getLease6(Lease::TYPE_NA, lease->addr_);
+ ASSERT_FALSE(lease);
+}
+
+void Lease6CmdsTest::testLease6ConflictingAdd() {
+ MultiThreadingTest mt(true);
+
+ // Initialize lease manager (true = v6, false = don't add leases)
+ initLeaseMgr(true, false);
+
+ // Make sure the lease doesn't exist.
+ IOAddress addr("2001:db8:1::1");
+ Lease6Ptr lease = lmptr_->getLease6(Lease::TYPE_NA, addr);
+ ASSERT_FALSE(lease);
+
+ // Verify stats show no leases.
+ checkLease6Stats(66, 0, 0, 0);
+
+ // Lock the address.
+ ResourceHandler resource_handler;
+ ASSERT_TRUE(resource_handler.tryLock(Lease::TYPE_NA, addr));
+
+ // Now let's try to add the lease.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-add\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"1a:1b:1c:1d:1e:1f\",\n"
+ " \"iaid\": 1234,\n"
+ " \"comment\": \"a comment\"\n"
+ " }\n"
+ "}";
+
+ string exp_rsp = "ResourceBusy: IP address:2001:db8:1::1 could not be added.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Lease should not have been added.
+ lease = lmptr_->getLease6(Lease::TYPE_NA, addr);
+ ASSERT_FALSE(lease);
+
+ // Stats should not have changed.
+ checkLease6Stats(66, 0, 0, 0);
+}
+
+void Lease6CmdsTest::testLease6ConflictingUpdate() {
+ MultiThreadingTest mt(true);
+
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, true);
+
+ // Verify lease stats show leases.
+ checkLease6Stats(66, 2, 0, 0);
+
+ // Make sure the lease exists.
+ IOAddress addr("2001:db8:1::1");
+ Lease6Ptr lease = lmptr_->getLease6(Lease::TYPE_NA, addr);
+ ASSERT_TRUE(lease);
+ // Save a copy of the original lease.
+ Lease6 original_lease = *lease;
+
+ // Lock the address.
+ ResourceHandler resource_handler;
+ ASSERT_TRUE(resource_handler.tryLock(Lease::TYPE_NA, addr));
+
+ // Now let's try to update the lease.
+ string txt =
+ "{\n"
+ " \"command\": \"lease6-update\",\n"
+ " \"arguments\": {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::1\",\n"
+ " \"duid\": \"2a:2b:2c:2d:2e:2f\",\n"
+ " \"iaid\": 1234,\n"
+ " \"comment\": \"a comment\"\n"
+ " }\n"
+ "}";
+
+ string exp_rsp = "ResourceBusy: IP address:2001:db8:1::1 could not be updated.";
+ testCommand(txt, CONTROL_RESULT_ERROR, exp_rsp);
+
+ // Fetch the lease again.
+ lease = lmptr_->getLease6(Lease::TYPE_NA, addr);
+ ASSERT_TRUE(lease);
+
+ // Lease should not have been changed.
+ EXPECT_EQ(original_lease, *lease);
+}
+
+void Lease6CmdsTest::testLease6ConflictingBulkApplyAdd() {
+ MultiThreadingTest mt(true);
+
+ // Initialize lease manager (true = v6, true = add leases)
+ initLeaseMgr(true, false);
+
+ checkLease6Stats(66, 0, 0, 0);
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Lock the address.
+ IOAddress locked_addr("2001:db8:2::77");
+ ResourceHandler resource_handler;
+ ASSERT_TRUE(resource_handler.tryLock(Lease::TYPE_NA, locked_addr));
+
+ // Now send the command.
+ string cmd =
+ "{\n"
+ " \"command\": \"lease6-bulk-apply\",\n"
+ " \"arguments\": {"
+ " \"leases\": ["
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::123\",\n"
+ " \"duid\": \"11:11:11:11:11:11\",\n"
+ " \"iaid\": 1234\n"
+ " },"
+ " {"
+ " \"subnet-id\": 99,\n"
+ " \"ip-address\": \"2001:db8:2::77\",\n"
+ " \"duid\": \"22:22:22:22:22:22\",\n"
+ " \"iaid\": 1234\n"
+ " },"
+ " {"
+ " \"subnet-id\": 66,\n"
+ " \"ip-address\": \"2001:db8:1::124\",\n"
+ " \"duid\": \"33:33:33:33:33:33\",\n"
+ " \"iaid\": 1234\n"
+ " },"
+ " ]"
+ " }"
+ "}";
+ string exp_rsp = "Bulk apply of 2 IPv6 leases completed.";
+
+ // The status expected is success.
+ auto rsp = testCommand(cmd, CONTROL_RESULT_SUCCESS, exp_rsp);
+
+ checkLease6Stats(66, 2, 0, 0);
+ checkLease6Stats(99, 0, 0, 0);
+
+ // Check that the leases we inserted are stored.
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::123")));
+ EXPECT_TRUE(lmptr_->getLease6(Lease::TYPE_NA, IOAddress("2001:db8:1::124")));
+
+ // Check that the lease for locked address was not added.
+ EXPECT_FALSE(lmptr_->getLease6(Lease::TYPE_NA, locked_addr));
+
+ auto args = rsp->get("arguments");
+ auto failed_leases = args->get("failed-leases");
+ ASSERT_TRUE(failed_leases);
+ ASSERT_EQ(Element::list, failed_leases->getType());
+ ASSERT_EQ(1, failed_leases->size());
+ checkFailedLease(failed_leases, "IA_NA", locked_addr.toText(),
+ CONTROL_RESULT_ERROR,
+ "ResourceBusy: IP address:2001:db8:2::77 could not be updated.");
+}
+
+TEST_F(Lease6CmdsTest, lease6AddMissingParams) {
+ testLease6AddMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddBadParams) {
+ testLease6AddBadParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddBadParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddBadParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6Add) {
+ testLease6Add();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6Add();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddDeclinedLeases) {
+ testLease6AddDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddExisting) {
+ testLease6AddExisting();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddExistingMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddExisting();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddSubnetIdMissing) {
+ testLease6AddSubnetIdMissing();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddSubnetIdMissingMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddSubnetIdMissing();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddSubnetIdMissingDeclinedLeases) {
+ testLease6AddSubnetIdMissingDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddSubnetIdMissingDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddSubnetIdMissingDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddSubnetIdMissingBadAddr) {
+ testLease6AddSubnetIdMissingBadAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddSubnetIdMissingBadAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddSubnetIdMissingBadAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddNegativeExpireTime) {
+ testLease6AddNegativeExpireTime();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddNegativeExpireTimeMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddNegativeExpireTime();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddNegativeCltt) {
+ testLease6AddNegativeCltt();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddNegativeClttMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddNegativeCltt();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddPrefix) {
+ testLease6AddPrefix();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddPrefixMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddPrefix();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddFullAddr) {
+ testLease6AddFullAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddFullAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddFullAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddComment) {
+ testLease6AddComment();
+}
+
+TEST_F(Lease6CmdsTest, lease6AddCommentMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6AddComment();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddrNotFound) {
+ testLease6GetByAddrNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddrNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByAddrNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByClientIdInvalidType) {
+ testLease6GetByClientIdInvalidType();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByClientIdInvalidTypeMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByClientIdInvalidType();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidNotFound) {
+ testLease6GetByDuidNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByDuidNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddr) {
+ testLease6GetByAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetMissingParams) {
+ testLease6GetMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddrBadParam) {
+ testLease6GetByAddrBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddrBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByAddrBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddrPrefix) {
+ testLease6GetByAddrPrefix();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByAddrPrefixMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByAddrPrefix();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuid) {
+ testLease6GetByDuid();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByDuid();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAll) {
+ testLease6GetAll();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetAll();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllNoLeases) {
+ testLease6GetAllNoLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllNoLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetAllNoLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllBySubnetId) {
+ testLease6GetAllBySubnetId();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllBySubnetIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetAllBySubnetId();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllBySubnetIdNoLeases) {
+ testLease6GetAllBySubnetIdNoLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllBySubnetIdNoLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetAllBySubnetIdNoLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllByMultipleSubnetIds) {
+ testLease6GetAllByMultipleSubnetIds();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetAllByMultipleSubnetIdsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetAllByMultipleSubnetIds();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetBySubnetIdInvalidArguments) {
+ testLease6GetBySubnetIdInvalidArguments();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetBySubnetIdInvalidArgumentsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetBySubnetIdInvalidArguments();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPaged) {
+ testLease6GetPaged();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetPaged();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedZeroAddress) {
+ testLease6GetPagedZeroAddress();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedZeroAddressMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetPagedZeroAddress();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedIPv4Address) {
+ testLease6GetPagedIPv4Address();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedIPv4AddressMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetPagedIPv4Address();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedInvalidFrom) {
+ testLease6GetPagedInvalidFrom();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedInvalidFromMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetPagedInvalidFrom();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedNoLimit) {
+ testLease6GetPagedNoLimit();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedNoLimitMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetPagedNoLimit();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedLimitNotNumber) {
+ testLease6GetPagedLimitNotNumber();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedLimitNotNumberMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetPagedLimitNotNumber();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedLimitIsZero) {
+ testLease6GetPagedLimitIsZero();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetPagedLimitIsZeroMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetPagedLimitIsZero();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidParams) {
+ testLease6GetByDuidParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByDuidParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidFind0) {
+ testLease6GetByDuidFind0();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidFind0MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByDuidFind0();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidFind2) {
+ testLease6GetByDuidFind2();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByDuidFind2MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByDuidFind2();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByHostnameParams) {
+ testLease6GetByHostnameParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByHostnameParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByHostnameParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByHostnameFind0) {
+ testLease6GetByHostnameFind0();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByHostnameFind0MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByHostnameFind0();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByHostnameFind2) {
+ testLease6GetByHostnameFind2();
+}
+
+TEST_F(Lease6CmdsTest, lease6GetByHostnameFind2MultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6GetByHostnameFind2();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateMissingParams) {
+ testLease6UpdateMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateBadParams) {
+ testLease6UpdateBadParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateBadParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateBadParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6Update) {
+ testLease6Update();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6Update();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateDeclinedLeases) {
+ testLease6UpdateDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateNoSubnetId) {
+ testLease6UpdateNoSubnetId();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateNoSubnetIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateNoSubnetId();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateNoSubnetIdDeclinedLeases) {
+ testLease6UpdateNoSubnetIdDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateNoSubnetIdDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateNoSubnetIdDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateComment) {
+ testLease6UpdateComment();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateCommentMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateComment();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateNoLease) {
+ testLease6UpdateNoLease();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateNoLeaseMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateNoLease();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateForceCreate) {
+ testLease6UpdateForceCreate();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateForceCreateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateForceCreate();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateForceCreateNoSubnetId) {
+ testLease6UpdateForceCreateNoSubnetId();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateForceCreateNoSubnetIdMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateForceCreateNoSubnetId();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateDoNotForceCreate) {
+ testLease6UpdateDoNotForceCreate();
+}
+
+TEST_F(Lease6CmdsTest, lease6UpdateDoNotForceCreateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6UpdateDoNotForceCreate();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelMissingParams) {
+ testLease6DelMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelMissingParamsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelMissingParams();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrNotFound) {
+ testLease6DelByAddrNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelByAddrNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByDuidNotFound) {
+ testLease6DelByDuidNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByDuidNotFoundMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelByDuidNotFound();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddr) {
+ testLease6DelByAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelByAddr();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrDeclinedLeases) {
+ testLease6DelByAddrDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrDeclinedLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelByAddrDeclinedLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrBadParam) {
+ testLease6DelByAddrBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelByAddrBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrPrefix) {
+ testLease6DelByAddrPrefix();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByAddrPrefixMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelByAddrPrefix();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByDuid) {
+ testLease6DelByDuid();
+}
+
+TEST_F(Lease6CmdsTest, lease6DelByDuidMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DelByDuid();
+}
+
+TEST_F(Lease6CmdsTest, lease6Wipe) {
+ testLease6Wipe();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6Wipe();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeAll) {
+ testLease6WipeAll();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeAllMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6WipeAll();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeAllNoArgs) {
+ testLease6WipeAllNoArgs();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeAllNoArgsMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6WipeAllNoArgs();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeNoLeases) {
+ testLease6WipeNoLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeNoLeasesMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6WipeNoLeases();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeNoLeasesAll) {
+ testLease6WipeNoLeasesAll();
+}
+
+TEST_F(Lease6CmdsTest, lease6WipeNoLeasesAllMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6WipeNoLeasesAll();
+}
+
+TEST_F(Lease6CmdsTest, lease6BrokenUpdate) {
+ testLease6BrokenUpdate();
+}
+
+TEST_F(Lease6CmdsTest, lease6BrokenUpdateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BrokenUpdate();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApply) {
+ testLease6BulkApply();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApply();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyAddsOnlyBadParam) {
+ testLease6BulkApplyAddsOnlyBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyAddsOnlyBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApplyAddsOnlyBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyAddsOnly) {
+ testLease6BulkApplyAddsOnly();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyAddsOnlyMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApplyAddsOnly();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyUpdatesOnlyBadParam) {
+ testLease6BulkApplyUpdatesOnlyBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyUpdatesOnlyBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApplyUpdatesOnlyBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyUpdatesOnly) {
+ testLease6BulkApplyUpdatesOnly();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyUpdatesOnlyMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApplyUpdatesOnly();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyDeletesOnly) {
+ testLease6BulkApplyDeletesOnly();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyDeletesOnlyMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApplyDeletesOnly();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyDeleteNonExiting) {
+ testLease6BulkApplyDeleteNonExiting();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyDeleteNonExitingMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApplyDeleteNonExiting();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyRollback) {
+ testLease6BulkApplyRollback();
+}
+
+TEST_F(Lease6CmdsTest, lease6BulkApplyRollbackMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6BulkApplyRollback();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsBadParam) {
+ testLease6ResendDdnsBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsBadParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6ResendDdnsBadParam();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsDisabled) {
+ testLease6ResendDdnsDisabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsDisabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6ResendDdnsDisabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsNoLease) {
+ testLease6ResendDdnsNoLease();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsNoLeaseMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6ResendDdnsNoLease();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendNoHostname) {
+ testLease6ResendNoHostname();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendNoHostnameMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6ResendNoHostname();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendNoDirectionsEnabled) {
+ testLease6ResendNoDirectionsEnabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendNoDirectionsEnabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6ResendNoDirectionsEnabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsEnabled) {
+ testLease6ResendDdnsEnabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6ResendDdnsEnabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6ResendDdnsEnabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6DnsRemoveD2Enabled) {
+ testLease6DnsRemoveD2Enabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6DnsRemoveD2EnabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DnsRemoveD2Enabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6DnsRemoveD2Disabled) {
+ testLease6DnsRemoveD2Disabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6DnsRemoveD2DisabledMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLease6DnsRemoveD2Disabled();
+}
+
+TEST_F(Lease6CmdsTest, lease6ConflictingAddMultiThreading) {
+ testLease6ConflictingAdd();
+}
+
+TEST_F(Lease6CmdsTest, lease6ConflictingUpdateMultiThreading) {
+ testLease6ConflictingUpdate();
+}
+
+TEST_F(Lease6CmdsTest, lease6ConflictingBulkApplyAddMultiThreading) {
+ testLease6ConflictingBulkApplyAdd();
+}
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc
new file mode 100644
index 0000000..d5e5c37
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.cc
@@ -0,0 +1,101 @@
+// Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
+#include <config/command_mgr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/ncr_generator.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/resource_handler.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <lease_cmds_unittest.h>
+#include <stats/stats_mgr.h>
+#include <testutils/user_context_utils.h>
+#include <testutils/multi_threading_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <set>
+
+using namespace std;
+using namespace isc;
+using namespace isc::hooks;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace isc::dhcp_ddns;
+using namespace isc::asiolink;
+using namespace isc::stats;
+using namespace isc::test;
+
+namespace {
+
+// Simple test that checks the library really registers the commands.
+TEST_F(LeaseCmdsTest, commands) {
+ vector<string> cmds = {
+ "lease4-add", "lease6-add",
+ "lease4-get", "lease6-get",
+ "lease4-get-all", "lease6-get-all",
+ "lease4-get-page", "lease6-get-page",
+ "lease4-get-by-hw-address",
+ "lease4-get-by-client-id", "lease6-get-by-duid",
+ "lease4-get-by-hostname", "lease6-get-by-hostname",
+ "lease4-del", "lease6-del",
+ "lease4-update", "lease6-update",
+ "lease4-wipe", "lease6-wipe",
+ "lease4-resend-ddns", "lease6-resend-ddns"
+ };
+ testCommands(cmds);
+}
+
+void LeaseCmdsTest::testLeaseXDelBadUpdateDdnsParam() {
+ string cmd =
+ "{\n"
+ " \"command\": \"lease4-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"192.0.1.0\","
+ " \"update-ddns\": 77"
+ " }\n"
+ "}";
+
+ string exp_rsp = "'update-ddns' is not a boolean";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+
+ cmd =
+ "{\n"
+ " \"command\": \"lease6-del\",\n"
+ " \"arguments\": {"
+ " \"ip-address\": \"2001:db8:1::1\","
+ " \"update-ddns\": \"bogus\""
+ " }\n"
+ "}";
+
+ exp_rsp = "'update-ddns' is not a boolean";
+ testCommand(cmd, CONTROL_RESULT_ERROR, exp_rsp);
+}
+
+
+// Check that the library can be loaded and unloaded multiple times.
+TEST_F(LeaseCmdsTest, multipleLoads) {
+ testMultipleLoads();
+}
+
+TEST_F(LeaseCmdsTest, leaseXDelBadUpdateDdnsParam) {
+ testLeaseXDelBadUpdateDdnsParam();
+}
+
+TEST_F(LeaseCmdsTest, leaseXDelBadUpdateDdnsParamMultiThreading) {
+ MultiThreadingTest mt(true);
+ testLeaseXDelBadUpdateDdnsParam();
+}
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h
new file mode 100644
index 0000000..638a902
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h
@@ -0,0 +1,554 @@
+// Copyright (C) 2017-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <hooks/hooks_manager.h>
+#include <config/command_mgr.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/ncr_generator.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/resource_handler.h>
+#include <cc/command_interpreter.h>
+#include <cc/data.h>
+#include <stats/stats_mgr.h>
+#include <testutils/user_context_utils.h>
+#include <testutils/multi_threading_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <errno.h>
+#include <set>
+
+namespace {
+
+/// @brief High valid lifetime used for leases in the tests below.
+constexpr uint32_t HIGH_VALID_LIFETIME = 0xFFFFFFFE;
+
+/// @brief December 11th 2030 date used in the unit tests for cltt.
+constexpr time_t DEC_2030_TIME = 1923222072;
+
+/// @brief Test fixture for testing loading and unloading the flex-id library
+class LibLoadTest : public ::testing::Test {
+public:
+ /// @brief Constructor
+ LibLoadTest(std::string lib_filename)
+ : lib_name_(lib_filename) {
+ isc::config::CommandMgr::instance();
+ unloadLibs();
+ }
+
+ /// @brief Destructor
+ /// Removes files that may be left over from previous tests
+ virtual ~LibLoadTest() {
+ unloadLibs();
+ }
+
+ /// @brief Adds library/parameters to list of libraries to be loaded
+ void addLib(const std::string& lib, isc::data::ConstElementPtr params) {
+ libraries_.push_back(make_pair(lib, params));
+ }
+
+ /// @brief Load all specified libraries.
+ ///
+ /// The libraries are stored in libraries
+ void loadLibs() {
+ ASSERT_TRUE(isc::hooks::HooksManager::loadLibraries(libraries_))
+ << "library loading failed";
+ }
+
+ /// @brief Unloads all libraries.
+ void unloadLibs() {
+ ASSERT_NO_THROW(isc::hooks::HooksManager::unloadLibraries());
+ }
+
+ /// @brief Checks whether specified command is registered
+ ///
+ /// @param name name of the command to be checked
+ /// @param expect_true true - must be registered, false - must not be
+ void checkCommandRegistered(const std::string& name, bool expect_true) {
+ // First get the list of registered commands
+ isc::data::ConstElementPtr lst = isc::data::Element::fromJSON("{ \"command\": \"list-commands\" }");
+ isc::data::ConstElementPtr rsp = isc::config::CommandMgr::instance().processCommand(lst);
+
+ ASSERT_TRUE(rsp);
+
+ isc::data::ConstElementPtr args = rsp->get("arguments");
+ ASSERT_TRUE(args);
+
+ std::string args_txt = args->str();
+
+ if (expect_true) {
+ EXPECT_TRUE(args_txt.find(name) != std::string::npos);
+ } else {
+ EXPECT_TRUE(args_txt.find(name) == std::string::npos);
+ }
+ }
+
+ /// @brief tests specified command and verifies response
+ ///
+ /// This method loads the library, sends specific command,
+ /// then checks if the result is as expected, checks if text response
+ /// is ok (optional, check skipped if exp_txt is empty) and then returns
+ /// the response (for possible additional checks).
+ ///
+ /// @param cmd JSON command to be sent (must be valid JSON)
+ /// @param exp_result 0 - success, 1 - error, 2 - ...
+ /// @param exp_txt expected text response (optional)
+ /// @return full response returned by the command execution.
+ isc::data::ConstElementPtr testCommand(std::string cmd_txt, int exp_result,
+ std::string exp_txt) {
+ // Let's load the library first.
+ loadLib();
+
+ isc::data::ConstElementPtr cmd;
+ EXPECT_NO_THROW(cmd = isc::data::Element::fromJSON(cmd_txt));
+ if (!cmd) {
+ ADD_FAILURE() << cmd_txt << " is not a valid JSON, test broken";
+ return (isc::data::ConstElementPtr());
+ }
+
+ // Process the command and verify response.
+ isc::data::ConstElementPtr rsp = isc::config::CommandMgr::instance().processCommand(cmd);
+ checkAnswer(rsp, exp_result, exp_txt);
+
+ return (rsp);
+ }
+
+ /// @brief Compares the status in the given parse result to a given value.
+ ///
+ /// @param answer Element set containing an integer response and string
+ /// comment.
+ /// @param exp_status is an integer against which to compare the status.
+ /// @param exp_txt is expected text (not checked if "")
+ void checkAnswer(isc::data::ConstElementPtr answer,
+ int exp_status,
+ std::string exp_txt = "") {
+ int rcode = 0;
+ isc::data::ConstElementPtr comment;
+ comment = isc::config::parseAnswer(rcode, answer);
+
+ if (rcode != exp_status) {
+ ADD_FAILURE() << "Expected status code " << exp_status
+ << " but received " << rcode << ", comment: "
+ << (comment ? comment->str() : "(none)");
+ }
+
+ // Ok, parseAnswer interface is weird. If there are no arguments,
+ // it returns content of text. But if there is an argument,
+ // it returns the argument and it's not possible to retrieve
+ // "text" (i.e. comment).
+ if (comment->getType() != isc::data::Element::string) {
+ comment = answer->get("text");
+ }
+
+ if (!exp_txt.empty()) {
+ EXPECT_EQ(exp_txt, comment->stringValue());
+ }
+ }
+
+ /// @brief Loads the library specified by lib_name_
+ void loadLib() {
+ if (libraries_.empty()) {
+ isc::data::ElementPtr params = isc::data::Element::createMap();
+ addLib(lib_name_, params);
+ }
+ EXPECT_NO_THROW(loadLibs());
+ }
+
+ /// @brief Test checks if specified commands are provided by the library.
+ ///
+ /// @param cms a vector of string with command names
+ void testCommands(const std::vector<std::string> cmds) {
+ // The commands should not be registered yet.
+ for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) {
+ checkCommandRegistered(*cmd, false);
+ }
+
+ loadLib();
+
+ // The commands should be available after library was loaded.
+ for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) {
+ checkCommandRegistered(*cmd, true);
+ }
+
+ unloadLibs();
+
+ // and the commands should be gone now.
+ for (auto cmd = cmds.begin(); cmd != cmds.end(); ++cmd) {
+ checkCommandRegistered(*cmd, false);
+ }
+
+ }
+
+ // Check that the library can be loaded and unloaded multiple times.
+ void testMultipleLoads() {
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+
+ EXPECT_NO_THROW(loadLib());
+ EXPECT_NO_THROW(unloadLibs());
+ }
+
+ /// @brief Verify that NameChangeRequest holds valid values.
+ ///
+ /// This function picks first NameChangeRequest from the internal server's
+ /// queue and checks that it holds valid parameters. The NameChangeRequest
+ /// is removed from the queue.
+ ///
+ /// @param type An expected type of the NameChangeRequest (Add or Remove).
+ /// @param reverse An expected setting of the reverse update flag.
+ /// @param forward An expected setting of the forward update flag.
+ /// @param addr A string representation of the IPv6 address held in the
+ /// NameChangeRequest.
+ /// @param fqdn The expected string value of the FQDN, if blank the
+ /// check is skipped
+ void verifyNameChangeRequest(const isc::dhcp_ddns::NameChangeType type,
+ const bool reverse, const bool forward,
+ const std::string& addr,
+ const std::string& fqdn = "") {
+ isc::dhcp_ddns::NameChangeRequestPtr ncr;
+ ASSERT_NO_THROW(ncr = isc::dhcp::CfgMgr::instance().getD2ClientMgr().peekAt(0));
+ ASSERT_TRUE(ncr);
+
+ EXPECT_EQ(type, ncr->getChangeType());
+ EXPECT_EQ(forward, ncr->isForwardChange());
+ EXPECT_EQ(reverse, ncr->isReverseChange());
+ EXPECT_EQ(addr, ncr->getIpAddress());
+
+ if (!fqdn.empty()) {
+ EXPECT_EQ(fqdn, ncr->getFqdn());
+ }
+
+ // Process the message off the queue
+ ASSERT_NO_THROW(isc::dhcp::CfgMgr::instance().getD2ClientMgr().runReadyIO());
+ }
+
+ /// List of libraries to be/being loaded (usually just one)
+ isc::hooks::HookLibsCollection libraries_;
+
+ /// Path to the library filename
+ std::string lib_name_;
+};
+
+/// @brief Class dedicated to testing lease_cmds library.
+///
+/// Provides convenience methods for loading, testing all commands and
+/// unloading the lease_cmds library.
+class LeaseCmdsTest : public LibLoadTest {
+public:
+
+ /// @brief Pointer to the lease manager
+ isc::dhcp::LeaseMgr* lmptr_;
+
+ /// @brief Reference to the D2 client manager.
+ isc::dhcp::D2ClientMgr& d2_mgr_;
+
+ /// @brief Constructor
+ ///
+ /// Sets the library filename and clears the lease manager pointer.
+ /// Also ensured there is no lease manager leftovers from previous
+ /// test.
+ LeaseCmdsTest()
+ : LibLoadTest(LEASE_CMDS_LIB_SO),
+ d2_mgr_(isc::dhcp::CfgMgr::instance().getD2ClientMgr()) {
+ isc::dhcp::LeaseMgrFactory::destroy();
+ enableD2();
+ lmptr_ = 0;
+ isc::stats::StatsMgr::instance().removeAll();
+ }
+
+ /// @brief Destructor
+ ///
+ /// Removes library (if any), destroys lease manager (if any).
+ virtual ~LeaseCmdsTest() {
+ // destroys lease manager first because the other order triggers
+ // a clang/boost bug
+ isc::dhcp::LeaseMgrFactory::destroy();
+ disableD2();
+ unloadLibs();
+ lmptr_ = 0;
+ isc::stats::StatsMgr::instance().removeAll();
+ }
+
+ /// @brief Creates an IPv4 lease
+ ///
+ /// Lease parameters: valid lifetime = 0xFFFFFFFE, cltt = 1923222072, fqdn-fwd = false,
+ /// fqdn-rev = true, hostname = myhost.example.com
+ ///
+ /// @param ip_address IP address for the lease.
+ /// @param subnet_id subnet identifier
+ /// @param hw_address_pattern value to be used for generating HW address by repeating
+ /// it 6 times.
+ /// @param client_id_pattern value to be used for generating client identifier by
+ /// repeating it 8 times.
+ /// @param declined controls whether the lease should be in declined state.
+ ///
+ /// @return Returns the lease created
+ isc::dhcp::Lease4Ptr createLease4(const std::string& ip_address,
+ const isc::dhcp::SubnetID& subnet_id,
+ const uint8_t hw_address_pattern,
+ const uint8_t client_id_pattern,
+ bool declined = false) {
+ isc::dhcp::Lease4Ptr lease(new isc::dhcp::Lease4());
+
+ lease->addr_ = isc::asiolink::IOAddress(ip_address);
+
+ // Set other parameters. For historical reasons, address 0 is not used.
+ lease->hwaddr_.reset(new isc::dhcp::HWAddr(std::vector<uint8_t>(6, hw_address_pattern), isc::dhcp::HTYPE_ETHER));
+ lease->client_id_ = isc::dhcp::ClientIdPtr(new isc::dhcp::ClientId(std::vector<uint8_t>(8, client_id_pattern)));
+ // Purposely using high cltt and valid lifetime to test that
+ // expiration time is cast properly.
+ lease->valid_lft_ = HIGH_VALID_LIFETIME; // Very high valid lifetime
+ lease->cltt_ = DEC_2030_TIME; // December 11th 2030
+ lease->updateCurrentExpirationTime();
+ if (declined) {
+ lease->state_ = isc::dhcp::Lease::STATE_DECLINED;
+ }
+ lease->subnet_id_ = subnet_id;
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ return (lease);
+ }
+
+ /// @brief Creates an IPv6 lease
+ ///
+ /// Lease parameters: cltt = 1923222072, fqdn-fwd = false, fqdn-rev = true,
+ /// hostname = myhost.example.com, preferred lifetime = 1800,
+ /// valid lifetime = 0xFFFFFFFE
+ ///
+ /// @param ip_address IP address for the lease.
+ /// @param subnet_id subnet identifier
+ /// @param duid_address_pattern value to be used for generating DUID by
+ /// repeating it 8 times
+ /// @param declined controls whether the lease should be in declined state.
+ ///
+ /// @return Returns the lease created
+ isc::dhcp::Lease6Ptr createLease6(const std::string& ip_address,
+ const isc::dhcp::SubnetID& subnet_id,
+ const uint8_t duid_pattern,
+ bool declined = false) {
+ isc::dhcp::Lease6Ptr lease(new isc::dhcp::Lease6());
+
+ lease->addr_ = isc::asiolink::IOAddress(ip_address);
+ lease->type_ = isc::dhcp::Lease::TYPE_NA;
+ lease->prefixlen_ = 128;
+ lease->iaid_ = 42;
+ lease->duid_ = isc::dhcp::DuidPtr(new isc::dhcp::DUID(std::vector<uint8_t>(8, duid_pattern)));
+ lease->preferred_lft_ = 1800;
+ // Purposely using high cltt and valid lifetime to test that
+ // expiration time is cast properly.
+ lease->valid_lft_ = HIGH_VALID_LIFETIME; // Very high valid lifetime
+ lease->cltt_ = DEC_2030_TIME; // December 11th 2030
+ lease->updateCurrentExpirationTime();
+ if (declined) {
+ lease->state_ = isc::dhcp::Lease::STATE_DECLINED;
+ }
+ lease->subnet_id_ = subnet_id;
+ lease->fqdn_fwd_ = false;
+ lease->fqdn_rev_ = true;
+ lease->hostname_ = "myhost.example.com.";
+
+ return (lease);
+ }
+
+ /// @brief Initializes lease manager (and optionally populates it with a lease)
+ ///
+ /// Creates a lease manager (memfile, trimmed down to keep everything in memory
+ /// only) and optionally can create a lease, which is useful for leaseX-get and
+ /// leaseX-del type of tests. For lease details, see @ref createLease4 and
+ /// @ref createLease6.
+ ///
+ /// @param v6 true = v6, false = v4
+ /// @param insert_lease governs whether a lease should be pre-inserted
+ /// @param declined governs whether a lease should be in declined state
+ void initLeaseMgr(bool v6, bool insert_lease, bool declined = false) {
+ isc::dhcp::LeaseMgrFactory::destroy();
+ std::ostringstream s;
+ s << "type=memfile persist=false " << (v6 ? "universe=6" : "universe=4");
+ isc::dhcp::LeaseMgrFactory::create(s.str());
+
+ lmptr_ = &(isc::dhcp::LeaseMgrFactory::instance());
+ ASSERT_TRUE(lmptr_);
+
+ isc::dhcp::CfgMgr& cfg_mgr = isc::dhcp::CfgMgr::instance();
+ if (v6) {
+ isc::dhcp::Subnet6Ptr subnet66(new isc::dhcp::Subnet6(isc::asiolink::IOAddress("2001:db8:1::"), 48, 1, 2, 3, 4, 66));
+ isc::dhcp::Subnet6Ptr subnet99(new isc::dhcp::Subnet6(isc::asiolink::IOAddress("2001:db8:2::"), 48, 1, 2, 3, 4, 99));
+ isc::dhcp::CfgSubnets6Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets6();
+ subnets->add(subnet66);
+ subnets->add(subnet99);
+ cfg_mgr.commit();
+ } else {
+ isc::dhcp::Subnet4Ptr subnet44(new isc::dhcp::Subnet4(isc::asiolink::IOAddress("192.0.2.0"), 24, 1, 2, 3, 44));
+ isc::dhcp::Subnet4Ptr subnet88(new isc::dhcp::Subnet4(isc::asiolink::IOAddress("192.0.3.0"), 24, 1, 2, 3, 88));
+ isc::dhcp::CfgSubnets4Ptr subnets = cfg_mgr.getStagingCfg()->getCfgSubnets4();
+ subnets->add(subnet44);
+ subnets->add(subnet88);
+ cfg_mgr.commit();
+ }
+
+ if (insert_lease) {
+ if (v6) {
+ lmptr_->addLease(createLease6("2001:db8:1::1", 66, 0x42, declined));
+ lmptr_->addLease(createLease6("2001:db8:1::2", 66, 0x56, declined));
+ lmptr_->addLease(createLease6("2001:db8:2::1", 99, 0x42, declined));
+ lmptr_->addLease(createLease6("2001:db8:2::2", 99, 0x56, declined));
+ if (declined) {
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 66, "declined-addresses"),
+ int64_t(2));
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 99, "declined-addresses"),
+ int64_t(2));
+ } else {
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 66, "declined-addresses"),
+ int64_t(0));
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 99, "declined-addresses"),
+ int64_t(0));
+ }
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 66, "assigned-nas" ),
+ int64_t(2));
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 99, "assigned-nas" ),
+ int64_t(2));
+ } else {
+ lmptr_->addLease(createLease4("192.0.2.1", 44, 0x08, 0x42, declined));
+ lmptr_->addLease(createLease4("192.0.2.2", 44, 0x09, 0x56, declined));
+ lmptr_->addLease(createLease4("192.0.3.1", 88, 0x08, 0x42, declined));
+ lmptr_->addLease(createLease4("192.0.3.2", 88, 0x09, 0x56, declined));
+ if (declined) {
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 44, "declined-addresses"),
+ int64_t(2));
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 88, "declined-addresses"),
+ int64_t(2));
+ } else {
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 44, "declined-addresses"),
+ int64_t(0));
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 88, "declined-addresses"),
+ int64_t(0));
+ }
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 44, "assigned-addresses"),
+ int64_t(2));
+ isc::stats::StatsMgr::instance().setValue(
+ isc::stats::StatsMgr::generateName("subnet", 88, "assigned-addresses"),
+ int64_t(2));
+ }
+ }
+ }
+
+ /// @brief This function checks that the JSON list contains an entry
+ /// indicating lease deletion, creation or update failure.
+ ///
+ /// @param failed_leases_list JSON list containing list of leases.
+ /// @param expected_type Expected lease type as text.
+ /// @param expected_ip_address Expected IP address.
+ /// @oaram expected_control_result Expected control result for the lease.
+ /// @param expected_error_msg Expected error message. Default is an empty
+ /// string which indicates that the error message should not be checked.
+ void checkFailedLease(const isc::data::ConstElementPtr& failed_leases_list,
+ const std::string& expected_type,
+ const std::string& expected_ip_address,
+ const int expected_control_result,
+ const std::string& expected_error_msg = "") {
+ ASSERT_TRUE(failed_leases_list);
+
+ for (auto i = 0; i < failed_leases_list->size(); ++i) {
+ auto failed_lease = failed_leases_list->get(i);
+ ASSERT_TRUE(failed_lease);
+ ASSERT_EQ(isc::data::Element::map, failed_lease->getType());
+
+ auto ip_address = failed_lease->get("ip-address");
+ ASSERT_TRUE(ip_address);
+ ASSERT_EQ(isc::data::Element::string, ip_address->getType());
+
+ if (ip_address->stringValue() == expected_ip_address) {
+ auto lease_type = failed_lease->get("type");
+ ASSERT_TRUE(lease_type);
+ ASSERT_EQ(isc::data::Element::string, lease_type->getType());
+ EXPECT_EQ(expected_type, lease_type->stringValue());
+
+ auto control_result = failed_lease->get("result");
+ ASSERT_TRUE(control_result);
+ ASSERT_EQ(isc::data::Element::integer, control_result->getType());
+ EXPECT_EQ(expected_control_result, control_result->intValue());
+
+ if (!expected_error_msg.empty()) {
+ auto error_msg = failed_lease->get("error-message");
+ ASSERT_TRUE(error_msg);
+ ASSERT_EQ(isc::data::Element::string, error_msg->getType());
+ EXPECT_EQ(expected_error_msg, error_msg->stringValue());
+ }
+
+ return;
+ }
+ }
+
+ ADD_FAILURE() << "expected lease not found";
+ }
+
+ /// @brief Enables DHCP-DDNS updates.
+ void enableD2() {
+ isc::dhcp::D2ClientConfigPtr cfg(new isc::dhcp::D2ClientConfig());
+ ASSERT_NO_THROW(cfg->enableUpdates(true));
+ ASSERT_NO_THROW(isc::dhcp::CfgMgr::instance().setD2ClientConfig(cfg));
+ d2_mgr_.startSender(std::bind(&LeaseCmdsTest::d2ErrorHandler, this,
+ std::placeholders::_1, std::placeholders::_2));
+ }
+
+ /// @brief Disables DHCP-DDNS updates.
+ void disableD2() {
+ d2_mgr_.stopSender();
+ // Default constructor creates a config with DHCP-DDNS updates
+ // disabled.
+ isc::dhcp::D2ClientConfigPtr cfg(new isc::dhcp::D2ClientConfig());
+ isc::dhcp::CfgMgr::instance().setD2ClientConfig(cfg);
+ }
+
+ /// @brief No-op error handler for D2.
+ void d2ErrorHandler(const isc::dhcp_ddns::NameChangeSender::Result,
+ isc::dhcp_ddns::NameChangeRequestPtr&) {
+ // no-op
+ }
+
+ /// @brief Fetches the number of entries in the NCR sender queue.
+ ///
+ /// @return The NCR queue size.
+ int ncrQueueSize() {
+ int size = -1;
+ try {
+ size = d2_mgr_.getQueueSize();
+ } catch (...) {
+ // If d2_mgr_ isn't in sending, it will throw.
+ // Swallow the exception and return -1.
+ }
+
+ return (size);
+ }
+
+ /// @brief Check that leaseX-del checks update-ddns input.
+ void testLeaseXDelBadUpdateDdnsParam();
+};
+
+} // end of anonymous namespace
diff --git a/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc
new file mode 100644
index 0000000..3b1baf9
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/tests/run_unittests.cc
@@ -0,0 +1,19 @@
+// Copyright (C) 2017-2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <log/logger_support.h>
+#include <gtest/gtest.h>
+
+int
+main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+ isc::log::initLogger();
+ int result = RUN_ALL_TESTS();
+
+ return (result);
+}
diff --git a/src/hooks/dhcp/lease_cmds/version.cc b/src/hooks/dhcp/lease_cmds/version.cc
new file mode 100644
index 0000000..d3f7057
--- /dev/null
+++ b/src/hooks/dhcp/lease_cmds/version.cc
@@ -0,0 +1,17 @@
+// Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+#include <hooks/hooks.h>
+
+extern "C" {
+
+/// @brief returns Kea hooks version.
+int version() {
+ return (KEA_HOOKS_VERSION);
+}
+
+}