diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 11:36:04 +0000 |
commit | 040eee1aa49b49df4698d83a05af57c220127fd1 (patch) | |
tree | f635435954e6ccde5eee9893889e24f30ca68346 /src/bin/netconf/tests | |
parent | Initial commit. (diff) | |
download | isc-kea-upstream.tar.xz isc-kea-upstream.zip |
Adding upstream version 2.2.0.upstream/2.2.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/bin/netconf/tests')
-rw-r--r-- | src/bin/netconf/tests/Makefile.am | 91 | ||||
-rw-r--r-- | src/bin/netconf/tests/Makefile.in | 1224 | ||||
-rw-r--r-- | src/bin/netconf/tests/basic_library.cc | 72 | ||||
-rw-r--r-- | src/bin/netconf/tests/control_socket_unittests.cc | 866 | ||||
-rw-r--r-- | src/bin/netconf/tests/get_config_unittest.cc | 294 | ||||
-rw-r--r-- | src/bin/netconf/tests/netconf_cfg_mgr_unittests.cc | 738 | ||||
-rw-r--r-- | src/bin/netconf/tests/netconf_controller_unittests.cc | 194 | ||||
-rw-r--r-- | src/bin/netconf/tests/netconf_process_unittests.cc | 92 | ||||
-rw-r--r-- | src/bin/netconf/tests/netconf_unittests.cc | 1212 | ||||
-rw-r--r-- | src/bin/netconf/tests/parser_unittests.cc | 988 | ||||
-rw-r--r-- | src/bin/netconf/tests/run_unittests.cc | 27 | ||||
-rw-r--r-- | src/bin/netconf/tests/shtests/Makefile.am | 15 | ||||
-rw-r--r-- | src/bin/netconf/tests/shtests/Makefile.in | 841 | ||||
-rw-r--r-- | src/bin/netconf/tests/shtests/netconf_tests.sh.in | 216 | ||||
-rw-r--r-- | src/bin/netconf/tests/test_data_files_config.h.in | 9 | ||||
-rw-r--r-- | src/bin/netconf/tests/test_libraries.h.in | 24 | ||||
-rw-r--r-- | src/bin/netconf/tests/testdata/get_config.json | 24 |
17 files changed, 6927 insertions, 0 deletions
diff --git a/src/bin/netconf/tests/Makefile.am b/src/bin/netconf/tests/Makefile.am new file mode 100644 index 0000000..e25e62a --- /dev/null +++ b/src/bin/netconf/tests/Makefile.am @@ -0,0 +1,91 @@ +SUBDIRS = . shtests + +EXTRA_DIST = testdata/get_config.json + +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib +AM_CPPFLAGS += -I$(top_srcdir)/src -I$(top_builddir)/src +AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/netconf/tests\" +AM_CPPFLAGS += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/netconf\" +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/netconf/tests\" +AM_CPPFLAGS += -DSYNTAX_FILE=\"$(abs_srcdir)/../netconf_parser.yy\" +AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) +AM_CPPFLAGS += $(SYSREPO_CPPFLAGS) +AM_CPPFLAGS += $(SYSREPO_INCLUDEDIR) + +CLEANFILES = *.json *.log + +DISTCLEANFILES = test_data_files_config.h test_libraries.h + +AM_CXXFLAGS = $(KEA_CXXFLAGS) + +if USE_STATIC_LINK +AM_LDFLAGS = -static +endif + +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +TESTS = +if HAVE_GTEST + +noinst_LTLIBRARIES = libbasic.la + +TESTS += netconf_unittests + +netconf_unittests_SOURCES = control_socket_unittests.cc +netconf_unittests_SOURCES += get_config_unittest.cc +netconf_unittests_SOURCES += netconf_cfg_mgr_unittests.cc +netconf_unittests_SOURCES += netconf_controller_unittests.cc +netconf_unittests_SOURCES += netconf_process_unittests.cc +netconf_unittests_SOURCES += netconf_unittests.cc +netconf_unittests_SOURCES += parser_unittests.cc +netconf_unittests_SOURCES += run_unittests.cc + +netconf_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +netconf_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) + + +netconf_unittests_LDADD = $(top_builddir)/src/bin/netconf/libnetconf.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/process/testutils/libprocesstest.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/process/libkea-process.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/yang/testutils/libyangtest.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/yang/libkea-yang.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/testutils/libkea-testutils.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/log/libkea-log.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/util/unittests/libutil_unittests.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/util/libkea-util.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +netconf_unittests_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) +netconf_unittests_LDADD += $(BOOST_LIBS) $(GTEST_LDADD) $(SYSREPO_LIBS) + +# The basic callout library - contains standard callouts +libbasic_la_SOURCES = basic_library.cc +libbasic_la_CXXFLAGS = $(AM_CXXFLAGS) +libbasic_la_CPPFLAGS = $(AM_CPPFLAGS) +libbasic_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la +libbasic_la_LIBADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la +libbasic_la_LIBADD += $(top_builddir)/src/lib/log/libkea-log.la +libbasic_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere + +nodist_netconf_unittests_SOURCES = test_data_files_config.h test_libraries.h +endif + +noinst_EXTRA_DIST = configs-list.txt + +noinst_PROGRAMS = $(TESTS) diff --git a/src/bin/netconf/tests/Makefile.in b/src/bin/netconf/tests/Makefile.in new file mode 100644 index 0000000..e2b84ae --- /dev/null +++ b/src/bin/netconf/tests/Makefile.in @@ -0,0 +1,1224 @@ +# 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 = netconf_unittests +noinst_PROGRAMS = $(am__EXEEXT_2) +subdir = src/bin/netconf/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 = test_data_files_config.h test_libraries.h +CONFIG_CLEAN_VPATH_FILES = +@HAVE_GTEST_TRUE@am__EXEEXT_1 = netconf_unittests$(EXEEXT) +am__EXEEXT_2 = $(am__EXEEXT_1) +PROGRAMS = $(noinst_PROGRAMS) +LTLIBRARIES = $(noinst_LTLIBRARIES) +@HAVE_GTEST_TRUE@libbasic_la_DEPENDENCIES = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la +am__libbasic_la_SOURCES_DIST = basic_library.cc +@HAVE_GTEST_TRUE@am_libbasic_la_OBJECTS = \ +@HAVE_GTEST_TRUE@ libbasic_la-basic_library.lo +libbasic_la_OBJECTS = $(am_libbasic_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 = +libbasic_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(libbasic_la_CXXFLAGS) \ + $(CXXFLAGS) $(libbasic_la_LDFLAGS) $(LDFLAGS) -o $@ +@HAVE_GTEST_TRUE@am_libbasic_la_rpath = +am__netconf_unittests_SOURCES_DIST = control_socket_unittests.cc \ + get_config_unittest.cc netconf_cfg_mgr_unittests.cc \ + netconf_controller_unittests.cc netconf_process_unittests.cc \ + netconf_unittests.cc parser_unittests.cc run_unittests.cc +@HAVE_GTEST_TRUE@am_netconf_unittests_OBJECTS = netconf_unittests-control_socket_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ netconf_unittests-get_config_unittest.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ netconf_unittests-netconf_cfg_mgr_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ netconf_unittests-netconf_controller_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ netconf_unittests-netconf_process_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ netconf_unittests-netconf_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ netconf_unittests-parser_unittests.$(OBJEXT) \ +@HAVE_GTEST_TRUE@ netconf_unittests-run_unittests.$(OBJEXT) +nodist_netconf_unittests_OBJECTS = +netconf_unittests_OBJECTS = $(am_netconf_unittests_OBJECTS) \ + $(nodist_netconf_unittests_OBJECTS) +am__DEPENDENCIES_1 = +@HAVE_GTEST_TRUE@netconf_unittests_DEPENDENCIES = $(top_builddir)/src/bin/netconf/libnetconf.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/testutils/libprocesstest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/yang/testutils/libyangtest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/yang/libkea-yang.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/testutils/libkea-testutils.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/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/unittests/libutil_unittests.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) \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) +netconf_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(netconf_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)/libbasic_la-basic_library.Plo \ + ./$(DEPDIR)/netconf_unittests-control_socket_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-get_config_unittest.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_controller_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_process_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-parser_unittests.Po \ + ./$(DEPDIR)/netconf_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 = $(libbasic_la_SOURCES) $(netconf_unittests_SOURCES) \ + $(nodist_netconf_unittests_SOURCES) +DIST_SOURCES = $(am__libbasic_la_SOURCES_DIST) \ + $(am__netconf_unittests_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/test_data_files_config.h.in \ + $(srcdir)/test_libraries.h.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 = . shtests +EXTRA_DIST = testdata/get_config.json +AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \ + -I$(top_srcdir)/src -I$(top_builddir)/src \ + -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin \ + -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/netconf/tests\" \ + -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/netconf\" \ + -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/netconf/tests\" \ + -DSYNTAX_FILE=\"$(abs_srcdir)/../netconf_parser.yy\" \ + $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) \ + $(SYSREPO_CPPFLAGS) $(SYSREPO_INCLUDEDIR) +CLEANFILES = *.json *.log +DISTCLEANFILES = test_data_files_config.h test_libraries.h +AM_CXXFLAGS = $(KEA_CXXFLAGS) +@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static +TESTS_ENVIRONMENT = \ + $(LIBTOOL) --mode=execute $(VALGRIND_COMMAND) + +@HAVE_GTEST_TRUE@noinst_LTLIBRARIES = libbasic.la +@HAVE_GTEST_TRUE@netconf_unittests_SOURCES = \ +@HAVE_GTEST_TRUE@ control_socket_unittests.cc \ +@HAVE_GTEST_TRUE@ get_config_unittest.cc \ +@HAVE_GTEST_TRUE@ netconf_cfg_mgr_unittests.cc \ +@HAVE_GTEST_TRUE@ netconf_controller_unittests.cc \ +@HAVE_GTEST_TRUE@ netconf_process_unittests.cc \ +@HAVE_GTEST_TRUE@ netconf_unittests.cc parser_unittests.cc \ +@HAVE_GTEST_TRUE@ run_unittests.cc +@HAVE_GTEST_TRUE@netconf_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) +@HAVE_GTEST_TRUE@netconf_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libkea-dhcpsrv.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/testutils/libdhcpsrvtest.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/eval/libkea-eval.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcp_ddns/libkea-dhcp_ddns.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la +#netconf_unittests_LDADD += $(top_builddir)/src/lib/dhcp/tests/libdhcptest.la +@HAVE_GTEST_TRUE@netconf_unittests_LDADD = $(top_builddir)/src/bin/netconf/libnetconf.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/testutils/libprocesstest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/process/libkea-process.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/yang/testutils/libyangtest.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/yang/libkea-yang.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/testutils/libkea-testutils.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/cc/libkea-cc.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/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/unittests/libutil_unittests.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) $(SYSREPO_LIBS) + +# The basic callout library - contains standard callouts +@HAVE_GTEST_TRUE@libbasic_la_SOURCES = basic_library.cc +@HAVE_GTEST_TRUE@libbasic_la_CXXFLAGS = $(AM_CXXFLAGS) +@HAVE_GTEST_TRUE@libbasic_la_CPPFLAGS = $(AM_CPPFLAGS) +@HAVE_GTEST_TRUE@libbasic_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/hooks/libkea-hooks.la \ +@HAVE_GTEST_TRUE@ $(top_builddir)/src/lib/log/libkea-log.la +@HAVE_GTEST_TRUE@libbasic_la_LDFLAGS = -avoid-version -export-dynamic -module -rpath /nowhere +@HAVE_GTEST_TRUE@nodist_netconf_unittests_SOURCES = test_data_files_config.h test_libraries.h +noinst_EXTRA_DIST = configs-list.txt +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/bin/netconf/tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/bin/netconf/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): +test_data_files_config.h: $(top_builddir)/config.status $(srcdir)/test_data_files_config.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +test_libraries.h: $(top_builddir)/config.status $(srcdir)/test_libraries.h.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +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 + +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}; \ + } + +libbasic.la: $(libbasic_la_OBJECTS) $(libbasic_la_DEPENDENCIES) $(EXTRA_libbasic_la_DEPENDENCIES) + $(AM_V_CXXLD)$(libbasic_la_LINK) $(am_libbasic_la_rpath) $(libbasic_la_OBJECTS) $(libbasic_la_LIBADD) $(LIBS) + +netconf_unittests$(EXEEXT): $(netconf_unittests_OBJECTS) $(netconf_unittests_DEPENDENCIES) $(EXTRA_netconf_unittests_DEPENDENCIES) + @rm -f netconf_unittests$(EXEEXT) + $(AM_V_CXXLD)$(netconf_unittests_LINK) $(netconf_unittests_OBJECTS) $(netconf_unittests_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libbasic_la-basic_library.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_unittests-control_socket_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_unittests-get_config_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_unittests-netconf_controller_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_unittests-netconf_process_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_unittests-netconf_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_unittests-parser_unittests.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_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 $@ $< + +libbasic_la-basic_library.lo: basic_library.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libbasic_la_CPPFLAGS) $(CPPFLAGS) $(libbasic_la_CXXFLAGS) $(CXXFLAGS) -MT libbasic_la-basic_library.lo -MD -MP -MF $(DEPDIR)/libbasic_la-basic_library.Tpo -c -o libbasic_la-basic_library.lo `test -f 'basic_library.cc' || echo '$(srcdir)/'`basic_library.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libbasic_la-basic_library.Tpo $(DEPDIR)/libbasic_la-basic_library.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='basic_library.cc' object='libbasic_la-basic_library.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) $(libbasic_la_CPPFLAGS) $(CPPFLAGS) $(libbasic_la_CXXFLAGS) $(CXXFLAGS) -c -o libbasic_la-basic_library.lo `test -f 'basic_library.cc' || echo '$(srcdir)/'`basic_library.cc + +netconf_unittests-control_socket_unittests.o: control_socket_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-control_socket_unittests.o -MD -MP -MF $(DEPDIR)/netconf_unittests-control_socket_unittests.Tpo -c -o netconf_unittests-control_socket_unittests.o `test -f 'control_socket_unittests.cc' || echo '$(srcdir)/'`control_socket_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-control_socket_unittests.Tpo $(DEPDIR)/netconf_unittests-control_socket_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='control_socket_unittests.cc' object='netconf_unittests-control_socket_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-control_socket_unittests.o `test -f 'control_socket_unittests.cc' || echo '$(srcdir)/'`control_socket_unittests.cc + +netconf_unittests-control_socket_unittests.obj: control_socket_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-control_socket_unittests.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-control_socket_unittests.Tpo -c -o netconf_unittests-control_socket_unittests.obj `if test -f 'control_socket_unittests.cc'; then $(CYGPATH_W) 'control_socket_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/control_socket_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-control_socket_unittests.Tpo $(DEPDIR)/netconf_unittests-control_socket_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='control_socket_unittests.cc' object='netconf_unittests-control_socket_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-control_socket_unittests.obj `if test -f 'control_socket_unittests.cc'; then $(CYGPATH_W) 'control_socket_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/control_socket_unittests.cc'; fi` + +netconf_unittests-get_config_unittest.o: get_config_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-get_config_unittest.o -MD -MP -MF $(DEPDIR)/netconf_unittests-get_config_unittest.Tpo -c -o netconf_unittests-get_config_unittest.o `test -f 'get_config_unittest.cc' || echo '$(srcdir)/'`get_config_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-get_config_unittest.Tpo $(DEPDIR)/netconf_unittests-get_config_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='get_config_unittest.cc' object='netconf_unittests-get_config_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-get_config_unittest.o `test -f 'get_config_unittest.cc' || echo '$(srcdir)/'`get_config_unittest.cc + +netconf_unittests-get_config_unittest.obj: get_config_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-get_config_unittest.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-get_config_unittest.Tpo -c -o netconf_unittests-get_config_unittest.obj `if test -f 'get_config_unittest.cc'; then $(CYGPATH_W) 'get_config_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/get_config_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-get_config_unittest.Tpo $(DEPDIR)/netconf_unittests-get_config_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='get_config_unittest.cc' object='netconf_unittests-get_config_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-get_config_unittest.obj `if test -f 'get_config_unittest.cc'; then $(CYGPATH_W) 'get_config_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/get_config_unittest.cc'; fi` + +netconf_unittests-netconf_cfg_mgr_unittests.o: netconf_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_cfg_mgr_unittests.o -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Tpo -c -o netconf_unittests-netconf_cfg_mgr_unittests.o `test -f 'netconf_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`netconf_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_cfg_mgr_unittests.cc' object='netconf_unittests-netconf_cfg_mgr_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_cfg_mgr_unittests.o `test -f 'netconf_cfg_mgr_unittests.cc' || echo '$(srcdir)/'`netconf_cfg_mgr_unittests.cc + +netconf_unittests-netconf_cfg_mgr_unittests.obj: netconf_cfg_mgr_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_cfg_mgr_unittests.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Tpo -c -o netconf_unittests-netconf_cfg_mgr_unittests.obj `if test -f 'netconf_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'netconf_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_cfg_mgr_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_cfg_mgr_unittests.cc' object='netconf_unittests-netconf_cfg_mgr_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_cfg_mgr_unittests.obj `if test -f 'netconf_cfg_mgr_unittests.cc'; then $(CYGPATH_W) 'netconf_cfg_mgr_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_cfg_mgr_unittests.cc'; fi` + +netconf_unittests-netconf_controller_unittests.o: netconf_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_controller_unittests.o -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_controller_unittests.Tpo -c -o netconf_unittests-netconf_controller_unittests.o `test -f 'netconf_controller_unittests.cc' || echo '$(srcdir)/'`netconf_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_controller_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_controller_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_controller_unittests.cc' object='netconf_unittests-netconf_controller_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_controller_unittests.o `test -f 'netconf_controller_unittests.cc' || echo '$(srcdir)/'`netconf_controller_unittests.cc + +netconf_unittests-netconf_controller_unittests.obj: netconf_controller_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_controller_unittests.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_controller_unittests.Tpo -c -o netconf_unittests-netconf_controller_unittests.obj `if test -f 'netconf_controller_unittests.cc'; then $(CYGPATH_W) 'netconf_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_controller_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_controller_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_controller_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_controller_unittests.cc' object='netconf_unittests-netconf_controller_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_controller_unittests.obj `if test -f 'netconf_controller_unittests.cc'; then $(CYGPATH_W) 'netconf_controller_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_controller_unittests.cc'; fi` + +netconf_unittests-netconf_process_unittests.o: netconf_process_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_process_unittests.o -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_process_unittests.Tpo -c -o netconf_unittests-netconf_process_unittests.o `test -f 'netconf_process_unittests.cc' || echo '$(srcdir)/'`netconf_process_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_process_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_process_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_process_unittests.cc' object='netconf_unittests-netconf_process_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_process_unittests.o `test -f 'netconf_process_unittests.cc' || echo '$(srcdir)/'`netconf_process_unittests.cc + +netconf_unittests-netconf_process_unittests.obj: netconf_process_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_process_unittests.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_process_unittests.Tpo -c -o netconf_unittests-netconf_process_unittests.obj `if test -f 'netconf_process_unittests.cc'; then $(CYGPATH_W) 'netconf_process_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_process_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_process_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_process_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_process_unittests.cc' object='netconf_unittests-netconf_process_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_process_unittests.obj `if test -f 'netconf_process_unittests.cc'; then $(CYGPATH_W) 'netconf_process_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_process_unittests.cc'; fi` + +netconf_unittests-netconf_unittests.o: netconf_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_unittests.o -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_unittests.Tpo -c -o netconf_unittests-netconf_unittests.o `test -f 'netconf_unittests.cc' || echo '$(srcdir)/'`netconf_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_unittests.cc' object='netconf_unittests-netconf_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_unittests.o `test -f 'netconf_unittests.cc' || echo '$(srcdir)/'`netconf_unittests.cc + +netconf_unittests-netconf_unittests.obj: netconf_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-netconf_unittests.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-netconf_unittests.Tpo -c -o netconf_unittests-netconf_unittests.obj `if test -f 'netconf_unittests.cc'; then $(CYGPATH_W) 'netconf_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-netconf_unittests.Tpo $(DEPDIR)/netconf_unittests-netconf_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='netconf_unittests.cc' object='netconf_unittests-netconf_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-netconf_unittests.obj `if test -f 'netconf_unittests.cc'; then $(CYGPATH_W) 'netconf_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/netconf_unittests.cc'; fi` + +netconf_unittests-parser_unittests.o: parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-parser_unittests.o -MD -MP -MF $(DEPDIR)/netconf_unittests-parser_unittests.Tpo -c -o netconf_unittests-parser_unittests.o `test -f 'parser_unittests.cc' || echo '$(srcdir)/'`parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-parser_unittests.Tpo $(DEPDIR)/netconf_unittests-parser_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parser_unittests.cc' object='netconf_unittests-parser_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-parser_unittests.o `test -f 'parser_unittests.cc' || echo '$(srcdir)/'`parser_unittests.cc + +netconf_unittests-parser_unittests.obj: parser_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-parser_unittests.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-parser_unittests.Tpo -c -o netconf_unittests-parser_unittests.obj `if test -f 'parser_unittests.cc'; then $(CYGPATH_W) 'parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/parser_unittests.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-parser_unittests.Tpo $(DEPDIR)/netconf_unittests-parser_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='parser_unittests.cc' object='netconf_unittests-parser_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-parser_unittests.obj `if test -f 'parser_unittests.cc'; then $(CYGPATH_W) 'parser_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/parser_unittests.cc'; fi` + +netconf_unittests-run_unittests.o: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-run_unittests.o -MD -MP -MF $(DEPDIR)/netconf_unittests-run_unittests.Tpo -c -o netconf_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/netconf_unittests-run_unittests.Tpo $(DEPDIR)/netconf_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='netconf_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-run_unittests.o `test -f 'run_unittests.cc' || echo '$(srcdir)/'`run_unittests.cc + +netconf_unittests-run_unittests.obj: run_unittests.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT netconf_unittests-run_unittests.obj -MD -MP -MF $(DEPDIR)/netconf_unittests-run_unittests.Tpo -c -o netconf_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)/netconf_unittests-run_unittests.Tpo $(DEPDIR)/netconf_unittests-run_unittests.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='run_unittests.cc' object='netconf_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) $(netconf_unittests_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o netconf_unittests-run_unittests.obj `if test -f 'run_unittests.cc'; then $(CYGPATH_W) 'run_unittests.cc'; else $(CYGPATH_W) '$(srcdir)/run_unittests.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) $(LTLIBRARIES) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/libbasic_la-basic_library.Plo + -rm -f ./$(DEPDIR)/netconf_unittests-control_socket_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-get_config_unittest.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_controller_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_process_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-parser_unittests.Po + -rm -f ./$(DEPDIR)/netconf_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)/libbasic_la-basic_library.Plo + -rm -f ./$(DEPDIR)/netconf_unittests-control_socket_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-get_config_unittest.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_controller_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_process_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-netconf_unittests.Po + -rm -f ./$(DEPDIR)/netconf_unittests-parser_unittests.Po + -rm -f ./$(DEPDIR)/netconf_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-noinstLTLIBRARIES 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/bin/netconf/tests/basic_library.cc b/src/bin/netconf/tests/basic_library.cc new file mode 100644 index 0000000..f2186e0 --- /dev/null +++ b/src/bin/netconf/tests/basic_library.cc @@ -0,0 +1,72 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// @file +/// @brief Basic callout library +/// +/// This is source of a test library for Control Agent. +/// +/// - Only the "version" framework function is supplied. +/// +/// - hookpt_one callout is supplied. + +#include <config.h> +#include <hooks/hooks.h> + +using namespace isc::hooks; +using namespace std; + +namespace { + +extern "C" { + +// Callouts. All return their result through the "result" argument. + +int +context_create(CalloutHandle& handle) { + handle.setContext("result", static_cast<int>(10)); + handle.setArgument("result", static_cast<int>(10)); + return (0); +} + +// First callout adds the passed "integer" argument to the initialized context +// value of 10. (Note that the value set by context_create is accessed through +// context and not the argument, so checking that context is correctly passed +// between callouts in the same library.) + +int +hookpt_one(CalloutHandle& handle) { + int data; + handle.getArgument("integer", data); + + int result; + handle.getArgument("result", result); + + result += data; + handle.setArgument("result", result); + + return (0); +} + +// Framework functions. + +int +version() { + return (KEA_HOOKS_VERSION); +} + +// load() initializes the user library if the main image was statically linked. +int +load(isc::hooks::LibraryHandle&) { +#ifdef USE_STATIC_LINK + hooksStaticLinkInit(); +#endif + return (0); +} + +} +} + diff --git a/src/bin/netconf/tests/control_socket_unittests.cc b/src/bin/netconf/tests/control_socket_unittests.cc new file mode 100644 index 0000000..fc4b35e --- /dev/null +++ b/src/bin/netconf/tests/control_socket_unittests.cc @@ -0,0 +1,866 @@ +// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <netconf/netconf_config.h> +#include <netconf/http_control_socket.h> +#include <netconf/stdout_control_socket.h> +#include <netconf/unix_control_socket.h> +#include <asiolink/asio_wrapper.h> +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> +#include <http/listener.h> +#include <http/post_request_json.h> +#include <http/response_creator.h> +#include <http/response_creator_factory.h> +#include <http/response_json.h> +#include <http/tests/response_test.h> +#include <testutils/threaded_test.h> +#include <testutils/sandbox.h> +#include <yang/tests/sysrepo_setup.h> + +#include <gtest/gtest.h> + +#include <sstream> +#include <thread> + +using namespace std; +using namespace isc; +using namespace isc::netconf; +using namespace isc::asiolink; +using namespace isc::data; +using namespace isc::http; +using namespace isc::http::test; +using namespace isc::test; + +using isc::yang::test::SysrepoSetup; + +namespace { + +/// @brief Type definition for the pointer to Thread objects. +typedef boost::shared_ptr<thread> ThreadPtr; + +//////////////////////////////// STDOUT //////////////////////////////// + +/// @brief Test derived StdoutControlSocket class. +/// +/// This class exposes the constructor taking the output stream. +class TestStdoutControlSocket : public StdoutControlSocket { +public: + /// @brief Constructor. + /// + /// @param ctrl_sock The control socket configuration. + /// @param output The output stream. + TestStdoutControlSocket(CfgControlSocketPtr ctrl_sock, ostream& output) + : StdoutControlSocket(ctrl_sock, output) { + } + + /// @brief Destructor. + virtual ~TestStdoutControlSocket() { + } +}; + +/// @brief Type definition for the pointer to the @c TestStdoutControlSocket. +typedef boost::shared_ptr<TestStdoutControlSocket> TestStdoutControlSocketPtr; + +// Verifies that the createControlSocket template can create a stdout +// control socket. +TEST(StdoutControlSocketTest, createControlSocket) { + CfgControlSocketPtr cfg(new + CfgControlSocket(CfgControlSocket::Type::STDOUT, + "", + Url("http://127.0.0.1:8000/"))); + ASSERT_TRUE(cfg); + ControlSocketBasePtr cs = controlSocketFactory(cfg); + ASSERT_TRUE(cs); + StdoutControlSocketPtr scs = + boost::dynamic_pointer_cast<StdoutControlSocket>(cs); + EXPECT_TRUE(scs); +} + +// Verifies that a stdout control socket does not implement configGet. +TEST(StdoutControlSocketTest, configGet) { + CfgControlSocketPtr cfg(new + CfgControlSocket(CfgControlSocket::Type::STDOUT, + "", + Url("http://127.0.0.1:8000/"))); + ASSERT_TRUE(cfg); + StdoutControlSocketPtr scs(new StdoutControlSocket(cfg)); + ASSERT_TRUE(scs); + EXPECT_THROW(scs->configGet("foo"), NotImplemented); +} + +// Verifies that a stdout control socket does not nothing for configTest. +TEST(StdoutControlSocketTest, configTest) { + CfgControlSocketPtr cfg(new + CfgControlSocket(CfgControlSocket::Type::STDOUT, + "", + Url("http://127.0.0.1:8000/"))); + ASSERT_TRUE(cfg); + StdoutControlSocketPtr scs(new StdoutControlSocket(cfg)); + ASSERT_TRUE(scs); + ConstElementPtr answer; + ASSERT_NO_THROW_LOG(answer = scs->configTest(ConstElementPtr(), "foo")); + + // Check answer. + ASSERT_TRUE(answer); + EXPECT_EQ("{ \"result\": 0 }", answer->str()); +} + +// Verifies that a stdout control socket outputs configSet argument. +TEST(StdoutControlSocketTest, configSet) { + CfgControlSocketPtr cfg(new + CfgControlSocket(CfgControlSocket::Type::STDOUT, + "", + Url("http://127.0.0.1:8000/"))); + ASSERT_TRUE(cfg); + ostringstream os; + TestStdoutControlSocketPtr tscs(new TestStdoutControlSocket(cfg, os)); + ASSERT_TRUE(tscs); + ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + ConstElementPtr answer; + ASSERT_NO_THROW_LOG(answer = tscs->configSet(json, "foo")); + + // Check answer. + ASSERT_TRUE(answer); + EXPECT_EQ("{ \"result\": 0 }", answer->str()); + + // Check output. + string expected = "{\n \"bar\": 1\n}\n"; + EXPECT_EQ(expected, os.str()); +} + +//////////////////////////////// UNIX //////////////////////////////// + +/// @brief Test timeout in ms. +const long TEST_TIMEOUT = 10000; + +/// @brief Test fixture class for unix control sockets. +class UnixControlSocketTest : public ThreadedTest { +public: + isc::test::Sandbox sandbox; + + /// @brief Constructor. + UnixControlSocketTest() + : ThreadedTest(), io_service_() { + } + + + void SetUp() override { + SysrepoSetup::cleanSharedMemory(); + removeUnixSocketFile(); + } + + void TearDown() override { + if (thread_) { + thread_->join(); + thread_.reset(); + } + // io_service must be stopped after the thread returns, + // otherwise the thread may never return if it is + // waiting for the completion of some asynchronous tasks. + io_service_.stop(); + removeUnixSocketFile(); + } + + /// @brief Returns socket file path. + /// + /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the + /// socket file is created in the location pointed to by this variable. + /// Otherwise, it is created in the build directory. + string unixSocketFilePath() { + std::string socket_path; + const char* env = getenv("KEA_SOCKET_TEST_DIR"); + if (env) { + socket_path = std::string(env) + "/test-socket"; + } else { + socket_path = sandbox.join("test-socket"); + } + return (socket_path); + } + + /// @brief Removes unix socket descriptor. + void removeUnixSocketFile() { + static_cast<void>(remove(unixSocketFilePath().c_str())); + } + + /// @brief Create configuration of the control socket. + /// + /// @return a pointer to a control socket configuration. + CfgControlSocketPtr createCfgControlSocket() { + CfgControlSocketPtr cfg; + cfg.reset(new CfgControlSocket(CfgControlSocket::Type::UNIX, + unixSocketFilePath(), + Url("http://127.0.0.1:8000/"))); + return (cfg); + } + + /// @brief Thread reflecting server function. + void reflectServer(); + + /// @brief IOService object. + IOService io_service_; +}; + +/// @brief Server method running in a thread reflecting the command. +/// +/// It creates the server socket, accepts client connection, read +/// the command and send it back in a received JSON map. +void +UnixControlSocketTest::reflectServer() { + // Acceptor. + boost::asio::local::stream_protocol::acceptor + acceptor(io_service_.get_io_service()); + EXPECT_NO_THROW(acceptor.open()); + boost::asio::local::stream_protocol::endpoint + endpoint(unixSocketFilePath()); + EXPECT_NO_THROW(acceptor.bind(endpoint)); + EXPECT_NO_THROW(acceptor.listen()); + boost::asio::local::stream_protocol::socket + socket(io_service_.get_io_service()); + + // Ready. + signalReady(); + + // Timeout. + IntervalTimer timer(io_service_); + bool timeout = false; + timer.setup([&timeout]() { + timeout = true; + FAIL() << "timeout"; + }, 1500, IntervalTimer::ONE_SHOT); + + // Accept. + bool accepted = false; + boost::system::error_code ec; + acceptor.async_accept(socket, + [&ec, &accepted] + (const boost::system::error_code& error) { + ec = error; + accepted = true; + }); + while (!accepted && !timeout) { + io_service_.run_one(); + } + ASSERT_FALSE(ec); + + // Receive command. + string rbuf(1024, ' '); + size_t received = 0; + socket.async_receive(boost::asio::buffer(&rbuf[0], rbuf.size()), + [&ec, &received] + (const boost::system::error_code& error, size_t cnt) { + ec = error; + received = cnt; + }); + while (!received && !timeout) { + io_service_.run_one(); + } + ASSERT_FALSE(ec); + rbuf.resize(received); + + // Reflect. + ElementPtr map = Element::createMap(); + map->set("received", Element::create(rbuf)); + string sbuf = map->str(); + + // Send back. + size_t sent = 0; + socket.async_send(boost::asio::buffer(&sbuf[0], sbuf.size()), + [&ec, &sent] + (const boost::system::error_code& error, size_t cnt) { + ec = error; + sent = cnt; + }); + while (!sent && !timeout) { + io_service_.run_one(); + } + ASSERT_FALSE(ec); + + // Stop timer. + timer.cancel(); + + // Close socket. + if (socket.is_open()) { + EXPECT_NO_THROW(socket.close()); + } + + EXPECT_FALSE(timeout); + EXPECT_TRUE(accepted); + EXPECT_TRUE(received); + EXPECT_TRUE(sent); + EXPECT_EQ(sent, sbuf.size()); +} + +// Verifies that the createControlSocket template can create an unix +// control socket. +TEST_F(UnixControlSocketTest, createControlSocket) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + ControlSocketBasePtr cs = controlSocketFactory(cfg); + ASSERT_TRUE(cs); + UnixControlSocketPtr ucs = + boost::dynamic_pointer_cast<UnixControlSocket>(cs); + EXPECT_TRUE(ucs); +} + +// Verifies that unix control sockets handle configGet() as expected. +TEST_F(UnixControlSocketTest, configGet) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + UnixControlSocketPtr ucs(new UnixControlSocket(cfg)); + ASSERT_TRUE(ucs); + + // Run a reflecting server in a thread. + thread_.reset(new thread([this]() { reflectServer(); })); + + waitReady(); + + // Try configGet. + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = ucs->configGet("foo")); + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"command\": \"config-get\" }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that unix control sockets handle configTest() as expected. +TEST_F(UnixControlSocketTest, configTest) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + UnixControlSocketPtr ucs(new UnixControlSocket(cfg)); + ASSERT_TRUE(ucs); + + // Run a reflecting server in a thread. + thread_.reset(new thread([this]() { reflectServer(); })); + + waitReady(); + + // Prepare a config to test. + ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = ucs->configTest(json, "foo")); + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"arguments\": { \"bar\": 1 }, " + "\"command\": \"config-test\" }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that unix control sockets handle configSet() as expected. +TEST_F(UnixControlSocketTest, configSet) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + UnixControlSocketPtr ucs(new UnixControlSocket(cfg)); + ASSERT_TRUE(ucs); + + // Run a reflecting server in a thread. + thread_.reset(new thread([this]() { reflectServer(); })); + + waitReady(); + + // Prepare a config to set. + ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = ucs->configSet(json, "foo")); + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"arguments\": { \"bar\": 1 }, " + "\"command\": \"config-set\" }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that unix control sockets handle timeouts. +TEST_F(UnixControlSocketTest, timeout) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + UnixControlSocketPtr ucs(new UnixControlSocket(cfg)); + ASSERT_TRUE(ucs); + + // Run a timeout server in a thread. + thread_.reset(new thread([this]() { waitReady(); })); + + // Try configGet: it should get a communication error, + EXPECT_THROW(ucs->configGet("foo"), ControlSocketError); + signalReady(); +} + +//////////////////////////////// HTTP //////////////////////////////// + +/// @brief IP address to which HTTP service is bound. +const string SERVER_ADDRESS = "127.0.0.1"; + +/// @brief Port number to which HTTP service is bound. +const uint16_t SERVER_PORT = 18123; + +/// @brief Test HTTP JSON response. +typedef TestHttpResponseBase<HttpResponseJson> Response; + +/// @brief Pointer to test HTTP JSON response. +typedef boost::shared_ptr<Response> ResponsePtr; + +/// @brief Generic test HTTP response. +typedef TestHttpResponseBase<HttpResponse> GenericResponse; + +/// @brief Pointer to generic test HTTP response. +typedef boost::shared_ptr<GenericResponse> GenericResponsePtr; + +/// @brief Implementation of the HttpResponseCreator. +/// +/// Send back the request in a received JSON map. +class TestHttpResponseCreator : public HttpResponseCreator { +public: + /// @brief Create a new request. + /// + /// @return Pointer to the new instance of the HttpRequest. + virtual HttpRequestPtr + createNewHttpRequest() const { + return (HttpRequestPtr(new PostHttpRequestJson())); + } + +protected: + /// @brief Creates HTTP response. + /// + /// @param request Pointer to the HTTP request. + /// @return Pointer to the generated HTTP response. + virtual HttpResponsePtr + createStockHttpResponse(const HttpRequestPtr& request, + const HttpStatusCode& status_code) const { + // Data is in the request context. + HttpVersion http_version(request->context()->http_version_major_, + request->context()->http_version_minor_); + ResponsePtr response(new Response(http_version, status_code)); + response->finalize(); + return (response); + } + + /// @brief Creates HTTP response. + /// + /// Build a response with reflected request in a received JSON map. + /// It can be told to respond with a partial JSON. + /// + /// @param request Pointer to the HTTP request. + /// @return Pointer to an object representing HTTP response. + virtual HttpResponsePtr + createDynamicHttpResponse(HttpRequestPtr request) { + // Request must always be JSON. + PostHttpRequestJsonPtr request_json = + boost::dynamic_pointer_cast<PostHttpRequestJson>(request); + if (!request_json) { + isc_throw(Unexpected, "request is not JSON"); + } + ConstElementPtr body = request_json->getBodyAsJson(); + if (!body) { + isc_throw(Unexpected, "can't get JSON from request"); + } + + // Check for the special partial JSON. + ConstElementPtr arguments = body->get("arguments"); + if (arguments && (arguments->contains("want-partial"))) { + // Use a generic response. + GenericResponsePtr + response(new GenericResponse(request->getHttpVersion(), + HttpStatusCode::OK)); + HttpResponseContextPtr ctx = response->context(); + // Generate JSON response. + ctx->headers_.push_back(HttpHeaderContext("Content-Type", + "application/json")); + // Not closed JSON map so it will fail. + ctx->body_ = "{"; + response->finalize(); + // Take into account the missing '}'. + response->setContentLength(2); + return (response); + } + + // Reflect. + ResponsePtr response(new Response(request->getHttpVersion(), + HttpStatusCode::OK)); + ElementPtr map = Element::createMap(); + map->set("received", Element::create(body->str())); + response->setBodyAsJson(map); + response->finalize(); + return (response); + } +}; + +/// @brief Implementation of the test HttpResponseCreatorFactory. +class TestHttpResponseCreatorFactory : public HttpResponseCreatorFactory { +public: + + /// @brief Creates @ref TestHttpResponseCreator instance. + virtual HttpResponseCreatorPtr create() const { + HttpResponseCreatorPtr response_creator(new TestHttpResponseCreator()); + return (response_creator); + } +}; + +/// @brief Test fixture class for http control sockets. +class HttpControlSocketTest : public ThreadedTest { +public: + void SetUp() override { + SysrepoSetup::cleanSharedMemory(); + } + + void TearDown() override { + SysrepoSetup::cleanSharedMemory(); + if (thread_) { + thread_->join(); + thread_.reset(); + } + // io_service must be stopped after the thread returns, + // otherwise the thread may never return if it is + // waiting for the completion of some asynchronous tasks. + io_service_.stop(); + } + + /// @brief Returns socket URL. + static Url httpSocketUrl() { + ostringstream s; + s << "http://" << SERVER_ADDRESS << ":" << SERVER_PORT << "/"; + return (Url(s.str())); + } + + /// @brief Create configuration of the control socket. + /// + /// @return a pointer to a control socket configuration. + CfgControlSocketPtr createCfgControlSocket() { + CfgControlSocketPtr cfg; + cfg.reset(new CfgControlSocket(CfgControlSocket::Type::HTTP, + "", httpSocketUrl())); + return (cfg); + } + + /// @brief Create the reflecting listener. + void createReflectListener(); + + /// @brief Start listener. + /// + /// Run IO in a thread. + void start() { + thread_.reset(new thread([this]() { + // The thread is ready to go. Signal it to the main + // thread so it can start the actual test. + signalReady(); + // Until stop() is called run IO service. + while (!isStopping()) { + io_service_.run_one(); + } + // Main thread signalled that the thread should + // terminate. Launch any outstanding IO service + // handlers. + io_service_.poll(); + // Nothing more to do. Signal that the thread is + // done so as the main thread can close HTTP + // listener and clean up after the test. + signalStopped(); + })); + + // Main thread waits here for the thread to start. + waitReady(); + + // If the thread is ready to go, start the listener. + if (listener_) { + ASSERT_NO_THROW_LOG(listener_->start()); + } + } + + /// @brief Stop listener. + /// + /// Post an empty action to finish current run_one. + void stop() { + // Notify the thread that it should terminate. + signalStopping(); + // If the thread is blocked on running the IO + // service, post the empty handler to cause + // run_one to return. + io_service_.post([]() { return; }); + // We asked that the thread stops. Let's wait + // for it to signal that it has stopped. + waitStopped(); + + // Thread has terminated. We can stop the HTTP + // listener safely. + if (listener_) { + ASSERT_NO_THROW_LOG(listener_->stop()); + } + } + + /// @brief IOService object. + IOService io_service_; + + /// @brief Pointer to listener. + HttpListenerPtr listener_; +}; + +/// @brief Create the reflecting listener. +void +HttpControlSocketTest::createReflectListener() { + HttpResponseCreatorFactoryPtr + factory(new TestHttpResponseCreatorFactory()); + listener_.reset(new + HttpListener(io_service_, + IOAddress(SERVER_ADDRESS), SERVER_PORT, + TlsContextPtr(), factory, + HttpListener::RequestTimeout(2000), + HttpListener::IdleTimeout(2000))); +} + +// Verifies that the createControlSocket template can create a http +// control socket. +TEST_F(HttpControlSocketTest, createControlSocket) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + ControlSocketBasePtr cs = controlSocketFactory(cfg); + ASSERT_TRUE(cs); + HttpControlSocketPtr hcs = + boost::dynamic_pointer_cast<HttpControlSocket>(cs); + EXPECT_TRUE(hcs); +} + +// Verifies that http control sockets handle configGet() as expected. +TEST_F(HttpControlSocketTest, configGet) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Run a reflecting server in a thread. + createReflectListener(); + start(); + + // Try configGet. + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = hcs->configGet("foo")); + stop(); + + // Check result. + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"command\": \"config-get\", " + "\"remote-address\": \"127.0.0.1\", \"service\": [ \"foo\" ] }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that http control sockets handle configGet() for a control agent +// as expected. +TEST_F(HttpControlSocketTest, configGetCA) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Run a reflecting server in a thread. + createReflectListener(); + start(); + + // Try configGet. + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = hcs->configGet("ca")); + stop(); + + // Check result. + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"command\": \"config-get\", \"remote-address\": \"127.0.0.1\" }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that http control sockets handle configTest() as expected. +TEST_F(HttpControlSocketTest, configTest) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Run a reflecting server in a thread. + createReflectListener(); + start(); + + // Prepare a config to test. + ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configTest. + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = hcs->configTest(json, "foo")); + stop(); + + // Check result. + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"arguments\": { \"bar\": 1 }, " + "\"command\": \"config-test\", " + "\"remote-address\": \"127.0.0.1\", \"service\": [ \"foo\" ] }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that http control sockets handle configTest() for a control agent +// as expected. +TEST_F(HttpControlSocketTest, configTestCA) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Run a reflecting server in a thread. + createReflectListener(); + start(); + + // Prepare a config to test. + ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configTest. + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = hcs->configTest(json, "ca")); + stop(); + + // Check result. + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"arguments\": { \"bar\": 1 }, " + "\"command\": \"config-test\", \"remote-address\": \"127.0.0.1\" }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that http control sockets handle configSet() as expected. +TEST_F(HttpControlSocketTest, configSet) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Run a reflecting server in a thread. + createReflectListener(); + start(); + + // Prepare a config to set. + ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configSet. + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = hcs->configSet(json, "foo")); + stop(); + + // Check result. + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"arguments\": { \"bar\": 1 }, " + "\"command\": \"config-set\", " + "\"remote-address\": \"127.0.0.1\", \"service\": [ \"foo\" ] }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that http control sockets handle configSet() for a control agent +// as expected. +TEST_F(HttpControlSocketTest, configSetCA) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Run a reflecting server in a thread. + createReflectListener(); + start(); + + // Prepare a config to set. + ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configSet. + ConstElementPtr reflected; + EXPECT_NO_THROW(reflected = hcs->configSet(json, "ca")); + stop(); + + // Check result. + ASSERT_TRUE(reflected); + ASSERT_EQ(Element::map, reflected->getType()); + ConstElementPtr command = reflected->get("received"); + ASSERT_TRUE(command); + ASSERT_EQ(Element::string, command->getType()); + string expected = "{ \"arguments\": { \"bar\": 1 }, " + "\"command\": \"config-set\", \"remote-address\": \"127.0.0.1\" }"; + EXPECT_EQ(expected, command->stringValue()); +} + +// Verifies that http control sockets handle can't connect errors. +TEST_F(HttpControlSocketTest, connectionRefused) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Try configGet: it should get a communication error, + try { + hcs->configGet("foo"); + } catch (const ControlSocketError& ex) { + EXPECT_EQ("communication error (code): Connection refused", + string(ex.what())); + } catch (const std::exception& ex) { + FAIL() << "unexpected exception: " << ex.what(); + } catch (...) { + FAIL() << "unexpected exception"; + } +} + +// Verifies that http control sockets handle timeout errors. +TEST_F(HttpControlSocketTest, partial) { + CfgControlSocketPtr cfg = createCfgControlSocket(); + ASSERT_TRUE(cfg); + HttpControlSocketPtr hcs(new HttpControlSocket(cfg)); + ASSERT_TRUE(hcs); + + // Create the server but do not start it. + createReflectListener(); + start(); + + // Prepare a special config to set. + ConstElementPtr json = Element::fromJSON("{ \"want-partial\": true }"); + + // Warn this makes time. + cout << "this test waits for 2 seconds" << endl; + + // Try configSet: it should get a communication error, + try { + hcs->configSet(json, "foo"); + } catch (const ControlSocketError& ex) { + EXPECT_EQ("communication error (code): End of file", + string(ex.what())); + } catch (const std::exception& ex) { + FAIL() << "unexpected exception: " << ex.what(); + } catch (...) { + FAIL() << "unexpected exception"; + } + stop(); +} + +} // namespace diff --git a/src/bin/netconf/tests/get_config_unittest.cc b/src/bin/netconf/tests/get_config_unittest.cc new file mode 100644 index 0000000..fd5c2e6 --- /dev/null +++ b/src/bin/netconf/tests/get_config_unittest.cc @@ -0,0 +1,294 @@ +// Copyright (C) 2018-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 <cc/data.h> +#include <cc/command_interpreter.h> +#include <testutils/gtest_utils.h> +#include <testutils/user_context_utils.h> +#include <process/testutils/d_test_stubs.h> +#include <netconf/netconf_cfg_mgr.h> +#include <netconf/parser_context.h> + +#include <boost/scoped_ptr.hpp> + +#include <gtest/gtest.h> + +#include <iostream> +#include <fstream> +#include <string> +#include <sstream> + +#include "test_data_files_config.h" +#include "test_libraries.h" + +using namespace isc::netconf; +using namespace isc::config; +using namespace isc::data; +using namespace isc::process; +using namespace isc::test; + +namespace { + +/// @name How to generate the testdata/get_config.json file +/// +/// Define GENERATE_ACTION and recompile. Run netconf_unittests on +/// NetconfGetCfgTest redirecting the standard error to a temporary +/// file, e.g. by +/// @code +/// ./netconf_unittests --gtest_filter="NetconfGetCfg*" > /dev/null 2> u +/// @endcode +/// +/// Update testdata/get_config.json using the temporary file content, +/// (removing head comment and restoring hook library path), +/// recompile without GENERATE_ACTION. + +/// @brief the generate action +/// false means do nothing, true means unparse extracted configurations +#ifdef GENERATE_ACTION +const bool generate_action = true; +#else +const bool generate_action = false; +#endif + +/// @brief Read a file into a string +std::string +readFile(const std::string& file_path) { + std::ifstream ifs(file_path); + if (!ifs.is_open()) { + ADD_FAILURE() << "readFile cannot open " << file_path; + isc_throw(isc::Unexpected, "readFile cannot open " << file_path); + } + std::string lines; + std::string line; + while (std::getline(ifs, line)) { + lines += line + "\n"; + } + ifs.close(); + return (lines); +} + +/// @brief Runs parser in JSON mode +ElementPtr +parseJSON(const std::string& in, bool verbose = false) { + try { + ParserContext ctx; + return (ctx.parseString(in, ParserContext::PARSER_JSON)); + } catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +/// @brief Runs parser in NETCONF mode +ElementPtr +parseNETCONF(const std::string& in, bool verbose = false) { + try { + ParserContext ctx; + return (ctx.parseString(in, ParserContext::PARSER_NETCONF)); + } catch (const std::exception& ex) { + if (verbose) { + std::cout << "EXCEPTION: " << ex.what() << std::endl; + } + throw; + } +} + +/// @brief Replace the library path +void +pathReplacer(ConstElementPtr netconf_cfg) { + ConstElementPtr hooks_libs = netconf_cfg->get("hooks-libraries"); + if (!hooks_libs || hooks_libs->empty()) { + return; + } + ElementPtr first_lib = hooks_libs->getNonConst(0); + std::string lib_path(BASIC_CALLOUT_LIBRARY); + first_lib->set("library", Element::create(lib_path)); +} + +/// @brief Almost regular netconf CfgMgr with internal parse method exposed. +class NakedNetconfCfgMgr : public NetconfCfgMgr { +public: + using NetconfCfgMgr::parse; +}; + +} // namespace + +/// @brief Test fixture class +class NetconfGetCfgTest : public ConfigParseTest { +public: + NetconfGetCfgTest() + : rcode_(-1) { + srv_.reset(new NakedNetconfCfgMgr()); + // Create fresh context. + resetConfiguration(); + } + + ~NetconfGetCfgTest() { + resetConfiguration(); + } + + /// @brief Parse and Execute configuration + /// + /// Parses a configuration and executes a configuration of the server. + /// If the operation fails, the current test will register a failure. + /// + /// @param config Configuration to parse + /// @param operation Operation being performed. In the case of an error, + /// the error text will include the string "unable to <operation>.". + /// + /// @return true if the configuration succeeded, false if not. + bool + executeConfiguration(const std::string& config, const char* operation) { + // try JSON parser + ConstElementPtr json; + try { + json = parseJSON(config, true); + } catch (const std::exception& ex) { + ADD_FAILURE() << "invalid JSON for " << operation + << " failed with " << ex.what() + << " on\n" << config << "\n"; + return (false); + } + + // try NETCONF parser + try { + json = parseNETCONF(config, true); + } catch (...) { + ADD_FAILURE() << "parsing failed for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // get Netconf element + ConstElementPtr ca = json->get("Netconf"); + if (!ca) { + ADD_FAILURE() << "cannot get Netconf for " << operation + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // update hooks-libraries + pathReplacer(ca); + + // try NETCONF configure + ConstElementPtr status; + try { + status = srv_->parse(ca, true); + } catch (const std::exception& ex) { + ADD_FAILURE() << "configure for " << operation + << " failed with " << ex.what() + << " on\n" << prettyPrint(json) << "\n"; + return (false); + } + + // The status object must not be NULL + if (!status) { + ADD_FAILURE() << "configure for " << operation + << " returned null on\n" + << prettyPrint(json) << "\n"; + return (false); + } + + // Returned value should be 0 (configuration success) + comment_ = parseAnswer(rcode_, status); + if (rcode_ != 0) { + string reason = ""; + if (comment_) { + reason = string(" (") + comment_->stringValue() + string(")"); + } + ADD_FAILURE() << "configure for " << operation + << " returned error code " + << rcode_ << reason << " on\n" + << prettyPrint(json) << "\n"; + return (false); + } + return (true); + } + + /// @brief Reset configuration database. + /// + /// This function resets configuration data base by + /// removing managed servers and hooks. Reset must + /// be performed after each test to make sure that + /// contents of the database do not affect result of + /// subsequent tests. + void resetConfiguration() { + string config = "{ \"Netconf\": { } }"; + EXPECT_TRUE(executeConfiguration(config, "reset config")); + } + + boost::scoped_ptr<NakedNetconfCfgMgr> srv_; ///< Netconf server under test + int rcode_; ///< Return code from element parsing + ConstElementPtr comment_; ///< Reason for parse fail +}; + +// Test a simple configuration. +TEST_F(NetconfGetCfgTest, simple) { + + // get the simple configuration + std::string simple_file = string(CFG_EXAMPLES) + "/" + "simple-dhcp4.json"; + std::string config; + ASSERT_NO_THROW_LOG(config = readFile(simple_file)); + + // get the expected configuration + std::string expected_file = + std::string(NETCONF_TEST_DATA_DIR) + "/" + "get_config.json"; + std::string expected; + ASSERT_NO_THROW_LOG(expected = readFile(expected_file)); + + // execute the sample configuration + ASSERT_TRUE(executeConfiguration(config, "simple config")); + + // unparse it + NetconfConfigPtr context = srv_->getNetconfConfig(); + ConstElementPtr unparsed; + ASSERT_NO_THROW_LOG(unparsed = context->toElement()); + + // dump if wanted else check + if (generate_action) { + std::cerr << "// Generated Configuration (remove this line)\n"; + ASSERT_NO_THROW_LOG(expected = prettyPrint(unparsed)); + prettyPrint(unparsed, std::cerr, 0, 4); + std::cerr << "\n"; + } else { + // get the expected config using the netconf syntax parser + ElementPtr jsond; + ASSERT_NO_THROW_LOG(jsond = parseNETCONF(expected, true)); + // get the expected config using the generic JSON syntax parser + ElementPtr jsonj; + ASSERT_NO_THROW_LOG(jsonj = parseJSON(expected)); + // the generic JSON parser does not handle comments + EXPECT_TRUE(isEquivalent(jsond, moveComments(jsonj))); + // replace the path by its actual value + ConstElementPtr ca; + ASSERT_NO_THROW_LOG(ca = jsonj->get("Netconf")); + ASSERT_TRUE(ca); + pathReplacer(ca); + // check that unparsed and updated expected values match + EXPECT_TRUE(isEquivalent(unparsed, jsonj)); + // check on pretty prints too + std::string current = prettyPrint(unparsed, 0, 4); + std::string expected2 = prettyPrint(jsonj, 0, 4); + EXPECT_EQ(expected2, current); + if (expected2 != current) { + expected = current + "\n"; + } + } + + // execute the netconft configuration + EXPECT_TRUE(executeConfiguration(expected, "unparsed config")); + + // is it a fixed point? + NetconfConfigPtr context2 = srv_->getNetconfConfig(); + ConstElementPtr unparsed2; + ASSERT_NO_THROW_LOG(unparsed2 = context2->toElement()); + ASSERT_TRUE(unparsed2); + EXPECT_TRUE(isEquivalent(unparsed, unparsed2)); +} diff --git a/src/bin/netconf/tests/netconf_cfg_mgr_unittests.cc b/src/bin/netconf/tests/netconf_cfg_mgr_unittests.cc new file mode 100644 index 0000000..1ec7b87 --- /dev/null +++ b/src/bin/netconf/tests/netconf_cfg_mgr_unittests.cc @@ -0,0 +1,738 @@ +// Copyright (C) 2018-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 <netconf/netconf_cfg_mgr.h> +#include <netconf/parser_context.h> +#include <exceptions/exceptions.h> +#include <cc/command_interpreter.h> +#include <process/testutils/d_test_stubs.h> +#include <process/d_cfg_mgr.h> +#include <yang/yang_models.h> +#include <testutils/gtest_utils.h> +#include <testutils/test_to_element.h> +#include <netconf/tests/test_libraries.h> + +#include <boost/scoped_ptr.hpp> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::netconf; +using namespace isc::config; +using namespace isc::data; +using namespace isc::hooks; +using namespace isc::http; +using namespace isc::process; +using namespace isc::yang; + +namespace { + +/// @brief Almost regular netconf CfgMgr with internal parse method exposed. +class NakedNetconfCfgMgr : public NetconfCfgMgr { +public: + using NetconfCfgMgr::parse; +}; + +// Tests construction of NetconfCfgMgr class. +TEST(NetconfCfgMgr, construction) { + boost::scoped_ptr<NetconfCfgMgr> cfg_mgr; + + // Verify that configuration manager constructions without error. + ASSERT_NO_THROW_LOG(cfg_mgr.reset(new NetconfCfgMgr())); + + // Verify that the context can be retrieved and is not null. + NetconfConfigPtr context; + ASSERT_NO_THROW_LOG(context = cfg_mgr->getNetconfConfig()); + EXPECT_TRUE(context); + + // Verify that the manager can be destructed without error. + EXPECT_NO_THROW(cfg_mgr.reset()); +} + +// Tests if getContext can be retrieved. +TEST(NetconfCfgMgr, getContext) { + NetconfCfgMgr cfg_mgr; + + NetconfConfigPtr ctx; + ASSERT_NO_THROW_LOG(ctx = cfg_mgr.getNetconfConfig()); + ASSERT_TRUE(ctx); +} + +// Tests if context can store and retrieve managed server information. +TEST(NetconfCfgMgr, contextServer) { + + NetconfConfig ctx; + + // Check managed server parameters. + // By default, there are no server stored. + ASSERT_TRUE(ctx.getCfgServersMap()); + EXPECT_EQ(0, ctx.getCfgServersMap()->size()); + + CfgControlSocketPtr + socket1(new CfgControlSocket(CfgControlSocket::Type::UNIX, + "socket1", + Url("http://127.0.0.1:8000/"))); + CfgServerPtr server1(new CfgServer("model1", socket1)); + CfgControlSocketPtr + socket2(new CfgControlSocket(CfgControlSocket::Type::UNIX, + "socket2", + Url("http://127.0.0.1:8000/"))); + CfgServerPtr server2(new CfgServer("model2", socket2)); + CfgControlSocketPtr + socket3(new CfgControlSocket(CfgControlSocket::Type::UNIX, + "socket3", + Url("http://127.0.0.1:8000/"))); + CfgServerPtr server3(new CfgServer("model3", socket3)); + CfgControlSocketPtr + socket4(new CfgControlSocket(CfgControlSocket::Type::UNIX, + "socket4", + Url("http://127.0.0.1:8000/"))); + CfgServerPtr server4(new CfgServer("model4", socket4)); + + // Ok, now set the server for D2 + EXPECT_NO_THROW(ctx.getCfgServersMap()->insert(make_pair("d2", server1))); + + // Now check the values returned + EXPECT_EQ(1, ctx.getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx.getCfgServersMap()->at("d2")); + EXPECT_EQ(server1, ctx.getCfgServersMap()->at("d2")); + EXPECT_THROW(ctx.getCfgServersMap()->at("dhcp4"), std::out_of_range); + + // Now set the v6 server and sanity check again + EXPECT_NO_THROW(ctx.getCfgServersMap()->insert(make_pair("dhcp6", server2))); + + // Should be possible to retrieve two servers + EXPECT_EQ(2, ctx.getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx.getCfgServersMap()->at("dhcp6")); + EXPECT_EQ(server1, ctx.getCfgServersMap()->at("d2")); + EXPECT_EQ(server2, ctx.getCfgServersMap()->at("dhcp6")); + + // Finally, set all servers. + EXPECT_NO_THROW(ctx.getCfgServersMap()->insert(make_pair("dhcp4", server3))); + EXPECT_NO_THROW(ctx.getCfgServersMap()->insert(make_pair("ca", server4))); + EXPECT_EQ(4, ctx.getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx.getCfgServersMap()->at("dhcp4")); + ASSERT_NO_THROW_LOG(ctx.getCfgServersMap()->at("ca")); + EXPECT_EQ(server3, ctx.getCfgServersMap()->at("dhcp4")); + EXPECT_EQ(server4, ctx.getCfgServersMap()->at("ca")); +} + +// Tests if the context can store and retrieve hook libs information. +TEST(NetconfCfgMgr, contextHookParams) { + NetconfConfig ctx; + + // By default there should be no hooks. + HooksConfig& libs = ctx.getHooksConfig(); + EXPECT_TRUE(libs.get().empty()); + + libs.add("libone.so", ConstElementPtr()); + libs.add("libtwo.so", Element::fromJSON("{\"foo\": true}")); + libs.add("libthree.so", Element::fromJSON("{\"bar\": 42}")); + + const HooksConfig& stored_libs = ctx.getHooksConfig(); + EXPECT_EQ(3, stored_libs.get().size()); + + EXPECT_EQ(libs.get(), stored_libs.get()); +} + +// Tests if the context can store and retrieve globals. +TEST(NetconfCfgMgr, contextGlobals) { + NetconfConfig ctx; + + // By default there should be no globals. + ConstElementPtr globals = ctx.getConfiguredGlobals(); + ASSERT_TRUE(globals); + ASSERT_EQ(Element::map, globals->getType()); + EXPECT_EQ(0, globals->mapValue().size()); + + // Attempting to extract globals from a non-map should throw. + ASSERT_THROW(ctx.extractConfiguredGlobals(Element::create(777)), BadValue); + + // Now let's create a configuration from which to extract global scalars. + // Extraction (currently) has no business logic, so the elements we use + // can be arbitrary. + ConstElementPtr global_cfg; + string global_cfg_str = + "{\n" + " \"astring\": \"okay\",\n" + " \"amap\": { \"not-this\":777, \"not-that\": \"poo\" },\n" + " \"anint\": 444,\n" + " \"alist\": [ 1, 2, 3 ],\n" + " \"abool\": true\n" + "}\n"; + ASSERT_NO_THROW_LOG(global_cfg = Element::fromJSON(global_cfg_str)); + + // Extract globals from the config. + ASSERT_NO_THROW_LOG(ctx.extractConfiguredGlobals(global_cfg)); + + // Now see if the extract was correct. + globals = ctx.getConfiguredGlobals(); + ASSERT_TRUE(globals); + ASSERT_EQ(Element::map, globals->getType()); + EXPECT_NE(0, globals->mapValue().size()); + + // Maps and lists should be excluded. + for (auto it : globals->mapValue()) { + if (it.first == "astring") { + ASSERT_EQ(Element::string, it.second->getType()); + EXPECT_EQ("okay", it.second->stringValue()); + } else if (it.first == "anint") { + ASSERT_EQ(Element::integer, it.second->getType()); + EXPECT_EQ(444, it.second->intValue()); + } else if (it.first == "abool") { + ASSERT_EQ(Element::boolean, it.second->getType()); + EXPECT_TRUE(it.second->boolValue()); + } else { + ADD_FAILURE() << "unexpected element found:" << it.first; + } + } +} + +/// @brief Netconf configurations used in tests. +const char* NETCONF_CONFIGS[] = { + + // configuration 0: empty (nothing specified) + "{ }", + + // Configuration 1: global parameters only (no server, not hooks) + "{\n" + " \"boot-update\": false,\n" + " \"subscribe-changes\": false,\n" + " \"validate-changes\": false\n" + "}", + + // Configuration 2: 1 server + "{\n" + " \"boot-update\": false,\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"boot-update\": true,\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-v4\"\n" + " }\n" + " }\n" + " }\n" + "}", + + // Configuration 3: all 4 servers + "{\n" + " \"boot-update\": false,\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"boot-update\": true,\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-v4\"\n" + " }\n" + " },\n" + " \"dhcp6\": {\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-v6\"\n" + " }\n" + " },\n" + " \"d2\": {\n" + " \"subscribe-changes\": false,\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-d2\"\n" + " }\n" + " },\n" + " \"ca\": {\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-ca\"\n" + " }\n" + " }\n" + " }\n" + "}", + + // Configuration 4: 1 server and hooks + // Netconf is able to load hook libraries that augment its operation. + // The primary functionality is the ability to add new commands. + "{\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-v4\"\n" + " }\n" + " }\n" + " },\n" + " \"hooks-libraries\": [" + " {" + " \"library\": \"%LIBRARY%\"," + " \"parameters\": {\n" + " \"param1\": \"foo\"\n" + " }\n" + " }\n" + " ]\n" + "}", + + // Configuration 5: 1 server (d2 only) + "{\n" + " \"managed-servers\": {\n" + " \"d2\": {\n" + " \"subscribe-changes\": false,\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-d2\"\n" + " }\n" + " }\n" + " }\n" + "}", + + // Configuration 6: 1 server (dhcp6 only) + "{\n" + " \"managed-servers\": {\n" + " \"dhcp6\": {\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-v6\"\n" + " }\n" + " }\n" + " }\n" + "}", + + // Configuration 7: 2 servers with user contexts and comments + "{\n" + " \"user-context\": { \"comment\": \"Indirect comment\" },\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"comment\": \"dhcp4 server\",\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-v4\"\n" + " }\n" + " },\n" + " \"dhcp6\": {\n" + " \"control-socket\": {\n" + " \"socket-name\": \"/tmp/socket-v6\",\n" + " \"user-context\": { \"version\": 1 }\n" + " }\n" + " }\n" + " }\n" + "}", + + // Configuration 8: empty server with no control socket + "{\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"comment\": \"empty map not allowed\"\n" + " }\n" + " }\n" + "}", + + // Configuration 9: empty control socket + "{\n" + " \"boot-update\": false,\n" + " \"subscribe-changes\": false,\n" + " \"validate-changes\": false,\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"comment\": \"empty map not allowed\"\n" + " }\n" + " }\n" + " }\n" + "}", + + // Configuration 10: bad socket type + "{\n" + " \"managed-servers\": {\n" + " \"dhcp6\": {\n" + " \"control-socket\": {\n" + " \"socket-type\": \"tcp\"\n" + " }\n" + " }\n" + " }\n" + "}", + + // Configuration 11: invalid socket Url + "{\n" + " \"managed-servers\": {\n" + " \"dhcp6\": {\n" + " \"control-socket\": {\n" + " \"socket-url\": \"bad\"\n" + " }\n" + " }\n" + " }\n" + "}" +}; + +// Tests the handling of bad socket type. Can't use the fixture class +// because the Netconf parser does not allow bad socket types. +TEST(NetconfParser, badSocketType) { + ConstElementPtr json; + ParserContext parser; + EXPECT_NO_THROW(json = parser.parseString(NETCONF_CONFIGS[10], + ParserContext::PARSER_JSON)); + ConstElementPtr answer; + NakedNetconfCfgMgr cfg_mgr; + EXPECT_NO_THROW(answer = cfg_mgr.parse(json, false)); + int rcode = 0; + string expected = + "\"Unknown control socket type: tcp 'tcp' (<string>:5:32)\""; + EXPECT_EQ(expected, parseAnswer(rcode, answer)->str()); + EXPECT_EQ(1, rcode); +} + +/// @brief Class used for testing CfgMgr +class NetconfParserTest : public isc::process::ConfigParseTest { +public: + + /// @brief Tries to load input text as a configuration + /// + /// @param config text containing input configuration + /// @param expected_answer expected result of configuration (0 = success) + void configParse(const char* config, int expected_answer) { + isc::netconf::ParserContext parser; + ConstElementPtr json = parser.parseString(config, ParserContext::PARSER_SUB_NETCONF); + + EXPECT_NO_THROW(answer_ = cfg_mgr_.parse(json, false)); + EXPECT_TRUE(checkAnswer(expected_answer)); + } + + /// @brief Replaces %LIBRARY% with specified library name + /// + /// @param config input config text (should contain "%LIBRARY%" string) + /// @param lib_name %LIBRARY% will be replaced with that name + /// @return configuration text with library name replaced + string pathReplacer(const char* config, const char* lib_name) { + string txt(config); + txt.replace(txt.find("%LIBRARY%"), strlen("%LIBRARY%"), string(lib_name)); + return (txt); + } + + /// Configuration Manager (used in tests) + NakedNetconfCfgMgr cfg_mgr_; +}; + +// This test verifies if an empty config is handled properly. In practice such +// a config makes little sense, but perhaps it's ok for a default deployment. +TEST_F(NetconfParserTest, configParseEmpty) { + configParse(NETCONF_CONFIGS[0], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(0, ctx->getCfgServersMap()->size()); +} + +// This test verifies if a config with only globals is handled properly. +TEST_F(NetconfParserTest, configParseGlobalOnly) { + configParse(NETCONF_CONFIGS[1], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(0, ctx->getCfgServersMap()->size()); + ConstElementPtr globals = ctx->getConfiguredGlobals(); + ASSERT_TRUE(globals); + string expected = "{ " + "\"boot-update\": false, " + "\"subscribe-changes\": false, " + "\"validate-changes\": false }"; + EXPECT_EQ(expected, globals->str()); +} + +// Tests if an empty (i.e. without a control socket) can be configured. +// Note that the syntax required the server map to not be really empty. +TEST_F(NetconfParserTest, configParseEmptyCfgServer) { + configParse(NETCONF_CONFIGS[8], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(1, ctx->getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("dhcp4")); + CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel()); + // Defaults. + EXPECT_TRUE(server->getBootUpdate()); + EXPECT_TRUE(server->getSubscribeChanges()); + EXPECT_TRUE(server->getValidateChanges()); + CfgControlSocketPtr socket = server->getCfgControlSocket(); + EXPECT_FALSE(socket); +} + +// This tests default values using a server with empty control socket +// Note that the syntax required the control socket map to not be really empty. +TEST_F(NetconfParserTest, configParseDefaults) { + configParse(NETCONF_CONFIGS[9], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(1, ctx->getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("dhcp4")); + CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel()); + // Globals overwrite defaults. + EXPECT_FALSE(server->getBootUpdate()); + EXPECT_FALSE(server->getSubscribeChanges()); + EXPECT_FALSE(server->getValidateChanges()); + CfgControlSocketPtr socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + + // Checking default. + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); +} + +// Tests if a single DHCPv4 server can be configured. +TEST_F(NetconfParserTest, configParseServerDhcp4) { + configParse(NETCONF_CONFIGS[2], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(1, ctx->getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("dhcp4")); + CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel()); + // Locals overwrite globals. + EXPECT_TRUE(server->getBootUpdate()); + EXPECT_TRUE(server->getSubscribeChanges()); + EXPECT_TRUE(server->getValidateChanges()); + CfgControlSocketPtr socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("/tmp/socket-v4", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); +} + +// Tests if a single D2 server can be configured. +TEST_F(NetconfParserTest, configParseServerD2) { + configParse(NETCONF_CONFIGS[5], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(1, ctx->getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("d2")); + CfgServerPtr server = ctx->getCfgServersMap()->at("d2"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP_DDNS, server->getModel()); + EXPECT_TRUE(server->getBootUpdate()); + EXPECT_FALSE(server->getSubscribeChanges()); + EXPECT_TRUE(server->getValidateChanges()); + CfgControlSocketPtr socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("/tmp/socket-d2", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); +} + +// Tests if a single DHCPv6 server can be configured. +TEST_F(NetconfParserTest, configParseServerDhcp6) { + configParse(NETCONF_CONFIGS[6], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(1, ctx->getCfgServersMap()->size()); + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("dhcp6")); + CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp6"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP6_SERVER, server->getModel()); + CfgControlSocketPtr socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("/tmp/socket-v6", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); +} + +// This tests if all 4 servers can be configured and makes sure the parser +// doesn't confuse them. +TEST_F(NetconfParserTest, configParse4Servers) { + configParse(NETCONF_CONFIGS[3], 0); + + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(ctx); + ASSERT_TRUE(ctx->getCfgServersMap()); + EXPECT_EQ(4, ctx->getCfgServersMap()->size()); + + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("dhcp4")); + CfgServerPtr server = ctx->getCfgServersMap()->at("dhcp4"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP4_SERVER, server->getModel()); + EXPECT_TRUE(server->getBootUpdate()); + EXPECT_TRUE(server->getSubscribeChanges()); + EXPECT_TRUE(server->getValidateChanges()); + CfgControlSocketPtr socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("/tmp/socket-v4", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); + + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("dhcp6")); + server = ctx->getCfgServersMap()->at("dhcp6"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP6_SERVER, server->getModel()); + socket = server->getCfgControlSocket(); + EXPECT_FALSE(server->getBootUpdate()); + EXPECT_TRUE(server->getSubscribeChanges()); + EXPECT_TRUE(server->getValidateChanges()); + ASSERT_TRUE(socket); + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("/tmp/socket-v6", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); + + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("d2")); + server = ctx->getCfgServersMap()->at("d2"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_DHCP_DDNS, server->getModel()); + EXPECT_FALSE(server->getBootUpdate()); + EXPECT_FALSE(server->getSubscribeChanges()); + EXPECT_TRUE(server->getValidateChanges()); + socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("/tmp/socket-d2", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); + + ASSERT_NO_THROW_LOG(ctx->getCfgServersMap()->at("ca")); + server = ctx->getCfgServersMap()->at("ca"); + ASSERT_TRUE(server); + EXPECT_EQ(KEA_CTRL_AGENT, server->getModel()); + EXPECT_FALSE(server->getBootUpdate()); + EXPECT_TRUE(server->getSubscribeChanges()); + EXPECT_TRUE(server->getValidateChanges()); + socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + EXPECT_EQ(CfgControlSocket::Type::STDOUT, socket->getType()); + EXPECT_EQ("/tmp/socket-ca", socket->getName()); + EXPECT_EQ("http://127.0.0.1:8000/", socket->getUrl().toText()); + + // Check unparsing. + string expected = "{\n" + " \"Netconf\": {\n" + " \"boot-update\": false,\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"model\": \"kea-dhcp4-server\",\n" + " \"boot-update\": true,\n" + " \"subscribe-changes\": true,\n" + " \"validate-changes\": true,\n" + " \"control-socket\": {\n" + " \"socket-type\": \"stdout\",\n" + " \"socket-name\": \"/tmp/socket-v4\",\n" + " \"socket-url\": \"http://127.0.0.1:8000/\"\n" + " }\n" + " },\n" + " \"dhcp6\": {\n" + " \"model\": \"kea-dhcp6-server\",\n" + " \"boot-update\": false,\n" + " \"subscribe-changes\": true,\n" + " \"validate-changes\": true,\n" + " \"control-socket\": {\n" + " \"socket-type\": \"stdout\",\n" + " \"socket-name\": \"/tmp/socket-v6\",\n" + " \"socket-url\": \"http://127.0.0.1:8000/\"\n" + " }\n" + " },\n" + " \"d2\": {\n" + " \"model\": \"kea-dhcp-ddns\",\n" + " \"boot-update\": false,\n" + " \"subscribe-changes\": false,\n" + " \"validate-changes\": true,\n" + " \"control-socket\": {\n" + " \"socket-type\": \"stdout\",\n" + " \"socket-name\": \"/tmp/socket-d2\",\n" + " \"socket-url\": \"http://127.0.0.1:8000/\"\n" + " }\n" + " },\n" + " \"ca\": {\n" + " \"model\": \"kea-ctrl-agent\",\n" + " \"boot-update\": false,\n" + " \"subscribe-changes\": true,\n" + " \"validate-changes\": true,\n" + " \"control-socket\": {\n" + " \"socket-type\": \"stdout\",\n" + " \"socket-name\": \"/tmp/socket-ca\",\n" + " \"socket-url\": \"http://127.0.0.1:8000/\"\n" + " }\n" + " }\n" + " },\n" + " \"hooks-libraries\": [ ]\n" + " }\n" + "}"; + isc::test::runToElementTest<NetconfConfig>(expected, *ctx); +} + +// Tests the handling of invalid socket URL. +TEST_F(NetconfParserTest, configParseInvalidSocketUrl) { + configParse(NETCONF_CONFIGS[11], 1); + int rcode = 0; + string expected = + "\"invalid control socket url: url bad lacks http or https scheme " + "'bad' (<string>:5:31)\""; + EXPECT_EQ(expected, parseAnswer(rcode, answer_)->str()); +} + +// This test checks that the config file with hook library specified can be +// loaded. This one is a bit tricky, because the parser sanity checks the lib +// name. In particular, it checks if such a library exists. Therefore we +// can't use NETCONF_CONFIGS[4] as is, but need to run it through path replacer. +TEST_F(NetconfParserTest, configParseHooks) { + // Create the configuration with proper lib path. + string cfg = pathReplacer(NETCONF_CONFIGS[4], BASIC_CALLOUT_LIBRARY); + // The configuration should be successful. + configParse(cfg.c_str(), 0); + + // The context now should have the library specified. + NetconfConfigPtr ctx = cfg_mgr_.getNetconfConfig(); + const HookLibsCollection libs = ctx->getHooksConfig().get(); + ASSERT_EQ(1, libs.size()); + EXPECT_EQ(string(BASIC_CALLOUT_LIBRARY), libs[0].first); + ASSERT_TRUE(libs[0].second); + EXPECT_EQ("{ \"param1\": \"foo\" }", libs[0].second->str()); +} + +// This test checks comments. +TEST_F(NetconfParserTest, comments) { + configParse(NETCONF_CONFIGS[7], 0); + NetconfConfigPtr netconf_ctx = cfg_mgr_.getNetconfConfig(); + ASSERT_TRUE(netconf_ctx); + + // Check global user context. + ConstElementPtr ctx = netconf_ctx->getContext(); + ASSERT_TRUE(ctx); + ASSERT_EQ(1, ctx->size()); + ASSERT_TRUE(ctx->get("comment")); + EXPECT_EQ("\"Indirect comment\"", ctx->get("comment")->str()); + + // There is a DHCP4 server. + ASSERT_TRUE(netconf_ctx->getCfgServersMap()); + ASSERT_NO_THROW_LOG(netconf_ctx->getCfgServersMap()->at("dhcp4")); + CfgServerPtr server = netconf_ctx->getCfgServersMap()->at("dhcp4"); + ASSERT_TRUE(server); + + // Check DHCP4 server user context. + ConstElementPtr ctx4 = server->getContext(); + ASSERT_TRUE(ctx4); + ASSERT_EQ(1, ctx4->size()); + ASSERT_TRUE(ctx4->get("comment")); + EXPECT_EQ("\"dhcp4 server\"", ctx4->get("comment")->str()); + + // There is a DHCP6 server. + ASSERT_NO_THROW_LOG(netconf_ctx->getCfgServersMap()->at("dhcp6")); + server = netconf_ctx->getCfgServersMap()->at("dhcp6"); + ASSERT_TRUE(server); + + // There is a DHCP6 control socket. + CfgControlSocketPtr socket = server->getCfgControlSocket(); + ASSERT_TRUE(socket); + + // Check DHCP6 control socket user context. + ConstElementPtr ctx6 = socket->getContext(); + ASSERT_TRUE(ctx6); + ASSERT_EQ(1, ctx6->size()); + ASSERT_TRUE(ctx6->get("version")); + EXPECT_EQ("1", ctx6->get("version")->str()); +} + +} // namespace diff --git a/src/bin/netconf/tests/netconf_controller_unittests.cc b/src/bin/netconf/tests/netconf_controller_unittests.cc new file mode 100644 index 0000000..dd4cb85 --- /dev/null +++ b/src/bin/netconf/tests/netconf_controller_unittests.cc @@ -0,0 +1,194 @@ +// Copyright (C) 2018-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 <asiolink/testutils/timed_signal.h> +#include <netconf/netconf_controller.h> +#include <netconf/netconf_process.h> +#include <cc/data.h> +#include <process/testutils/d_test_stubs.h> +#include <testutils/gtest_utils.h> + +#include <boost/pointer_cast.hpp> + +#include <sstream> + +using namespace isc::asiolink::test; +using namespace isc::netconf; +using namespace isc::data; +using namespace isc::http; +using namespace isc::process; +using namespace boost::posix_time; +using namespace std; + +namespace { + +/// @brief Valid Netconf Config used in tests. +const char* valid_netconf_config = + "{" + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"managed-servers\": {" + " \"dhcp4\": {" + " \"control-socket\": {" + " \"socket-type\": \"unix\"," + " \"socket-name\": \"/first/dhcp4/socket\"" + " }" + " }," + " \"dhcp6\": {" + " \"control-socket\": {" + " \"socket-type\": \"unix\"," + " \"socket-name\": \"/first/dhcp6/socket\"" + " }" + " }" + " }" + "}"; + +/// @brief test fixture class for testing NetconfController class. +/// +/// This class derives from DControllerTest and wraps NetconfController. Much +/// of the underlying functionality is in the DControllerBase class which +/// has extensive set of unit tests that are independent from Netconf. +class NetconfControllerTest : public DControllerTest { +public: + + /// @brief Constructor. + NetconfControllerTest() + : DControllerTest(NetconfController::instance) { + } + + /// @brief Returns pointer to NetconfProcess instance. + NetconfProcessPtr getNetconfProcess() { + return (boost::dynamic_pointer_cast<NetconfProcess>(getProcess())); + } + + /// @brief Returns pointer to NetconfCfgMgr instance for a process. + NetconfCfgMgrPtr getNetconfCfgMgr() { + NetconfCfgMgrPtr p; + if (getNetconfProcess()) { + p = getNetconfProcess()->getNetconfCfgMgr(); + } + return (p); + } + + /// @brief Returns a pointer to the configuration context. + NetconfConfigPtr getNetconfConfig() { + NetconfConfigPtr p; + if (getNetconfCfgMgr()) { + p = getNetconfCfgMgr()->getNetconfConfig(); + } + return (p); + } +}; + +// Basic Controller instantiation testing. +// Verifies that the controller singleton gets created and that the +// basic derivation from the base class is intact. +TEST_F(NetconfControllerTest, basicInstanceTesting) { + // Verify the singleton instance can be fetched and that + // it has the correct type. + DControllerBasePtr& controller = DControllerTest::getController(); + ASSERT_TRUE(controller); + ASSERT_NO_THROW_LOG(boost::dynamic_pointer_cast<NetconfController>(controller)); + + // Verify that controller's app name is correct. + EXPECT_TRUE(checkAppName(NetconfController::netconf_app_name_)); + + // Verify that controller's bin name is correct. + EXPECT_TRUE(checkBinName(NetconfController::netconf_bin_name_)); + + // Verify that controller's IOService exists. + EXPECT_TRUE(checkIOService()); + + // Verify that the Process does NOT exist. + EXPECT_FALSE(checkProcess()); +} + + +// Tests basic command line processing. +// Verifies that: +// 1. Standard command line options are supported. +// 2. Invalid options are detected. +TEST_F(NetconfControllerTest, commandLineArgs) { + char* argv[] = { const_cast<char*>("progName"), + const_cast<char*>("-c"), + const_cast<char*>(DControllerTest::CFG_TEST_FILE), + const_cast<char*>("-d") }; + int argc = 4; + + // Verify that verbose flag is false initially. + EXPECT_TRUE(checkVerbose(false)); + + // Verify that standard options can be parsed without error. + EXPECT_NO_THROW(parseArgs(argc, argv)); + + // Verify that verbose flag is true. + EXPECT_TRUE(checkVerbose(true)); + + // Verify configuration file name is correct. + EXPECT_TRUE(checkConfigFileName(DControllerTest::CFG_TEST_FILE)); + + // Verify that an unknown option is detected. + char* argv2[] = { const_cast<char*>("progName"), + const_cast<char*>("-x") }; + argc = 2; + EXPECT_THROW(parseArgs(argc, argv2), InvalidUsage); +} + +// Tests application process creation and initialization. +// Verifies that the process can be successfully created and initialized. +TEST_F(NetconfControllerTest, initProcessTesting) { + ASSERT_NO_THROW_LOG(initProcess()); + EXPECT_TRUE(checkProcess()); +} + +// Tests launch and normal shutdown (stand alone mode). +// This creates an interval timer to generate a normal shutdown and then +// launches with a valid, stand-alone command line and no simulated errors. +TEST_F(NetconfControllerTest, launchNormalShutdown) { + // Write valid_netconf_config and then run launch() for 200 ms. + time_duration elapsed_time; + runWithConfig(valid_netconf_config, 200, elapsed_time); + + // Give a generous margin to accommodate slower test environs. + EXPECT_TRUE(elapsed_time.total_milliseconds() >= 100 && + elapsed_time.total_milliseconds() <= 500); +} + +// Tests that the SIGINT triggers a normal shutdown. +TEST_F(NetconfControllerTest, sigintShutdown) { + // Setup to raise SIGINT in 1 ms. + TimedSignal sighup(*getIOService(), SIGINT, 1); + + // Write valid_netconf_config and then run launch() for a maximum + // of 500 ms. + time_duration elapsed_time; + runWithConfig(valid_netconf_config, 500, elapsed_time); + + // Signaled shutdown should make our elapsed time much smaller than + // the maximum run time. Give generous margin to accommodate slow + // test environs. + EXPECT_TRUE(elapsed_time.total_milliseconds() < 300); +} + +// Tests that the SIGTERM triggers a normal shutdown. +TEST_F(NetconfControllerTest, sigtermShutdown) { + // Setup to raise SIGTERM in 1 ms. + TimedSignal sighup(*getIOService(), SIGTERM, 1); + + // Write valid_netconf_config and then run launch() for a maximum + // of 500 ms. + time_duration elapsed_time; + runWithConfig(valid_netconf_config, 500, elapsed_time); + + // Signaled shutdown should make our elapsed time much smaller than + // the maximum run time. Give generous margin to accommodate slow + // test environs. + EXPECT_TRUE(elapsed_time.total_milliseconds() < 300); +} + +} diff --git a/src/bin/netconf/tests/netconf_process_unittests.cc b/src/bin/netconf/tests/netconf_process_unittests.cc new file mode 100644 index 0000000..08d2d3c --- /dev/null +++ b/src/bin/netconf/tests/netconf_process_unittests.cc @@ -0,0 +1,92 @@ +// Copyright (C) 2018-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 <netconf/netconf_cfg_mgr.h> +#include <netconf/netconf_process.h> +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> +#include <process/testutils/d_test_stubs.h> +#include <testutils/gtest_utils.h> + +#include <boost/date_time/posix_time/posix_time.hpp> + +#include <gtest/gtest.h> + +#include <functional> + +using namespace boost::posix_time; +using namespace isc; +using namespace isc::netconf; +using namespace isc::asiolink; +using namespace isc::process; + +namespace { + +/// @brief NetconfProcess test fixture class. +class NetconfProcessTest : public NetconfProcess, public ::testing::Test { +public: + /// @brief Constructor + NetconfProcessTest() : + NetconfProcess("netconf-test", + IOServicePtr(new isc::asiolink::IOService())) { + NetconfConfigPtr ctx = getNetconfCfgMgr()->getNetconfConfig(); + } + + /// @brief Destructor + virtual ~NetconfProcessTest() { + } + + /// @brief Callback that will invoke shutdown method. + void genShutdownCallback() { + shutdown(isc::data::ConstElementPtr()); + } +}; + +// Test construction of the NetconfProcess object. +TEST(NetconfProcess, construction) { + // Verify that the constructor will fail if given an empty + // io service. + IOServicePtr lcl_io_service; + EXPECT_THROW(NetconfProcess("TestProcess", lcl_io_service), + DProcessBaseError); + + // Verify that the constructor succeeds with a valid io_service + lcl_io_service.reset(new IOService()); + ASSERT_NO_THROW_LOG(NetconfProcess("TestProcess", lcl_io_service)); + + // Verify tha the configuration is accessible after construction. + NetconfProcess netconf_process("TestProcess", lcl_io_service); + NetconfCfgMgrPtr cfg_mgr = netconf_process.getNetconfCfgMgr(); + ASSERT_TRUE(cfg_mgr); +} + +// Verifies that en external call to shutdown causes the run method to +// exit gracefully. +TEST_F(NetconfProcessTest, shutdown) { + // Use an asiolink IntervalTimer and callback to generate the + // shutdown invocation. (Note IntervalTimer setup is in milliseconds). + IntervalTimer timer(*getIoService()); + timer.setup(std::bind(&NetconfProcessTest::genShutdownCallback, this), + 200); + + // Record start time, and invoke run(). + ptime start = microsec_clock::universal_time(); + EXPECT_NO_THROW(run()); + + // Record stop time. + ptime stop = microsec_clock::universal_time(); + + // Verify that duration of the run invocation is the same as the + // timer duration. This demonstrates that the shutdown was driven + // by an io_service event and callback. + time_duration elapsed = stop - start; + EXPECT_TRUE(elapsed.total_milliseconds() >= 100 && + elapsed.total_milliseconds() <= 400); +} + +} diff --git a/src/bin/netconf/tests/netconf_unittests.cc b/src/bin/netconf/tests/netconf_unittests.cc new file mode 100644 index 0000000..bd9be00 --- /dev/null +++ b/src/bin/netconf/tests/netconf_unittests.cc @@ -0,0 +1,1212 @@ +// Copyright (C) 2018-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 <netconf/netconf.h> +#include <netconf/netconf_process.h> +#include <netconf/parser_context.h> +#include <netconf/simple_parser.h> +#include <netconf/unix_control_socket.h> +#include <asiolink/asio_wrapper.h> +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> +#include <cc/command_interpreter.h> +#include <yang/yang_models.h> +#include <yang/yang_revisions.h> +#include <yang/tests/sysrepo_setup.h> +#include <yang/translator_config.h> +#include <yang/testutils/translator_test.h> +#include <testutils/log_utils.h> +#include <testutils/threaded_test.h> +#include <testutils/sandbox.h> + +#include <gtest/gtest.h> + +#include <sstream> +#include <thread> + +using namespace std; +using namespace isc; +using namespace isc::netconf; +using namespace isc::asiolink; +using namespace isc::config; +using namespace isc::data; +using namespace isc::http; +using namespace isc::test; +using namespace isc::yang; +using namespace isc::yang::test; +using namespace sysrepo; + +using isc::yang::test::SysrepoSetup; + +namespace { + +/// @brief Test unix socket file name. +const string TEST_SOCKET = "test-socket"; + +/// @brief Type definition for the pointer to Thread objects. +typedef boost::shared_ptr<thread> ThreadPtr; + +/// @brief Test version of the NetconfAgent class. +class NakedNetconfAgent : public NetconfAgent { +public: + /// @brief Constructor. + NakedNetconfAgent() { + } + + /// @brief Destructor. + virtual ~NakedNetconfAgent() { + } + + /// Export protected methods and fields. + using NetconfAgent::keaConfig; + using NetconfAgent::initSysrepo; + using NetconfAgent::checkModule; + using NetconfAgent::checkModules; + using NetconfAgent::yangConfig; + using NetconfAgent::subscribeConfig; + using NetconfAgent::conn_; + using NetconfAgent::startup_sess_; + using NetconfAgent::running_sess_; + using NetconfAgent::modules_; + using NetconfAgent::subscriptions_; +}; + +/// @brief Type definition for the pointer to NakedNetconfAgent objects. +typedef boost::shared_ptr<NakedNetconfAgent> NakedNetconfAgentPtr; + +/// @brief Clear YANG configuration. +/// +/// @param agent The naked netconf agent (fr its startup datastore session). +void clearYang(NakedNetconfAgentPtr agent) { + if (agent && (agent->startup_sess_)) { + string xpath = "/kea-dhcp4-server:config"; + EXPECT_NO_THROW(agent->startup_sess_->delete_item(xpath.c_str())); + EXPECT_NO_THROW(agent->startup_sess_->apply_changes()); + } +} + + +// Empirically the requested subnets have sometimes returned in decreasing +// order of subnet ID. To avoid flaky test failures, sort them before +// comparing. +void sortSubnets(ConstElementPtr const& map) { + boost::dynamic_pointer_cast<ListElement>( + boost::const_pointer_cast<Element>( + map->get("arguments")->get("Dhcp4")->get("subnet4"))) + ->sort("id"); +} + +/// @brief Test fixture class for netconf agent. +class NetconfAgentTest : public ThreadedTest { +public: + isc::test::Sandbox sandbox; + + void SetUp() override { + SysrepoSetup::cleanSharedMemory(); + removeUnixSocketFile(); + io_service_.reset(new IOService()); + agent_.reset(new NakedNetconfAgent()); + } + + void TearDown() override { + if (thread_) { + thread_->join(); + thread_.reset(); + } + // io_service must be stopped after the thread returns, + // otherwise the thread may never return if it is + // waiting for the completion of some asynchronous tasks. + io_service_->stop(); + io_service_.reset(); + if (agent_) { + clearYang(agent_); + agent_->clear(); + } + agent_.reset(); + requests_.clear(); + responses_.clear(); + removeUnixSocketFile(); + SysrepoSetup::cleanSharedMemory(); + } + + /// @brief Returns socket file path. + /// + /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the + /// socket file is created in the location pointed to by this variable. + /// Otherwise, it is created in the build directory. + string unixSocketFilePath() { + std::string socket_path; + const char* env = getenv("KEA_SOCKET_TEST_DIR"); + if (env) { + socket_path = std::string(env) + "/test-socket"; + } else { + socket_path = sandbox.join("test-socket"); + } + return (socket_path); + } + + /// @brief Removes unix socket descriptor. + void removeUnixSocketFile() { + static_cast<void>(remove(unixSocketFilePath().c_str())); + } + + /// @brief Create configuration of the control socket. + /// + /// @return a pointer to a control socket configuration. + CfgControlSocketPtr createCfgControlSocket() { + CfgControlSocketPtr cfg; + cfg.reset(new CfgControlSocket(CfgControlSocket::Type::UNIX, + unixSocketFilePath(), + Url("http://127.0.0.1:8000/"))); + return (cfg); + } + + /// @brief Fake server (returns OK answer). + void fakeServer(); + + /// @brief IOService object. + IOServicePtr io_service_; + + /// @brief Test netconf agent. + NakedNetconfAgentPtr agent_; + + /// @brief Request list. + vector<string> requests_; + + /// @brief Response list. + vector<string> responses_; +}; + +/// @brief Special test fixture for logging tests. +class NetconfAgentLogTest : public dhcp::test::LogContentTest { +public: + /// @brief Constructor. + NetconfAgentLogTest() + : finished_(false), + io_service_(new IOService()), + thread_(), + agent_(new NakedNetconfAgent) { + } + + /// @brief Destructor. + virtual ~NetconfAgentLogTest() { + if (agent_) { + clearYang(agent_); + agent_->clear(); + } + agent_.reset(); + // io_service must be stopped to make the thread to return. + io_service_->stop(); + if (thread_) { + thread_->join(); + thread_.reset(); + } + io_service_.reset(); + } + + /// @brief Default change callback (print changes and return OK). + sr_error_t callback(sysrepo::S_Session sess, + const char* module_name, + const char* /* xpath */, + sr_event_t /* event */, + uint32_t /* request_id */) { + NetconfAgent::logChanges(sess, module_name); + finished_ = true; + return (SR_ERR_OK); + } + + /// @brief logChanges is called in another thread so we have to wait for it. + /// + /// @todo The better way to get notified and get rid of the sleep is with a + /// conditional variable. + void waitForCallback() { + while (!finished_) { + usleep(1000); + } + } + + /// @brief To know when the callback was called. + std::atomic<bool> finished_; + + /// @brief IOService object. + IOServicePtr io_service_; + + /// @brief Pointer to server thread. + ThreadPtr thread_; + + /// @brief Test netconf agent. + NakedNetconfAgentPtr agent_; +}; + +/// @brief Fake server (returns OK answer). +void +NetconfAgentTest::fakeServer() { + // Acceptor. + boost::asio::local::stream_protocol::acceptor + acceptor(io_service_->get_io_service()); + EXPECT_NO_THROW(acceptor.open()); + boost::asio::local::stream_protocol::endpoint + endpoint(unixSocketFilePath()); + boost::asio::socket_base::reuse_address option(true); + acceptor.set_option(option); + EXPECT_NO_THROW(acceptor.bind(endpoint)); + EXPECT_NO_THROW(acceptor.listen()); + boost::asio::local::stream_protocol::socket + socket(io_service_->get_io_service()); + + // Ready. + signalReady(); + + // Timeout. + bool timeout = false; + IntervalTimer timer(*io_service_); + timer.setup([&timeout]() { + timeout = true; + FAIL() << "timeout"; + }, 1500, IntervalTimer::ONE_SHOT); + + // Accept. + boost::system::error_code ec; + bool accepted = false; + acceptor.async_accept(socket, + [&ec, &accepted] + (const boost::system::error_code& error) { + ec = error; + accepted = true; + }); + while (!accepted && !timeout) { + io_service_->run_one(); + } + ASSERT_FALSE(ec); + + // Receive command. + string rbuf(1024, ' '); + size_t received = 0; + socket.async_receive(boost::asio::buffer(&rbuf[0], rbuf.size()), + [&ec, &received] + (const boost::system::error_code& error, size_t cnt) { + ec = error; + received = cnt; + }); + while (!received && !timeout) { + io_service_->run_one(); + } + ASSERT_FALSE(ec); + rbuf.resize(received); + requests_.push_back(rbuf); + ConstElementPtr json; + EXPECT_NO_THROW(json = Element::fromJSON(rbuf)); + EXPECT_TRUE(json); + string command; + ConstElementPtr config; + if (json) { + ConstElementPtr arg; + EXPECT_NO_THROW(command = parseCommand(arg, json)); + if (command == "config-get") { + config = Element::fromJSON("{ \"comment\": \"empty\" }"); + } + } + + // Send answer. + string sbuf = createAnswer(CONTROL_RESULT_SUCCESS, config)->str(); + responses_.push_back(sbuf); + size_t sent = 0; + socket.async_send(boost::asio::buffer(&sbuf[0], sbuf.size()), + [&ec, &sent] + (const boost::system::error_code& error, size_t cnt) { + ec = error; + sent = cnt; + }); + while (!sent && !timeout) { + io_service_->run_one(); + } + ASSERT_FALSE(ec); + + // Stop timer. + timer.cancel(); + + // Close socket and acceptor. + if (socket.is_open()) { + EXPECT_NO_THROW(socket.close()); + } + EXPECT_NO_THROW(acceptor.close()); + // Removed the socket file so it can be called again immediately. + removeUnixSocketFile(); + + /// Finished. + EXPECT_FALSE(timeout); + EXPECT_TRUE(accepted); + EXPECT_TRUE(received); + EXPECT_TRUE(sent); + EXPECT_EQ(sent, sbuf.size()); + + // signalStopped can't be called here because of the 2 runs for update. +} + +// Verifies that the initSysrepo method opens sysrepo connection and sessions. +TEST_F(NetconfAgentTest, initSysrepo) { + EXPECT_NO_THROW(agent_->initSysrepo()); + EXPECT_TRUE(agent_->conn_); + EXPECT_TRUE(agent_->startup_sess_); + EXPECT_TRUE(agent_->running_sess_); + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_LE(16, agent_->modules_.size()); +} + +// Verifies that the checkModule method emits expected errors. +TEST_F(NetconfAgentLogTest, checkModule) { + // keatest-module should not be in YANG_REVISIONS. + EXPECT_EQ(0, YANG_REVISIONS.count("keatest-module")); + // But kea-dhcp[46]-server must be in. + ASSERT_EQ(1, YANG_REVISIONS.count("kea-dhcp4-server")); + ASSERT_EQ(1, YANG_REVISIONS.count("kea-dhcp6-server")); + + // kea-dhcp[46]-server should be available. + EXPECT_NO_THROW(agent_->initSysrepo()); + EXPECT_EQ(1, agent_->modules_.count("kea-dhcp4-server")); + EXPECT_EQ(1, agent_->modules_.count("kea-dhcp6-server")); + EXPECT_TRUE(agent_->checkModule("kea-dhcp4-server")); + EXPECT_TRUE(agent_->checkModule("kea-dhcp6-server")); + + // Unknown module should emit a missing error. + EXPECT_EQ(0, agent_->modules_.count("does-not-exist")); + EXPECT_FALSE(agent_->checkModule("does-not-exist")); + addString("NETCONF_MODULE_MISSING_ERR Missing essential module " + "does-not-exist in sysrepo"); + + // Patch the found revision to get a revision error. + const string& module = "kea-dhcp4-server"; + auto it4 = agent_->modules_.find(module); + if (it4 != agent_->modules_.end()) { + agent_->modules_.erase(it4); + } + // The module was written far after 20180714... + const string& bad_revision = "2018-07-14"; + agent_->modules_.insert(make_pair(module, bad_revision)); + EXPECT_FALSE(agent_->checkModule(module)); + ostringstream msg; + msg << "NETCONF_MODULE_REVISION_ERR Essential module " << module + << " does NOT have the right revision: expected " + << YANG_REVISIONS.at(module) << ", got " << bad_revision; + addString(msg.str()); + + EXPECT_TRUE(checkFile()); +} + +// Verifies that the checkModules method emits expected warnings. +TEST_F(NetconfAgentLogTest, checkModules) { + // kea-dhcp[46]-server must be in YANG_REVISIONS. + ASSERT_EQ(1, YANG_REVISIONS.count("kea-dhcp4-server")); + ASSERT_EQ(1, YANG_REVISIONS.count("kea-dhcp6-server")); + + // kea-dhcp[46]-server should be available. + EXPECT_NO_THROW(agent_->initSysrepo()); + EXPECT_EQ(1, agent_->modules_.count("kea-dhcp4-server")); + EXPECT_EQ(1, agent_->modules_.count("kea-dhcp6-server")); + + // Run checkModules but it will be indirectly checked as + // emitting nothing. + ASSERT_NO_THROW_LOG(agent_->checkModules()); + + // Remove kea-dhcp6-server. + const string& module = "kea-dhcp6-server"; + auto it6 = agent_->modules_.find(module); + if (it6 != agent_->modules_.end()) { + agent_->modules_.erase(it6); + } + ASSERT_NO_THROW_LOG(agent_->checkModules()); + ostringstream mmsg; + mmsg << "NETCONF_MODULE_MISSING_WARN Missing module " << module + << " in sysrepo"; + addString(mmsg.str()); + + // Add it back with a bad revision. + const string& bad_revision = "2018-07-14"; + agent_->modules_.insert(make_pair(module, bad_revision)); + ASSERT_NO_THROW_LOG(agent_->checkModules()); + ostringstream rmsg; + rmsg << "NETCONF_MODULE_REVISION_WARN Module " << module + << " does NOT have the right revision: expected " + << YANG_REVISIONS.at(module) << ", got " << bad_revision; + addString(rmsg.str()); + + EXPECT_TRUE(checkFile()); +} + +// Verifies that the logChanges method handles correctly changes. +TEST_F(NetconfAgentLogTest, logChanges) { + // Initial YANG configuration. + const YRTree tree0 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", SR_STRING_T, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + // Load initial YANG configuration. + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + YangRepr repr(KEA_DHCP4_SERVER); + ASSERT_NO_THROW_LOG(repr.set(tree0, agent_->startup_sess_)); + EXPECT_NO_THROW(agent_->startup_sess_->apply_changes()); + + // Subscribe configuration changes. + S_Subscribe subs(new Subscribe(agent_->running_sess_)); + auto cb = [&](sysrepo::S_Session sess, const char* module_name, + const char* xpath, sr_event_t event, uint32_t request_id) { + return callback(sess, module_name, xpath, event, request_id); + }; + EXPECT_NO_THROW(subs->module_change_subscribe(KEA_DHCP4_SERVER.c_str(), cb, + nullptr, 0, + SR_SUBSCR_DONE_ONLY)); + thread_.reset(new thread([this]() { io_service_->run(); })); + + // Change configuration (subnet #1 moved from 10.0.0.0/24 to 10.0.1/0/24). + const YRTree tree1 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.1.0/24", SR_STRING_T, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + EXPECT_NO_THROW(repr.set(tree1, agent_->running_sess_)); + EXPECT_NO_THROW(agent_->running_sess_->apply_changes()); + + // Check that the debug output was correct. + addString( + "NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: created: " + "/kea-dhcp4-server:config/subnet4[id='2'] (list instance)"); + addString("NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: " + "created: /kea-dhcp4-server:config/subnet4[id='2']/id = 2"); + addString( + "NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: created: " + "/kea-dhcp4-server:config/subnet4[id='2']/subnet = 10.0.2.0/24"); + addString( + "NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: created: " + "/kea-dhcp4-server:config/subnet4[id='2']/relay (container)"); + + waitForCallback(); + + EXPECT_TRUE(checkFile()); +} + +// Verifies that the logChanges method handles correctly changes. +// Instead of the simple modified of the previous test, now there will +// deleted, created and moved. +TEST_F(NetconfAgentLogTest, logChanges2) { + // Initial YANG configuration. + const YRTree tree0 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", SR_STRING_T, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + // Load initial YANG configuration. + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + YangRepr repr(KEA_DHCP4_SERVER); + ASSERT_NO_THROW_LOG(repr.set(tree0, agent_->startup_sess_)); + EXPECT_NO_THROW(agent_->startup_sess_->apply_changes()); + + // Subscribe configuration changes. + S_Subscribe subs(new Subscribe(agent_->running_sess_)); + auto cb = [&](sysrepo::S_Session sess, const char* module_name, + const char* xpath, sr_event_t event, uint32_t request_id) { + return callback(sess, module_name, xpath, event, request_id); + }; + EXPECT_NO_THROW(subs->module_change_subscribe(KEA_DHCP4_SERVER.c_str(), cb, + nullptr, 0, + SR_SUBSCR_DONE_ONLY)); + thread_.reset(new thread([this]() { io_service_->run(); })); + + // Change configuration (subnet #1 moved to #10). + string xpath = "/kea-dhcp4-server:config/subnet4[id='1']"; + EXPECT_NO_THROW(agent_->running_sess_->delete_item(xpath.c_str())); + const YRTree tree1 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='10']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='10']/id", + "10", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='10']/subnet", + "10.0.0.0/24", SR_STRING_T, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + EXPECT_NO_THROW(repr.set(tree1, agent_->running_sess_)); + EXPECT_NO_THROW(agent_->running_sess_->apply_changes()); + + // Check that the debug output was correct. + addString( + "NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: deleted: " + "/kea-dhcp4-server:config/subnet4[id='1'] (list instance)"); + addString("NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: " + "deleted: /kea-dhcp4-server:config/subnet4[id='1']/id = 1"); + addString( + "NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: deleted: " + "/kea-dhcp4-server:config/subnet4[id='1']/subnet = 10.0.1.0/24"); + addString( + "NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: deleted: " + "/kea-dhcp4-server:config/subnet4[id='1']/relay (container)"); + + waitForCallback(); + + EXPECT_TRUE(checkFile()); +} + +// Verifies that the keaConfig method works as expected. +TEST_F(NetconfAgentTest, keaConfig) { + // Netconf configuration. + string config_prefix = "{\n" + " \"Netconf\": {\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"socket-type\": \"unix\",\n" + " \"socket-name\": \""; + string config_trailer = "\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + string config = config_prefix + unixSocketFilePath() + config_trailer; + NetconfConfigPtr ctx(new NetconfConfig()); + ElementPtr json; + ParserContext parser_context; + EXPECT_NO_THROW(json = + parser_context.parseString(config, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(json); + ASSERT_EQ(Element::map, json->getType()); + ConstElementPtr netconf_json = json->get("Netconf"); + ASSERT_TRUE(netconf_json); + json = boost::const_pointer_cast<Element>(netconf_json); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW(parser.parse(ctx, json, false)); + + // Get service pair. + CfgServersMapPtr servers_map = ctx->getCfgServersMap(); + ASSERT_TRUE(servers_map); + ASSERT_EQ(1, servers_map->size()); + CfgServersMapPair service_pair = *servers_map->begin(); + + // Launch server. + thread_.reset(new thread([this]() { fakeServer(); signalStopped(); })); + + // Wait until the server is listening. + waitReady(); + + // Try keaConfig. + EXPECT_NO_THROW(agent_->keaConfig(service_pair)); + + // Wait server to be stopped. + waitStopped(); + + // Check request. + ASSERT_EQ(1, requests_.size()); + const string& request_str = requests_[0]; + ConstElementPtr request; + ASSERT_NO_THROW_LOG(request = Element::fromJSON(request_str)); + string expected_str = "{\n" + "\"command\": \"config-get\"\n" + "}"; + ConstElementPtr expected; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + EXPECT_TRUE(expected->equals(*request)); + // Alternative showing more for debugging... + // EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check response. + ASSERT_EQ(1, responses_.size()); + const string& response_str = responses_[0]; + ConstElementPtr response; + ASSERT_NO_THROW_LOG(response = Element::fromJSON(response_str)); + expected_str = "{\n" + "\"result\": 0,\n" + "\"arguments\": {\n" + " \"comment\": \"empty\"\n" + " }\n" + "}"; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + EXPECT_TRUE(expected->equals(*response)); +} + +// Verifies that the yangConfig method works as expected: apply YANG config +// to the server. +TEST_F(NetconfAgentTest, yangConfig) { + // YANG configuration. + const YRTree tree = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", SR_STRING_T, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + // Load YANG configuration. + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + YangRepr repr(KEA_DHCP4_SERVER); + ASSERT_NO_THROW_LOG(repr.set(tree, agent_->startup_sess_)); + EXPECT_NO_THROW(agent_->startup_sess_->apply_changes()); + + // Netconf configuration. + string config_prefix = "{\n" + " \"Netconf\": {\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"socket-type\": \"unix\",\n" + " \"socket-name\": \""; + string config_trailer = "\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + string config = config_prefix + unixSocketFilePath() + config_trailer; + NetconfConfigPtr ctx(new NetconfConfig()); + ElementPtr json; + ParserContext parser_context; + EXPECT_NO_THROW(json = + parser_context.parseString(config, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(json); + ASSERT_EQ(Element::map, json->getType()); + ConstElementPtr netconf_json = json->get("Netconf"); + ASSERT_TRUE(netconf_json); + json = boost::const_pointer_cast<Element>(netconf_json); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW(parser.parse(ctx, json, false)); + + // Get service pair. + CfgServersMapPtr servers_map = ctx->getCfgServersMap(); + ASSERT_TRUE(servers_map); + ASSERT_EQ(1, servers_map->size()); + CfgServersMapPair service_pair = *servers_map->begin(); + + // Launch server. + thread_.reset(new thread([this]() { fakeServer(); signalStopped();})); + + // Wait until the server is listening. + waitReady(); + + // Try yangConfig. + EXPECT_NO_THROW(agent_->yangConfig(service_pair)); + + // Wait server to be stopped. + waitStopped(); + + // Check request. + ASSERT_EQ(1, requests_.size()); + const string& request_str = requests_[0]; + ConstElementPtr request; + ASSERT_NO_THROW_LOG(request = Element::fromJSON(request_str)); + string expected_str = "{\n" + "\"command\": \"config-set\",\n" + "\"arguments\": {\n" + " \"Dhcp4\": {\n" + " \"subnet4\": [\n" + " {\n" + " \"id\": 1,\n" + " \"subnet\": \"10.0.0.0/24\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"subnet\": \"10.0.2.0/24\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}"; + ConstElementPtr expected; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + + sortSubnets(expected); + sortSubnets(request); + EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check response. + ASSERT_EQ(1, responses_.size()); + const string& response_str = responses_[0]; + ConstElementPtr response; + ASSERT_NO_THROW_LOG(response = Element::fromJSON(response_str)); + expected_str = "{\n" + "\"result\": 0\n" + "}"; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + EXPECT_TRUE(expected->equals(*response)); +} + +// Verifies that the subscribeConfig method works as expected. +TEST_F(NetconfAgentTest, subscribeConfig) { + // Netconf configuration. + string config_prefix = "{\n" + " \"Netconf\": {\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"socket-type\": \"unix\",\n" + " \"socket-name\": \""; + string config_trailer = "\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + string config = config_prefix + unixSocketFilePath() + config_trailer; + NetconfConfigPtr ctx(new NetconfConfig()); + ElementPtr json; + ParserContext parser_context; + EXPECT_NO_THROW(json = + parser_context.parseString(config, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(json); + ASSERT_EQ(Element::map, json->getType()); + ConstElementPtr netconf_json = json->get("Netconf"); + ASSERT_TRUE(netconf_json); + json = boost::const_pointer_cast<Element>(netconf_json); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW(parser.parse(ctx, json, false)); + + // Get service pair. + CfgServersMapPtr servers_map = ctx->getCfgServersMap(); + ASSERT_TRUE(servers_map); + ASSERT_EQ(1, servers_map->size()); + CfgServersMapPair service_pair = *servers_map->begin(); + + // Try subscribeConfig. + EXPECT_EQ(0, agent_->subscriptions_.size()); + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW(agent_->subscribeConfig(service_pair)); + EXPECT_EQ(1, agent_->subscriptions_.size()); + + /// Unsubscribe. + EXPECT_NO_THROW(agent_->subscriptions_.clear()); +} + +// Verifies that the update method works as expected: apply new YANG configuration +// to the server. Note it is called by the subscription callback. +TEST_F(NetconfAgentTest, update) { + // Initial YANG configuration. + const YRTree tree0 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", SR_STRING_T, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + // Load initial YANG configuration. + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + YangRepr repr(KEA_DHCP4_SERVER); + ASSERT_NO_THROW_LOG(repr.set(tree0, agent_->startup_sess_)); + EXPECT_NO_THROW(agent_->startup_sess_->apply_changes()); + + // Netconf configuration. + // Set validate-changes to false to avoid validate() to be called. + string config_prefix = "{\n" + " \"Netconf\": {\n" + " \"validate-changes\": false,\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"socket-type\": \"unix\",\n" + " \"socket-name\": \""; + string config_trailer = "\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + string config = config_prefix + unixSocketFilePath() + config_trailer; + NetconfConfigPtr ctx(new NetconfConfig()); + ElementPtr json; + ParserContext parser_context; + EXPECT_NO_THROW(json = + parser_context.parseString(config, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(json); + ASSERT_EQ(Element::map, json->getType()); + ConstElementPtr netconf_json = json->get("Netconf"); + ASSERT_TRUE(netconf_json); + json = boost::const_pointer_cast<Element>(netconf_json); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW(parser.parse(ctx, json, false)); + + // Get service pair. + CfgServersMapPtr servers_map = ctx->getCfgServersMap(); + ASSERT_TRUE(servers_map); + ASSERT_EQ(1, servers_map->size()); + CfgServersMapPair service_pair = *servers_map->begin(); + + // Subscribe YANG changes. + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW(agent_->subscribeConfig(service_pair)); + EXPECT_EQ(1, agent_->subscriptions_.size()); + + // Launch server. + thread_.reset(new thread([this]() { fakeServer(); signalStopped(); })); + + // Wait until the server is listening. + waitReady(); + + // Change configuration (subnet #1 moved from 10.0.0.0/24 to 10.0.1/0/24). + const YRTree tree1 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.1.0/24", SR_STRING_T, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + EXPECT_NO_THROW(repr.set(tree1, agent_->running_sess_)); + EXPECT_NO_THROW(agent_->running_sess_->apply_changes()); + + // Wait server to be stopped. + waitStopped(); + + // Check request. + ASSERT_EQ(1, requests_.size()); + const string& request_str = requests_[0]; + ConstElementPtr request; + ASSERT_NO_THROW_LOG(request = Element::fromJSON(request_str)); + string expected_str = "{\n" + "\"command\": \"config-set\",\n" + "\"arguments\": {\n" + " \"Dhcp4\": {\n" + " \"subnet4\": [\n" + " {\n" + " \"id\": 1,\n" + " \"subnet\": \"10.0.1.0/24\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"subnet\": \"10.0.2.0/24\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}"; + ConstElementPtr expected; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + + sortSubnets(expected); + sortSubnets(request); + EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check response. + ASSERT_EQ(1, responses_.size()); + const string& response_str = responses_[0]; + ConstElementPtr response; + ASSERT_NO_THROW_LOG(response = Element::fromJSON(response_str)); + expected_str = "{\n" + "\"result\": 0\n" + "}"; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + EXPECT_TRUE(expected->equals(*response)); +} + +// Verifies that the validate method works as expected: test new YANG configuration +// with the server. Note it is called by the subscription callback and +// update is called after. +TEST_F(NetconfAgentTest, validate) { + // Initial YANG configuration. + const YRTree tree0 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", SR_STRING_T, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + // Load initial YANG configuration. + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + YangRepr repr(KEA_DHCP4_SERVER); + ASSERT_NO_THROW_LOG(repr.set(tree0, agent_->startup_sess_)); + EXPECT_NO_THROW(agent_->startup_sess_->apply_changes()); + + // Netconf configuration. + string config_prefix = "{\n" + " \"Netconf\": {\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"socket-type\": \"unix\",\n" + " \"socket-name\": \""; + string config_trailer = "\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + string config = config_prefix + unixSocketFilePath() + config_trailer; + NetconfConfigPtr ctx(new NetconfConfig()); + ElementPtr json; + ParserContext parser_context; + EXPECT_NO_THROW(json = + parser_context.parseString(config, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(json); + ASSERT_EQ(Element::map, json->getType()); + ConstElementPtr netconf_json = json->get("Netconf"); + ASSERT_TRUE(netconf_json); + json = boost::const_pointer_cast<Element>(netconf_json); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW(parser.parse(ctx, json, false)); + + // Get service pair. + CfgServersMapPtr servers_map = ctx->getCfgServersMap(); + ASSERT_TRUE(servers_map); + ASSERT_EQ(1, servers_map->size()); + CfgServersMapPair service_pair = *servers_map->begin(); + + // Subscribe YANG changes. + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW(agent_->subscribeConfig(service_pair)); + EXPECT_EQ(1, agent_->subscriptions_.size()); + + // Launch server twice. + thread_.reset(new thread([this]() + { + fakeServer(); + fakeServer(); + signalStopped(); + })); + + // Wait until the server is listening. + waitReady(); + + // Change configuration (subnet #1 moved from 10.0.0.0/24 to 10.0.1/0/24). + const YRTree tree1 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.1.0/24", SR_STRING_T, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", SR_STRING_T, true } + }); + EXPECT_NO_THROW(repr.set(tree1, agent_->running_sess_)); + EXPECT_NO_THROW(agent_->running_sess_->apply_changes()); + + // Wait servers to be stopped. + waitStopped(); + + // Check that the fake server received the first request. + ASSERT_LE(1, requests_.size()); + string request_str = requests_[0]; + ConstElementPtr request; + ASSERT_NO_THROW_LOG(request = Element::fromJSON(request_str)); + string expected_str = "{\n" + "\"command\": \"config-test\",\n" + "\"arguments\": {\n" + " \"Dhcp4\": {\n" + " \"subnet4\": [\n" + " {\n" + " \"id\": 1,\n" + " \"subnet\": \"10.0.1.0/24\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"subnet\": \"10.0.2.0/24\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}"; + ConstElementPtr expected; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + + sortSubnets(expected); + sortSubnets(request); + EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check that the fake server received the second request. + ASSERT_EQ(2, requests_.size()); + request_str = requests_[1]; + ASSERT_NO_THROW_LOG(request = Element::fromJSON(request_str)); + expected_str = "{\n" + "\"command\": \"config-set\",\n" + "\"arguments\": {\n" + " \"Dhcp4\": {\n" + " \"subnet4\": [\n" + " {\n" + " \"id\": 1,\n" + " \"subnet\": \"10.0.1.0/24\"\n" + " },\n" + " {\n" + " \"id\": 2,\n" + " \"subnet\": \"10.0.2.0/24\"\n" + " }\n" + " ]\n" + " }\n" + " }\n" + "}"; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + + sortSubnets(expected); + sortSubnets(request); + EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check responses. + ASSERT_EQ(2, responses_.size()); + string response_str = responses_[0]; + ConstElementPtr response; + ASSERT_NO_THROW_LOG(response = Element::fromJSON(response_str)); + expected_str = "{\n" + "\"result\": 0\n" + "}"; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + EXPECT_TRUE(expected->equals(*response)); + + response_str = responses_[1]; + ASSERT_NO_THROW_LOG(response = Element::fromJSON(response_str)); + expected_str = "{\n" + "\"result\": 0\n" + "}"; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + EXPECT_TRUE(expected->equals(*response)); +} + +// Verifies what happens when the validate method returns an error. +TEST_F(NetconfAgentTest, noValidate) { + // Initial YANG configuration. + const YRTree tree0 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", SR_STRING_T, true } + }); + // Load initial YANG configuration. + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + YangRepr repr(KEA_DHCP4_SERVER); + ASSERT_NO_THROW_LOG(repr.set(tree0, agent_->startup_sess_)); + EXPECT_NO_THROW(agent_->startup_sess_->apply_changes()); + + // Netconf configuration. + string config_prefix = "{\n" + " \"Netconf\": {\n" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"socket-type\": \"unix\",\n" + " \"socket-name\": \""; + string config_trailer = "\"\n" + " }\n" + " }\n" + " }\n" + " }\n" + "}"; + string config = config_prefix + unixSocketFilePath() + config_trailer; + NetconfConfigPtr ctx(new NetconfConfig()); + ElementPtr json; + ParserContext parser_context; + EXPECT_NO_THROW(json = + parser_context.parseString(config, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(json); + ASSERT_EQ(Element::map, json->getType()); + ConstElementPtr netconf_json = json->get("Netconf"); + ASSERT_TRUE(netconf_json); + json = boost::const_pointer_cast<Element>(netconf_json); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW(parser.parse(ctx, json, false)); + + // Get service pair. + CfgServersMapPtr servers_map = ctx->getCfgServersMap(); + ASSERT_TRUE(servers_map); + ASSERT_EQ(1, servers_map->size()); + CfgServersMapPair service_pair = *servers_map->begin(); + + // Subscribe YANG changes. + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW(agent_->subscribeConfig(service_pair)); + EXPECT_EQ(1, agent_->subscriptions_.size()); + + // Change configuration (add invalid user context). + const YRTree tree1 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config", "", SR_CONTAINER_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']", "", SR_LIST_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", SR_UINT32_T, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", SR_STRING_T, true }, + { "/kea-dhcp4-server:config/subnet4[id='1']/user-context", + "BOGUS", SR_STRING_T, true } + }); + try { + repr.set(tree1, agent_->running_sess_); + } catch (const sysrepo_exception& ex) { + EXPECT_EQ("User callback failed", string(ex.what())); + } catch (const std::exception& ex) { + ADD_FAILURE() << "unexpected exception: " << ex.what(); + } catch (...) { + ADD_FAILURE() << "unexpected exception"; + } +} + +} // namespace diff --git a/src/bin/netconf/tests/parser_unittests.cc b/src/bin/netconf/tests/parser_unittests.cc new file mode 100644 index 0000000..b496470 --- /dev/null +++ b/src/bin/netconf/tests/parser_unittests.cc @@ -0,0 +1,988 @@ +// Copyright (C) 2018-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 <cc/data.h> +#include <cc/dhcp_config_error.h> +#include <netconf/parser_context.h> +#include <testutils/gtest_utils.h> +#include <testutils/io_utils.h> +#include <testutils/log_utils.h> +#include <testutils/user_context_utils.h> + +#include <gtest/gtest.h> + +#include <fstream> +#include <set> + +#include <boost/algorithm/string.hpp> + +using namespace isc::data; +using namespace isc::test; +using namespace std; + +namespace isc { +namespace netconf { +namespace test { + +/// @brief compares two JSON trees +/// +/// If differences are discovered, gtest failure is reported (using EXPECT_EQ) +/// +/// @param a first to be compared +/// @param b second to be compared +void compareJSON(ConstElementPtr a, ConstElementPtr b) { + ASSERT_TRUE(a); + ASSERT_TRUE(b); + EXPECT_EQ(a->str(), b->str()); +} + +/// @brief Tests if the input string can be parsed with specific parser +/// +/// The input text will be passed to bison parser of specified type. +/// Then the same input text is passed to legacy JSON parser and outputs +/// from both parsers are compared. The legacy comparison can be disabled, +/// if the feature tested is not supported by the old parser (e.g. +/// new comment styles) +/// +/// @param txt text to be compared +/// @param parser_type bison parser type to be instantiated +/// @param compare whether to compare the output with legacy JSON parser +void testParser(const std::string& txt, ParserContext::ParserType parser_type, + bool compare = true) { + SCOPED_TRACE("\n=== tested config ===\n" + txt + "====================="); + + ConstElementPtr test_json; + ASSERT_NO_THROW_LOG({ + try { + ParserContext ctx; + test_json = ctx.parseString(txt, parser_type); + } catch (const std::exception &e) { + cout << "EXCEPTION: " << e.what() << endl; + throw; + } + + }); + + if (!compare) { + return; + } + + // Now compare if both representations are the same. + ElementPtr reference_json; + ASSERT_NO_THROW_LOG(reference_json = Element::fromJSON(txt, true)); + compareJSON(reference_json, test_json); +} + +TEST(ParserTest, mapInMap) { + string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }"; + testParser(txt, ParserContext::PARSER_JSON); +} + +TEST(ParserTest, listInList) { + string txt = "[ [ \"Britain\", \"Wales\", \"Scotland\" ], " + "[ \"Pomorze\", \"Wielkopolska\", \"Tatry\"] ]"; + testParser(txt, ParserContext::PARSER_JSON); +} + +TEST(ParserTest, nestedMaps) { + string txt = "{ \"europe\": { \"UK\": { \"London\": { \"street\": \"221B Baker\" }}}}"; + testParser(txt, ParserContext::PARSER_JSON); +} + +TEST(ParserTest, nestedLists) { + string txt = "[ \"half\", [ \"quarter\", [ \"eighth\", [ \"sixteenth\" ]]]]"; + testParser(txt, ParserContext::PARSER_JSON); +} + +TEST(ParserTest, listsInMaps) { + string txt = "{ \"constellations\": { \"orion\": [ \"rigel\", \"betelgeuse\" ], " + "\"cygnus\": [ \"deneb\", \"albireo\"] } }"; + testParser(txt, ParserContext::PARSER_JSON); +} + +TEST(ParserTest, mapsInLists) { + string txt = "[ { \"body\": \"earth\", \"gravity\": 1.0 }," + " { \"body\": \"mars\", \"gravity\": 0.376 } ]"; + testParser(txt, ParserContext::PARSER_JSON); +} + +TEST(ParserTest, types) { + string txt = "{ \"string\": \"foo\"," + "\"integer\": 42," + "\"boolean\": true," + "\"map\": { \"foo\": \"bar\" }," + "\"list\": [ 1, 2, 3 ]," + "\"null\": null }"; + testParser(txt, ParserContext::PARSER_JSON); +} + +TEST(ParserTest, keywordJSON) { + string txt = "{ \"name\": \"user\"," + "\"type\": \"password\"," + "\"user\": \"name\"," + "\"password\": \"type\" }"; + testParser(txt, ParserContext::PARSER_JSON); +} + +// This test checks if full config (with top level and Netconf objects) can +// be parsed with syntactic checking (and as pure JSON). +TEST(ParserTest, keywordNetconf) { + string txt = "{ \"Netconf\": {\n" + " \"boot-update\": true," + " \"subscribe-changes\": true," + " \"validate-changes\": true," + " \"managed-servers\": {" + " \"dhcp4\": {" + " \"model\": \"kea-dhcp4-server\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"unix\"," + " \"socket-name\": \"/tmp/kea4-ctrl-socket\"" + " }" + " }," + " \"dhcp6\": {" + " \"model\": \"kea-dhcp6-server\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"http\"," + " \"socket-url\": \"http://127.0.0.1:12345/\"" + " }" + " }," + " \"d2\": {" + " \"model\": \"kea-dhcp-ddns\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"stdout\"" + " }" + " }," + " \"ca\": {" + " \"model\": \"kea-ctrl-agent\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"http\"," + " \"user-context\": { \"use default\": true }" + " }" + " }" + " }," + " \"hooks-libraries\": [" + " {" + " \"library\": \"/opt/local/control-agent-commands.so\"," + " \"parameters\": {" + " \"param1\": \"foo\"" + " }" + " }" + " ]" + "} }"; + // This is a full config, so we'll parse it as full config (PARSER_NETCONF) + testParser(txt, ParserContext::PARSER_NETCONF); + testParser(txt, ParserContext::PARSER_JSON); +} + +// This test checks if simplified config (without top level and Netconf +// objects) can be parsed with syntactic checking (and as pure JSON). +TEST(ParserTest, keywordSubNetconf) { + + // This is similar to previous test, but note the lack of outer + // map and Netconf. + string txt = "{\n" + " \"boot-update\": true," + " \"subscribe-changes\": true," + " \"validate-changes\": true," + " \"managed-servers\": {" + " \"dhcp4\": {" + " \"model\": \"kea-dhcp4-server\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"unix\"," + " \"socket-name\": \"/tmp/kea4-ctrl-socket\"" + " }" + " }," + " \"dhcp6\": {" + " \"model\": \"kea-dhcp6-server\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"http\"," + " \"socket-url\": \"http://127.0.0.1:12345/\"" + " }" + " }," + " \"d2\": {" + " \"model\": \"kea-dhcp-ddns\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"stdout\"" + " }" + " }," + " \"ca\": {" + " \"model\": \"kea-ctrl-agent\"," + " \"boot-update\": false," + " \"subscribe-changes\": false," + " \"validate-changes\": false," + " \"control-socket\": {" + " \"socket-type\": \"http\"," + " \"user-context\": { \"use default\": true }" + " }" + " }" + " }," + " \"hooks-libraries\": [" + " {" + " \"library\": \"/opt/local/control-agent-commands.so\"," + " \"parameters\": {" + " \"param1\": \"foo\"" + " }" + " }" + " ]" + "}"; + + // This is only a subset of full config, so we'll parse with PARSER_SUB_NETCONF. + testParser(txt, ParserContext::PARSER_SUB_NETCONF); + testParser(txt, ParserContext::PARSER_JSON); +} + +// Tests if bash (#) comments are supported. That's the only comment type that +// was supported by the old parser. +TEST(ParserTest, bashComments) { + string txt= "{ \"Netconf\": {" + " \"managed-servers\": {\n" + " \"d2\": {\n" + " \"model\": \"foo\",\n" + " \"control-socket\": {\n" + "# this is a comment\n" + "\"socket-type\": \"unix\", \n" + "# This socket is mine. I can name it whatever\n" + "# I like, ok?\n" + "\"socket-name\": \"Hector\" \n" + "} } } } }"; + testParser(txt, ParserContext::PARSER_NETCONF); +} + +// Tests if C++ (//) comments can start anywhere, not just in the first line. +TEST(ParserTest, cppComments) { + string txt= "{ \"Netconf\": { // the level is over 9000!\n" + " \"managed-servers\": {\n" + " // Let's try talking to D2. Sadly, it never talks" + " // to us back :( Maybe he doesn't like his name?\n" + " \"d2\": {\n" + " \"model\": \"foo\",\n" + " \"control-socket\": {\n" + "\"socket-type\": \"unix\", \n" + "\"socket-name\": \"Hector\" \n" + "} } } } }"; + + testParser(txt, ParserContext::PARSER_NETCONF, false); +} + +// Tests if bash (#) comments can start anywhere, not just in the first line. +TEST(ParserTest, bashCommentsInline) { + string txt= "{ \"Netconf\": {" + " \"managed-servers\": {\n" + " \"d2\": {\n" + " \"model\": \"foo\",\n" + " \"control-socket\": {\n" + "\"socket-type\": \"unix\", # Maybe Hector is not really a \n" + "\"socket-name\": \"Hector\" # Unix process?\n" + "# Oh no! He's a windows one and just pretending!\n" + "} } } } }"; + testParser(txt, ParserContext::PARSER_NETCONF, false); +} + +// Tests if multi-line C style comments are handled correctly. +TEST(ParserTest, multilineComments) { + string txt= "{ \"Netconf\": {" + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"model\": \"foo\",\n" + " \"control-socket\": {\n" + " \"socket-type\": \"stdout\"\n" + " }\n" + " }\n" + " /* Ok, forget about it. If Hector doesn't want to talk,\n" + " we won't talk to him either. We now have quiet days. */\n" + " /* \"d2\": {" + " \"model\": \"bar\",\n" + " \"control-socket\": {\n" + " \"socket-type\": \"unix\",\n" + "\"socket-name\": \"Hector\"\n" + "} }*/ } } }"; + testParser(txt, ParserContext::PARSER_NETCONF, false); +} + +// Tests if embedded comments are handled correctly. +TEST(ParserTest, embbededComments) { + string txt= "{ \"Netconf\": {" + " \"comment\": \"a comment\"," + " \"managed-servers\": {\n" + " \"dhcp4\": {\n" + " \"control-socket\": {\n" + " \"user-context\": { \"comment\": \"indirect\" },\n" + " \"socket-type\": \"stdout\"\n" + " } } },\n" + " \"user-context\": { \"compatible\": true }\n" + "} }"; + testParser(txt, ParserContext::PARSER_NETCONF, false); +} + +/// @brief Loads specified example config file +/// +/// This test loads specified example file twice: first, using the legacy +/// JSON file and then second time using bison parser. Two created Element +/// trees are then compared. The input is decommented before it is passed +/// to legacy parser (as legacy support for comments is very limited). +/// +/// @param fname name of the file to be loaded +void testFile(const std::string& fname) { + ElementPtr json; + ElementPtr reference_json; + ConstElementPtr test_json; + + string decommented = decommentJSONfile(fname); + + cout << "Parsing file " << fname << "(" << decommented << ")" << endl; + + EXPECT_NO_THROW(json = Element::fromJSONFile(decommented, true)); + reference_json = moveComments(json); + + // remove the temporary file + EXPECT_NO_THROW(::remove(decommented.c_str())); + + EXPECT_NO_THROW( + try { + ParserContext ctx; + test_json = ctx.parseFile(fname, ParserContext::PARSER_NETCONF); + } catch (const std::exception &x) { + cout << "EXCEPTION: " << x.what() << endl; + throw; + }); + + ASSERT_TRUE(reference_json); + ASSERT_TRUE(test_json); + + compareJSON(reference_json, test_json); +} + +// This test loads all available existing files. Each config is loaded +// twice: first with the existing Element::fromJSONFile() and then +// the second time with NetconfParser. Both JSON trees are then compared. +// Hopefully the list of example configs will grow over time. +TEST(ParserTest, file) { + vector<string> configs; + configs.push_back("comments.json"); + configs.push_back("simple-dhcp4.json"); + configs.push_back("simple-dhcp6.json"); + + for (int i = 0; i<configs.size(); i++) { + testFile(string(CFG_EXAMPLES) + "/" + configs[i]); + } +} + +/// @brief Tests error conditions in NetconfParser +/// +/// @param txt text to be parsed +/// @param parser_type type of the parser to be used in the test +/// @param msg expected content of the exception +void testError(const std::string& txt, + ParserContext::ParserType parser_type, + const std::string& msg) { + SCOPED_TRACE("\n=== tested config ===\n" + txt + "====================="); + + try { + ParserContext ctx; + ConstElementPtr parsed = ctx.parseString(txt, parser_type); + FAIL() << "Expected ParseError but nothing was raised (expected: " + << msg << ")"; + } + catch (const ParseError& ex) { + EXPECT_EQ(msg, ex.what()); + } + catch (...) { + FAIL() << "Expected ParseError but something else was raised"; + } +} + +// Verify that error conditions are handled correctly. +TEST(ParserTest, errors) { + // no input + testError("", ParserContext::PARSER_JSON, + "<string>:1.1: syntax error, unexpected end of file"); + testError(" ", ParserContext::PARSER_JSON, + "<string>:1.2: syntax error, unexpected end of file"); + testError("\n", ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("\t", ParserContext::PARSER_JSON, + "<string>:1.2: syntax error, unexpected end of file"); + testError("\r", ParserContext::PARSER_JSON, + "<string>:1.2: syntax error, unexpected end of file"); + + // comments + testError("# nothing\n", + ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError(" #\n", + ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("// nothing\n", + ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("/* nothing */\n", + ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file"); + testError("/* no\nthing */\n", + ParserContext::PARSER_JSON, + "<string>:3.1: syntax error, unexpected end of file"); + testError("/* no\nthing */\n\n", + ParserContext::PARSER_JSON, + "<string>:4.1: syntax error, unexpected end of file"); + testError("/* nothing\n", + ParserContext::PARSER_JSON, + "Comment not closed. (/* in line 1"); + testError("\n\n\n/* nothing\n", + ParserContext::PARSER_JSON, + "Comment not closed. (/* in line 4"); + testError("{ /* */*/ }\n", + ParserContext::PARSER_JSON, + "<string>:1.3-8: Invalid character: *"); + testError("{ /* // *// }\n", + ParserContext::PARSER_JSON, + "<string>:1.3-11: Invalid character: /"); + testError("{ /* // */// }\n", + ParserContext::PARSER_JSON, + "<string>:2.1: syntax error, unexpected end of file, " + "expecting }"); + + // includes + testError("<?\n", + ParserContext::PARSER_JSON, + "Directive not closed."); + testError("<?include\n", + ParserContext::PARSER_JSON, + "Directive not closed."); + string file = string(CFG_EXAMPLES) + "/" + "simple-dhcp4.json"; + testError("<?include \"" + file + "\"\n", + ParserContext::PARSER_JSON, + "Directive not closed."); + testError("<?include \"/foo/bar\" ?>/n", + ParserContext::PARSER_JSON, + "Can't open include file /foo/bar"); + + // JSON keywords + testError("{ \"foo\": True }", + ParserContext::PARSER_JSON, + "<string>:1.10-13: JSON true reserved keyword is lower case only"); + testError("{ \"foo\": False }", + ParserContext::PARSER_JSON, + "<string>:1.10-14: JSON false reserved keyword is lower case only"); + testError("{ \"foo\": NULL }", + ParserContext::PARSER_JSON, + "<string>:1.10-13: JSON null reserved keyword is lower case only"); + testError("{ \"foo\": Tru }", + ParserContext::PARSER_JSON, + "<string>:1.10: Invalid character: T"); + testError("{ \"foo\": nul }", + ParserContext::PARSER_JSON, + "<string>:1.10: Invalid character: n"); + + // numbers + testError("123", + ParserContext::PARSER_NETCONF, + "<string>:1.1-3: syntax error, unexpected integer, " + "expecting {"); + testError("-456", + ParserContext::PARSER_NETCONF, + "<string>:1.1-4: syntax error, unexpected integer, " + "expecting {"); + testError("-0001", + ParserContext::PARSER_NETCONF, + "<string>:1.1-5: syntax error, unexpected integer, " + "expecting {"); + testError("1234567890123456789012345678901234567890", + ParserContext::PARSER_JSON, + "<string>:1.1-40: Failed to convert " + "1234567890123456789012345678901234567890" + " to an integer."); + testError("-3.14e+0", + ParserContext::PARSER_NETCONF, + "<string>:1.1-8: syntax error, unexpected floating point, " + "expecting {"); + testError("1e50000", + ParserContext::PARSER_JSON, + "<string>:1.1-7: Failed to convert 1e50000 " + "to a floating point."); + + // strings + testError("\"aabb\"", + ParserContext::PARSER_NETCONF, + "<string>:1.1-6: syntax error, unexpected constant string, " + "expecting {"); + testError("{ \"aabb\"err", + ParserContext::PARSER_JSON, + "<string>:1.9: Invalid character: e"); + testError("{ err\"aabb\"", + ParserContext::PARSER_JSON, + "<string>:1.3: Invalid character: e"); + testError("\"a\n\tb\"", + ParserContext::PARSER_JSON, + "<string>:1.1-6 (near 2): Invalid control in \"a\n\tb\""); + testError("\"a\n\\u12\"", + ParserContext::PARSER_JSON, + "<string>:1.1-8 (near 2): Invalid control in \"a\n\\u12\""); + testError("\"a\\n\\tb\"", + ParserContext::PARSER_NETCONF, + "<string>:1.1-8: syntax error, unexpected constant string, " + "expecting {"); + testError("\"a\\x01b\"", + ParserContext::PARSER_JSON, + "<string>:1.1-8 (near 3): Bad escape in \"a\\x01b\""); + testError("\"a\\u0162\"", + ParserContext::PARSER_JSON, + "<string>:1.1-9 (near 4): Unsupported unicode escape " + "in \"a\\u0162\""); + testError("\"a\\u062z\"", + ParserContext::PARSER_JSON, + "<string>:1.1-9 (near 3): Bad escape in \"a\\u062z\""); + testError("\"abc\\\"", + ParserContext::PARSER_JSON, + "<string>:1.1-6 (near 6): Overflow escape in \"abc\\\""); + testError("\"a\\u006\"", + ParserContext::PARSER_JSON, + "<string>:1.1-8 (near 3): Overflow unicode escape " + "in \"a\\u006\""); + testError("\"\\u\"", + ParserContext::PARSER_JSON, + "<string>:1.1-4 (near 2): Overflow unicode escape in \"\\u\""); + testError("\"\\u\x02\"", + ParserContext::PARSER_JSON, + "<string>:1.1-5 (near 2): Bad escape in \"\\u\x02\""); + testError("\"\\u\\\"foo\"", + ParserContext::PARSER_JSON, + "<string>:1.1-5 (near 2): Bad escape in \"\\u\\\"..."); + testError("\"\x02\\u\"", + ParserContext::PARSER_JSON, + "<string>:1.1-5 (near 1): Invalid control in \"\x02\\u\""); + + // from data_unittest.c + testError("\\a", + ParserContext::PARSER_JSON, + "<string>:1.1: Invalid character: \\"); + testError("\\", + ParserContext::PARSER_JSON, + "<string>:1.1: Invalid character: \\"); + testError("\\\"\\\"", + ParserContext::PARSER_JSON, + "<string>:1.1: Invalid character: \\"); + + // want a map + testError("[]\n", + ParserContext::PARSER_NETCONF, + "<string>:1.1: syntax error, unexpected [, " + "expecting {"); + testError("[]\n", + ParserContext::PARSER_NETCONF, + "<string>:1.1: syntax error, unexpected [, " + "expecting {"); + testError("{ 123 }\n", + ParserContext::PARSER_JSON, + "<string>:1.3-5: syntax error, unexpected integer, " + "expecting }"); + testError("{ 123 }\n", + ParserContext::PARSER_NETCONF, + "<string>:1.3-5: syntax error, unexpected integer, " + "expecting Netconf"); + testError("{ \"foo\" }\n", + ParserContext::PARSER_JSON, + "<string>:1.9: syntax error, unexpected }, " + "expecting :"); + testError("{ \"foo\" }\n", + ParserContext::PARSER_NETCONF, + "<string>:1.3-7: syntax error, unexpected constant string, " + "expecting Netconf"); + testError("{ \"foo\":null }\n", + ParserContext::PARSER_NETCONF, + "<string>:1.3-7: syntax error, unexpected constant string, " + "expecting Netconf"); + testError("{ \"Logging\":null }\n", + ParserContext::PARSER_NETCONF, + "<string>:1.3-11: syntax error, unexpected constant string, " + "expecting Netconf"); + testError("{ \"Netconf\" }\n", + ParserContext::PARSER_NETCONF, + "<string>:1.13: syntax error, unexpected }, " + "expecting :"); + testError("{ \"Netconf\":", + ParserContext::PARSER_NETCONF, + "<string>:1.13: syntax error, unexpected end of file, " + "expecting {"); + testError("{}{}\n", + ParserContext::PARSER_JSON, + "<string>:1.3: syntax error, unexpected {, " + "expecting end of file"); + + // duplicate in map + testError("{ \"foo\": 1, \"foo\": true }\n", + ParserContext::PARSER_JSON, + "<string>:1:13: duplicate foo entries in " + "JSON map (previous at <string>:1:10)"); + + // bad commas + testError("{ , }\n", + ParserContext::PARSER_JSON, + "<string>:1.3: syntax error, unexpected \",\", " + "expecting }"); + testError("{ , \"foo\":true }\n", + ParserContext::PARSER_JSON, + "<string>:1.3: syntax error, unexpected \",\", " + "expecting }"); + + // bad type + testError("{ \"Netconf\":{\n" + " \"managed-servers\":false }}\n", + ParserContext::PARSER_NETCONF, + "<string>:2.21-25: syntax error, unexpected boolean, " + "expecting {"); + + // unknown keyword + testError("{ \"Netconf\":{\n" + " \"topping\": \"Mozzarella\" }}\n", + ParserContext::PARSER_NETCONF, + "<string>:2.2-10: got unexpected keyword " + "\"topping\" in Netconf map."); + + // user context and embedded comments + testError("{ \"Netconf\":{\n" + " \"comment\": true } }\n", + ParserContext::PARSER_NETCONF, + "<string>:2.14-17: syntax error, unexpected boolean, " + "expecting constant string"); + + testError("{ \"Netconf\":{\n" + " \"user-context\": \"a comment\" } }\n", + ParserContext::PARSER_NETCONF, + "<string>:2.19-29: syntax error, unexpected constant string, " + "expecting {"); + + testError("{ \"Netconf\":{\n" + " \"comment\": \"a comment\",\n" + " \"comment\": \"another one\" } }\n", + ParserContext::PARSER_NETCONF, + "<string>:3.3-11: duplicate user-context/comment entries " + "(previous at <string>:2:3)"); + + testError("{ \"Netconf\":{\n" + " \"user-context\": { \"version\": 1 },\n" + " \"user-context\": { \"one\": \"only\" } } }\n", + ParserContext::PARSER_NETCONF, + "<string>:3.3-16: duplicate user-context entries " + "(previous at <string>:2:19)"); + + testError("{ \"Netconf\":{\n" + " \"user-context\": { \"comment\": \"indirect\" },\n" + " \"comment\": \"a comment\" } }\n", + ParserContext::PARSER_NETCONF, + "<string>:3.3-11: duplicate user-context/comment entries " + "(previous at <string>:2:19)"); + + // duplicate Netconf entries + testError("{ \"Netconf\":{\n" + " \"comment\": \"first\" },\n" + " \"Netconf\":{\n" + " \"comment\": \"second\" }}\n", + ParserContext::PARSER_NETCONF, + "<string>:3.3-11: syntax error, unexpected Netconf, expecting \",\" or }"); + + // duplicate of not string entries + testError("{ \"Netconf\":{\n" + " \"boot-update\": true,\n" + " \"boot-update\": false }}\n", + ParserContext::PARSER_NETCONF, + "<string>:3:2: duplicate boot-update entries in " + "Netconf map (previous at <string>:2:17)"); + + // duplicate of string entries + testError("{ \"Netconf\":{\n" + " \"managed-servers\": {\n" + " \"d2\": {\n" + " \"model\": \"foo\",\n" + " \"model\": \"bar\" }}}}\n", + ParserContext::PARSER_NETCONF, + "<string>:5:7: duplicate model entries in " + "managed-servers entry map (previous at <string>:4:16)"); +} + +// Check unicode escapes +TEST(ParserTest, unicodeEscapes) { + ConstElementPtr result; + string json; + + // check we can reread output + for (char c = -128; c < 127; ++c) { + string ins(" "); + ins[1] = c; + ConstElementPtr e(new StringElement(ins)); + json = e->str(); + ASSERT_NO_THROW_LOG( + try { + ParserContext ctx; + result = ctx.parseString(json, ParserContext::PARSER_JSON); + } catch (const std::exception &x) { + cout << "EXCEPTION: " << x.what() << endl; + throw; + }); + ASSERT_EQ(Element::string, result->getType()); + EXPECT_EQ(ins, result->stringValue()); + } +} + +// This test checks that all representations of a slash is recognized properly. +TEST(ParserTest, unicodeSlash) { + // check the 4 possible encodings of solidus '/' + ConstElementPtr result; + string json = "\"/\\/\\u002f\\u002F\""; + ASSERT_NO_THROW_LOG( + try { + ParserContext ctx; + result = ctx.parseString(json, ParserContext::PARSER_JSON); + } catch (const std::exception &x) { + cout << "EXCEPTION: " << x.what() << endl; + throw; + }); + ASSERT_EQ(Element::string, result->getType()); + EXPECT_EQ("////", result->stringValue()); +} + +/// @brief Load a file into a JSON element. +/// +/// @param fname The name of the file to load. +/// @param list The JSON element list to add the parsing result to. +void loadFile(const string& fname, ElementPtr list) { + ParserContext ctx; + ElementPtr json; + EXPECT_NO_THROW(json = ctx.parseFile(fname, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(json); + list->add(json); +} + +// This test checks that all map entries are in the sample file. +TEST(ParserTest, mapEntries) { + // Type of keyword set. + typedef set<string> KeywordSet; + + // Get keywords from the syntax file (netconf_parser.yy). + ifstream syntax_file(SYNTAX_FILE); + EXPECT_TRUE(syntax_file.is_open()); + string line; + KeywordSet syntax_keys = { "user-context" }; + // Code setting the map entry. + const string pattern = "ctx.stack_.back()->set(\""; + while (getline(syntax_file, line)) { + // Skip comments. + size_t comment = line.find("//"); + if (comment <= pattern.size()) { + continue; + } + if (comment != string::npos) { + line.resize(comment); + } + // Search for the code pattern. + size_t key_begin = line.find(pattern); + if (key_begin == string::npos) { + continue; + } + // Extract keywords. + line = line.substr(key_begin + pattern.size()); + size_t key_end = line.find_first_of('"'); + EXPECT_NE(string::npos, key_end); + string keyword = line.substr(0, key_end); + // Ignore result when adding the keyword to the syntax keyword set. + static_cast<void>(syntax_keys.insert(keyword)); + } + syntax_file.close(); + + // Get keywords from the sample file + string sample_dir(CFG_EXAMPLES); + sample_dir += "/"; + ElementPtr sample_json = Element::createList(); + loadFile(sample_dir + "simple-dhcp4.json", sample_json); + loadFile(sample_dir + "simple-dhcp6.json", sample_json); + KeywordSet sample_keys = { + "ca", "d2", + "hooks-libraries", "library", "parameters", + "socket-url" + }; + // Recursively extract keywords. + static void (*extract)(ConstElementPtr, KeywordSet&) = + [] (ConstElementPtr json, KeywordSet& set) { + if (json->getType() == Element::list) { + // Handle lists. + for (auto elem : json->listValue()) { + extract(elem, set); + } + } else if (json->getType() == Element::map) { + // Handle maps. + for (auto elem : json->mapValue()) { + static_cast<void>(set.insert(elem.first)); + // Skip entries with free content. + if ((elem.first != "user-context") && + (elem.first != "parameters")) { + extract(elem.second, set); + } + } + } + }; + extract(sample_json, sample_keys); + + // Compare. + EXPECT_EQ(syntax_keys, sample_keys); +} + +/// @brief Tests a duplicate entry. +/// +/// The entry was duplicated by adding a new <name>DDDD entry. +/// An error is expected, usually it is a duplicate but there are +/// a few syntax errors when the syntax allows only one parameter. +/// +/// @param json the JSON configuration with the duplicate entry. +void testDuplicate(ConstElementPtr json) { + string config = json->str(); + size_t where = config.find("DDDD"); + ASSERT_NE(string::npos, where); + string before = config.substr(0, where); + string after = config.substr(where + 4, string::npos); + ParserContext ctx; + EXPECT_THROW(ctx.parseString(before + after, + ParserContext::PARSER_NETCONF), + ParseError) << "config: " << config; +} + +// This test checks that duplicate entries make parsing to fail. +TEST(ParserTest, duplicateMapEntries) { + // Get the config to work with from the sample file. + string sample_fname(CFG_EXAMPLES); + sample_fname += "/simple-dhcp6.json"; + ParserContext ctx; + ElementPtr sample_json; + EXPECT_NO_THROW(sample_json = + ctx.parseFile(sample_fname, ParserContext::PARSER_NETCONF)); + ASSERT_TRUE(sample_json); + + // Recursively check duplicates. + static void (*test)(ElementPtr, ElementPtr, size_t&) = + [] (ElementPtr config, ElementPtr json, size_t& cnt) { + if (json->getType() == Element::list) { + // Handle lists. + for (auto elem : json->listValue()) { + test(config, elem, cnt); + } + } else if (json->getType() == Element::map) { + // Handle maps. + for (auto elem : json->mapValue()) { + // Skip entries with free content. + if ((elem.first == "user-context") || + (elem.first == "parameters")) { + continue; + } + + // Perform tests. + string dup = elem.first + "DDDD"; + json->set(dup, elem.second); + testDuplicate(config); + json->remove(dup); + ++cnt; + + // Recursive call. + ElementPtr mutable_json = + boost::const_pointer_cast<Element>(elem.second); + ASSERT_TRUE(mutable_json); + test(config, mutable_json, cnt); + } + } + }; + size_t cnt = 0; + test(sample_json, sample_json, cnt); + cout << "checked " << cnt << " duplicated map entries\n"; +} + +/// @brief Test fixture for trailing commas. +class TrailingCommasTest : public isc::dhcp::test::LogContentTest { +public: + /// @brief Add a log entry. + /// + /// @param loc Location of the trailing comma. + void addLog(const string& loc) { + string log = "NETCONF_CONFIG_SYNTAX_WARNING Netconf "; + log += "configuration syntax warning: " + loc; + log += ": Extraneous comma. "; + log += "A piece of configuration may have been omitted."; + addString(log); + } +}; + +// Test that trailing commas are allowed. +TEST_F(TrailingCommasTest, tests) { + string txt(R"({ + "Netconf": { + "boot-update": true, + "loggers": [ + { + "name": "kea-netconf", + "severity": "DEBUG", + "debuglevel": 99, + }, + ], + "managed-servers": { + "dhcp4": { + "control-socket": { + "socket-name": "/tmp/kea-dhcp4-ctrl.sock", + "socket-type": "unix", + }, + "model": "kea-dhcp4-server", + }, + "dhcp6": { + "control-socket": { + "socket-name": "/tmp/kea-dhcp6-ctrl.sock", + "socket-type": "unix", + }, + "model": "kea-dhcp6-server", + }, + }, + "subscribe-changes": true, + "validate-changes": true, + }, +})"); + testParser(txt, ParserContext::PARSER_NETCONF, false); + + addLog("<string>:8.25"); + addLog("<string>:9.8"); + addLog("<string>:15.32"); + addLog("<string>:17.36"); + addLog("<string>:22.32"); + addLog("<string>:24.36"); + addLog("<string>:25.8"); + addLog("<string>:28.29"); + addLog("<string>:29.4"); + EXPECT_TRUE(checkFile()); + + // Test with many consecutive commas. + boost::replace_all(txt, ",", ",,,,"); + testParser(txt, ParserContext::PARSER_NETCONF, false); +} + +} // namespace test +} // namespace netconf +} // namespace isc diff --git a/src/bin/netconf/tests/run_unittests.cc b/src/bin/netconf/tests/run_unittests.cc new file mode 100644 index 0000000..b735a8e --- /dev/null +++ b/src/bin/netconf/tests/run_unittests.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <gtest/gtest.h> +#include <util/unittests/run_all.h> +#include <log/logger_support.h> + +int +main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + + // See the documentation of the KEA_* environment variables in + // the developer's guide for info on how to tweak logging + isc::log::initLogger(); + + // Override --localstatedir value for PID files + setenv("KEA_PIDFILE_DIR", TEST_DATA_BUILDDIR, 1); + + int result = RUN_ALL_TESTS(); + + return (result); +} diff --git a/src/bin/netconf/tests/shtests/Makefile.am b/src/bin/netconf/tests/shtests/Makefile.am new file mode 100644 index 0000000..d05472c --- /dev/null +++ b/src/bin/netconf/tests/shtests/Makefile.am @@ -0,0 +1,15 @@ +SUBDIRS = . + +# Shell tests +SHTESTS = netconf_tests.sh + +# Run shell tests on "make check". +check_SCRIPTS = $(SHTESTS) +TESTS = $(SHTESTS) + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) + +# Don't install shell tests. +noinst_SCRIPTS = $(SHTESTS) diff --git a/src/bin/netconf/tests/shtests/Makefile.in b/src/bin/netconf/tests/shtests/Makefile.in new file mode 100644 index 0000000..8429d42 --- /dev/null +++ b/src/bin/netconf/tests/shtests/Makefile.in @@ -0,0 +1,841 @@ +# Makefile.in generated by automake 1.16.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2018 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/bin/netconf/tests/shtests +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 = netconf_tests.sh +CONFIG_CLEAN_VPATH_FILES = +SCRIPTS = $(noinst_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/netconf_tests.sh.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_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 = . + +# Shell tests +SHTESTS = netconf_tests.sh + +# Run shell tests on "make check". +check_SCRIPTS = $(SHTESTS) +TESTS = $(SHTESTS) + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) + +# Don't install shell tests. +noinst_SCRIPTS = $(SHTESTS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/bin/netconf/tests/shtests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/bin/netconf/tests/shtests/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): +netconf_tests.sh: $(top_builddir)/config.status $(srcdir)/netconf_tests.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_SCRIPTS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-recursive +all-am: Makefile $(SCRIPTS) +installdirs: installdirs-recursive +installdirs-am: +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + -rm -f Makefile +distclean-am: clean-am distclean-generic distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) check-am install-am install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-TESTS check-am clean clean-generic clean-libtool \ + cscopelist-am ctags ctags-am distclean distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/bin/netconf/tests/shtests/netconf_tests.sh.in b/src/bin/netconf/tests/shtests/netconf_tests.sh.in new file mode 100644 index 0000000..b6ba611 --- /dev/null +++ b/src/bin/netconf/tests/shtests/netconf_tests.sh.in @@ -0,0 +1,216 @@ +#!/bin/sh + +# Copyright (C) 2018-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/. + +# shellcheck disable=SC1091 +# SC1091: Not following: ... was not specified as input (see shellcheck -x). + +# shellcheck disable=SC2039 +# SC2039: In POSIX sh, 'local' is undefined. + +# Exit with error if commands exit with non-zero and if undefined variables are +# used. +set -eu + +# Path to the temporary configuration file. +CFG_FILE="@abs_top_builddir@/src/bin/netconf/tests/shtests/test_config.json" +# Path to the Kea log file. +LOG_FILE="@abs_top_builddir@/src/bin/netconf/tests/shtests/test.log" + +# Kea-netconf configuration to be stored in the configuration file. +CONFIG="{ + \"Netconf\": + { + \"managed-servers\": + { + \"dhcp4\": + { + \"comment\": \"simply use defaults...\" + } + }, + \"loggers\": [ + { + \"name\": \"kea-netconf\", + \"output_options\": [ + { + \"output\": \"$LOG_FILE\" + } + ], + \"severity\": \"INFO\" + } + ] + } +}" + +# Invalid configuration (syntax error) to check that Kea can check syntax. +CONFIG_BAD_SYNTAX="{ + \"Netconf\": + { + \"boot-update\": BOGUS + } +}" + +# Invalid configuration (invalid url) to check that Kea can check syntax. +CONFIG_BAD_VALUE="{ + \"Netconf\": + { + \"managed-servers\": + { + \"dhcp4\": + { + \"control-socket\": + { + \"socket-type\": \"http\", + \"socket-url\": \"BOGUS\" + } + } + } + } +}" + +# Set the location of the executable. +bin="kea-netconf" +bin_path="@abs_top_builddir@/src/bin/netconf" + +# Import common test library. +. "@abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh" + +# This test verifies that help can be printed out. +usage_test() { + local test_name="${1}" + local parameter="${2}" + local expected_code="${3}" + + # Log the start of the test and print test name. + test_start "${test_name}" + + # Create correct configuration file. + create_config "${CONFIG}" + + # Check it + printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\"" + run_command \ + "${bin_path}/${bin}" "${parameter}" + if [ "${EXIT_CODE}" -ne "${expected_code}" ]; then + printf 'ERROR: expected exit code %s, got %s\n' "${expected_code}" "${EXIT_CODE}" + clean_exit 1 + fi + test_finish 0 +} + +# This test verifies that no argument is not reported as a PID file error. +no_argument_test() { + local test_name="${1}" + local expected_code="${2}" + + # Log the start of the test and print test name. + test_start "${test_name}" + + # Check it + printf "Running command %s.\n" "\"${bin_path}/${bin}\"" + run_command \ + "${bin_path}/${bin}" + if [ "${EXIT_CODE}" -ne "${expected_code}" ]; then + printf 'ERROR: expected exit code %s, got %s\n' "${expected_code}" "${EXIT_CODE}" + clean_exit 1 + fi + test_finish 0 +} + +# This test verifies that syntax checking works properly. This function +# requires 3 parameters: +# test_name +# config - string with a content of the config (will be written to a file) +# expected_code - expected exit code returned by kea (0 - success, 1 - failure) +syntax_check_test() { + local test_name="${1}" + local config="${2}" + local expected_code="${3}" + + # Log the start of the test and print test name. + test_start "${test_name}" + # Create correct configuration file. + create_config "${config}" + # Check it + printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\"" + run_command \ + "${bin_path}/${bin}" -t "${CFG_FILE}" + if [ "${EXIT_CODE}" -ne "${expected_code}" ]; then + printf 'ERROR: expected exit code %s, got %s\n' "${expected_code}" "${EXIT_CODE}" + clean_exit 1 + fi + test_finish 0 +} + +# This test verifies that Netconf Agent is shut down gracefully when it +# receives a SIGINT or SIGTERM signal. +shutdown_test() { + test_name=${1} # Test name + signum=${2} # Signal number + # Log the start of the test and print test name. + test_start "${test_name}" + # Create new configuration file. + create_config "${CONFIG}" + # Instruct Netconf Agent to log to the specific file. + set_logger + # Start Netconf Agent. + start_kea ${bin_path}/${bin} + # Wait up to 20s for Netconf Agent to start. + wait_for_kea 20 + if [ "${_WAIT_FOR_KEA}" -eq 0 ]; then + printf "ERROR: timeout waiting for Netconf Agent to start.\n" + clean_exit 1 + fi + + # Check if it is still running. It could have terminated (e.g. as a result + # of configuration failure). + get_pid ${bin} + if [ "${_GET_PIDS_NUM}" -ne 1 ]; then + printf "ERROR: expected one Netconf Agent process to be started. Found %d processes\ + started.\n" "${_GET_PIDS_NUM}" + clean_exit 1 + fi + + # Check in the log file, how many times server has been configured. + # It should be just once on startup. + get_reconfigs + if [ "${_GET_RECONFIGS}" -ne 1 ]; then + printf "ERROR: server been configured %s time(s), but exactly 1 was expected.\n" "${_GET_RECONFIGS}" + clean_exit 1 + else + printf "Server successfully configured.\n" + fi + + # Send signal to Netconf Agent (SIGTERM, SIGINT etc.) + send_signal "${signum}" "${bin}" + + # Now wait for process to log that it is exiting. + wait_for_message 10 "DCTL_SHUTDOWN" 1 + if [ "${_WAIT_FOR_MESSAGE}" -eq 0 ]; then + printf "ERROR: Netconf Agent did not log shutdown.\n" + clean_exit 1 + fi + + # Make sure the server is down. + wait_for_server_down 5 ${bin} + assert_eq 1 "${_WAIT_FOR_SERVER_DOWN}" \ + "Expected wait_for_server_down return %d, returned %d" + + test_finish 0 +} + +version_test "netconf.version" +usage_test "netconf.invalid-param" "-f" 1 +usage_test "netconf.dash-h" "-h" 1 +usage_test "netconf.dash-v" "-v" 0 +no_argument_test "netconf.no-argument" 1 +shutdown_test "netconf.sigterm_test" 15 +shutdown_test "netconf.sigint_test" 2 +logger_vars_test "netconf.variables" +syntax_check_test "netconf.syntax_check_success" "${CONFIG}" 0 +syntax_check_test "netconf.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1 +syntax_check_test "netconf.syntax_check_bad_values" "${CONFIG_BAD_VALUE}" 1 diff --git a/src/bin/netconf/tests/test_data_files_config.h.in b/src/bin/netconf/tests/test_data_files_config.h.in new file mode 100644 index 0000000..78f5c60 --- /dev/null +++ b/src/bin/netconf/tests/test_data_files_config.h.in @@ -0,0 +1,9 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// @brief Path to netconf source dir +#define NETCONF_SRC_DIR "@abs_top_srcdir@/src/bin/netconf" +#define NETCONF_TEST_DATA_DIR "@abs_top_srcdir@/src/bin/netconf/tests/testdata" diff --git a/src/bin/netconf/tests/test_libraries.h.in b/src/bin/netconf/tests/test_libraries.h.in new file mode 100644 index 0000000..f2a5a65 --- /dev/null +++ b/src/bin/netconf/tests/test_libraries.h.in @@ -0,0 +1,24 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef AGENT_TEST_LIBRARIES_H +#define AGENT_TEST_LIBRARIES_H + +#include <config.h> + +namespace { + +// Names of the libraries used in these tests. These libraries are built using +// libtool, so we need to look in the hidden ".libs" directory to locate the +// .so file. Note that we access the .so file - libtool creates this as a +// like to the real shared library. + +// Basic library with context_create and three "standard" callouts. +static const char* BASIC_CALLOUT_LIBRARY = "@abs_builddir@/.libs/libbasic.so"; + +} // anonymous namespace + +#endif // TEST_LIBRARIES_H diff --git a/src/bin/netconf/tests/testdata/get_config.json b/src/bin/netconf/tests/testdata/get_config.json new file mode 100644 index 0000000..d08aed2 --- /dev/null +++ b/src/bin/netconf/tests/testdata/get_config.json @@ -0,0 +1,24 @@ +{ + "Netconf": { + "boot-update": true, + "hooks-libraries": [ ], + "managed-servers": { + "dhcp4": { + "boot-update": true, + "control-socket": { + "socket-name": "/tmp/kea4-ctrl-socket", + "socket-type": "unix", + "socket-url": "http://127.0.0.1:8000/" + }, + "model": "kea-dhcp4-server", + "subscribe-changes": true, + "user-context": { + "comment": "Kea DHCP4 server serving network on floor 13" + }, + "validate-changes": false + } + }, + "subscribe-changes": true, + "validate-changes": true + } +} |