summaryrefslogtreecommitdiffstats
path: root/src/bin/netconf
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/netconf')
-rw-r--r--src/bin/netconf/Makefile.am130
-rw-r--r--src/bin/netconf/Makefile.in1071
-rw-r--r--src/bin/netconf/control_socket.cc41
-rw-r--r--src/bin/netconf/control_socket.h131
-rw-r--r--src/bin/netconf/http_control_socket.cc130
-rw-r--r--src/bin/netconf/http_control_socket.h89
-rw-r--r--src/bin/netconf/location.hh306
-rw-r--r--src/bin/netconf/main.cc49
-rw-r--r--src/bin/netconf/netconf.cc797
-rw-r--r--src/bin/netconf/netconf.h183
-rw-r--r--src/bin/netconf/netconf_cfg_mgr.cc173
-rw-r--r--src/bin/netconf/netconf_cfg_mgr.h186
-rw-r--r--src/bin/netconf/netconf_config.cc211
-rw-r--r--src/bin/netconf/netconf_config.h328
-rw-r--r--src/bin/netconf/netconf_controller.cc75
-rw-r--r--src/bin/netconf/netconf_controller.h85
-rw-r--r--src/bin/netconf/netconf_lexer.cc3949
-rw-r--r--src/bin/netconf/netconf_lexer.ll842
-rw-r--r--src/bin/netconf/netconf_log.cc28
-rw-r--r--src/bin/netconf/netconf_log.h54
-rw-r--r--src/bin/netconf/netconf_messages.cc91
-rw-r--r--src/bin/netconf/netconf_messages.h49
-rw-r--r--src/bin/netconf/netconf_messages.mes173
-rw-r--r--src/bin/netconf/netconf_parser.cc2312
-rw-r--r--src/bin/netconf/netconf_parser.h2387
-rw-r--r--src/bin/netconf/netconf_parser.yy755
-rw-r--r--src/bin/netconf/netconf_process.cc99
-rw-r--r--src/bin/netconf/netconf_process.h106
-rw-r--r--src/bin/netconf/parser_context.cc200
-rw-r--r--src/bin/netconf/parser_context.h313
-rw-r--r--src/bin/netconf/parser_context_decl.h20
-rw-r--r--src/bin/netconf/simple_parser.cc194
-rw-r--r--src/bin/netconf/simple_parser.h74
-rw-r--r--src/bin/netconf/stdout_control_socket.cc56
-rw-r--r--src/bin/netconf/stdout_control_socket.h90
-rw-r--r--src/bin/netconf/tests/Makefile.am91
-rw-r--r--src/bin/netconf/tests/Makefile.in1224
-rw-r--r--src/bin/netconf/tests/basic_library.cc72
-rw-r--r--src/bin/netconf/tests/control_socket_unittests.cc866
-rw-r--r--src/bin/netconf/tests/get_config_unittest.cc294
-rw-r--r--src/bin/netconf/tests/netconf_cfg_mgr_unittests.cc738
-rw-r--r--src/bin/netconf/tests/netconf_controller_unittests.cc194
-rw-r--r--src/bin/netconf/tests/netconf_process_unittests.cc92
-rw-r--r--src/bin/netconf/tests/netconf_unittests.cc1212
-rw-r--r--src/bin/netconf/tests/parser_unittests.cc988
-rw-r--r--src/bin/netconf/tests/run_unittests.cc27
-rw-r--r--src/bin/netconf/tests/shtests/Makefile.am15
-rw-r--r--src/bin/netconf/tests/shtests/Makefile.in841
-rw-r--r--src/bin/netconf/tests/shtests/netconf_tests.sh.in216
-rw-r--r--src/bin/netconf/tests/test_data_files_config.h.in9
-rw-r--r--src/bin/netconf/tests/test_libraries.h.in24
-rw-r--r--src/bin/netconf/tests/testdata/get_config.json24
-rw-r--r--src/bin/netconf/unix_control_socket.cc97
-rw-r--r--src/bin/netconf/unix_control_socket.h89
54 files changed, 22890 insertions, 0 deletions
diff --git a/src/bin/netconf/Makefile.am b/src/bin/netconf/Makefile.am
new file mode 100644
index 0000000..b3b762b
--- /dev/null
+++ b/src/bin/netconf/Makefile.am
@@ -0,0 +1,130 @@
+SUBDIRS = . tests
+
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin
+AM_CPPFLAGS += -I$(top_srcdir)/src -I$(top_builddir)/src
+AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES)
+AM_CPPFLAGS += $(SYSREPO_CPPFLAGS)
+AM_CPPFLAGS += $(SYSREPO_INCLUDEDIR)
+
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+
+if USE_STATIC_LINK
+AM_LDFLAGS = -static
+endif
+
+CLEANFILES = *.gcno *.gcda
+
+#EXTRA_DIST += netconf.dox netconf_hooks.dox
+
+# convenience archive
+
+noinst_LTLIBRARIES = libnetconf.la
+
+libnetconf_la_SOURCES = control_socket.cc control_socket.h
+libnetconf_la_SOURCES += http_control_socket.cc http_control_socket.h
+libnetconf_la_SOURCES += stdout_control_socket.cc stdout_control_socket.h
+libnetconf_la_SOURCES += unix_control_socket.cc unix_control_socket.h
+libnetconf_la_SOURCES += netconf.cc netconf.h
+libnetconf_la_SOURCES += netconf_cfg_mgr.cc netconf_cfg_mgr.h
+libnetconf_la_SOURCES += netconf_config.cc netconf_config.h
+libnetconf_la_SOURCES += netconf_controller.cc netconf_controller.h
+libnetconf_la_SOURCES += netconf_log.cc netconf_log.h
+libnetconf_la_SOURCES += netconf_parser.cc netconf_parser.h
+libnetconf_la_SOURCES += netconf_process.cc netconf_process.h
+libnetconf_la_SOURCES += parser_context.cc parser_context.h
+libnetconf_la_SOURCES += parser_context_decl.h
+libnetconf_la_SOURCES += simple_parser.cc simple_parser.h
+libnetconf_la_SOURCES += location.hh
+libnetconf_la_SOURCES += netconf_lexer.ll netconf_parser.yy
+libnetconf_la_SOURCES += netconf_messages.h netconf_messages.cc
+
+EXTRA_DIST = netconf_messages.mes
+EXTRA_DIST += netconf_lexer.ll
+EXTRA_DIST += netconf_parser.yy
+
+sbin_PROGRAMS = kea-netconf
+
+kea_netconf_SOURCES = main.cc
+
+kea_netconf_LDADD = libnetconf.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/process/libkea-process.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/yang/libkea-yang.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/hooks/libkea-hooks.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/log/libkea-log.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/util/libkea-util.la
+kea_netconf_LDADD += $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
+kea_netconf_LDADD += $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS)
+
+
+kea_netconfdir = $(pkgdatadir)
+
+# If we want to get rid of all generated messages files, we need to use
+# make maintainer-clean. The proper way to introduce custom commands for
+# that operation is to define maintainer-clean-local target. However,
+# make maintainer-clean also removes Makefile, so running configure script
+# is required. To make it easy to rebuild messages without going through
+# reconfigure, a new target messages-clean has been added.
+maintainer-clean-local:
+ rm -f netconf_messages.h netconf_messages.cc
+
+# To regenerate messages files, one can do:
+#
+# make messages-clean
+# make messages
+#
+# This is needed only when a .mes file is modified.
+messages-clean: maintainer-clean-local
+
+if GENERATE_MESSAGES
+
+# Define rule to build logging source files from message file
+messages: netconf_messages.h netconf_messages.cc
+ @echo Message files regenerated
+
+netconf_messages.h netconf_messages.cc: netconf_messages.mes
+ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/bin/netconf/netconf_messages.mes
+
+else
+
+messages netconf_messages.h netconf_messages.cc:
+ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+endif
+
+if GENERATE_PARSER
+
+# Generate parser first.
+all-recursive: netconf_lexer.cc location.hh netconf_parser.cc netconf_parser.h
+
+parser: netconf_lexer.cc location.hh netconf_parser.cc netconf_parser.h
+ @echo "Flex/bison files regenerated"
+
+# --- Flex/Bison stuff below --------------------------------------------------
+# When debugging grammar issues, it's useful to add -v to bison parameters.
+# bison will generate parser.output file that explains the whole grammar.
+# It can be used to manually follow what's going on in the parser.
+# This is especially useful if yydebug_ is set to 1 as that variable
+# will cause parser to print out its internal state.
+# Call flex with -s to check that the default rule can be suppressed
+# Call bison with -W to get warnings like unmarked empty rules
+# Note C++11 deprecated register still used by flex < 2.6.0
+location.hh netconf_parser.cc netconf_parser.h: netconf_parser.yy
+ $(YACC) -Wno-yacc --defines=netconf_parser.h --report=all \
+ --report-file=netconf_parser.report -o netconf_parser.cc \
+ netconf_parser.yy
+
+netconf_lexer.cc: netconf_lexer.ll
+ $(LEX) --prefix netconf_ -o netconf_lexer.cc netconf_lexer.ll
+
+else
+
+parser location.hh netconf_parser.cc netconf_parser.h netconf_lexer.cc:
+ @echo Parser generation disabled. Configure with --enable-generate-parser to enable it.
+
+endif
diff --git a/src/bin/netconf/Makefile.in b/src/bin/netconf/Makefile.in
new file mode 100644
index 0000000..4fd1475
--- /dev/null
+++ b/src/bin/netconf/Makefile.in
@@ -0,0 +1,1071 @@
+# 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@
+sbin_PROGRAMS = kea-netconf$(EXEEXT)
+subdir = src/bin/netconf
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4macros/ax_boost_for_kea.m4 \
+ $(top_srcdir)/m4macros/ax_cpp11.m4 \
+ $(top_srcdir)/m4macros/ax_crypto.m4 \
+ $(top_srcdir)/m4macros/ax_find_library.m4 \
+ $(top_srcdir)/m4macros/ax_gssapi.m4 \
+ $(top_srcdir)/m4macros/ax_gtest.m4 \
+ $(top_srcdir)/m4macros/ax_isc_rpath.m4 \
+ $(top_srcdir)/m4macros/ax_sysrepo.m4 \
+ $(top_srcdir)/m4macros/libtool.m4 \
+ $(top_srcdir)/m4macros/ltoptions.m4 \
+ $(top_srcdir)/m4macros/ltsugar.m4 \
+ $(top_srcdir)/m4macros/ltversion.m4 \
+ $(top_srcdir)/m4macros/lt~obsolete.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)"
+PROGRAMS = $(sbin_PROGRAMS)
+LTLIBRARIES = $(noinst_LTLIBRARIES)
+libnetconf_la_LIBADD =
+am_libnetconf_la_OBJECTS = control_socket.lo http_control_socket.lo \
+ stdout_control_socket.lo unix_control_socket.lo netconf.lo \
+ netconf_cfg_mgr.lo netconf_config.lo netconf_controller.lo \
+ netconf_log.lo netconf_parser.lo netconf_process.lo \
+ parser_context.lo simple_parser.lo netconf_lexer.lo \
+ netconf_parser.lo netconf_messages.lo
+libnetconf_la_OBJECTS = $(am_libnetconf_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 =
+am_kea_netconf_OBJECTS = main.$(OBJEXT)
+kea_netconf_OBJECTS = $(am_kea_netconf_OBJECTS)
+am__DEPENDENCIES_1 =
+kea_netconf_DEPENDENCIES = libnetconf.la \
+ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \
+ $(top_builddir)/src/lib/process/libkea-process.la \
+ $(top_builddir)/src/lib/yang/libkea-yang.la \
+ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+ $(top_builddir)/src/lib/http/libkea-http.la \
+ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+ $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/log/libkea-log.la \
+ $(top_builddir)/src/lib/util/libkea-util.la \
+ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+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)/control_socket.Plo \
+ ./$(DEPDIR)/http_control_socket.Plo ./$(DEPDIR)/main.Po \
+ ./$(DEPDIR)/netconf.Plo ./$(DEPDIR)/netconf_cfg_mgr.Plo \
+ ./$(DEPDIR)/netconf_config.Plo \
+ ./$(DEPDIR)/netconf_controller.Plo \
+ ./$(DEPDIR)/netconf_lexer.Plo ./$(DEPDIR)/netconf_log.Plo \
+ ./$(DEPDIR)/netconf_messages.Plo \
+ ./$(DEPDIR)/netconf_parser.Plo ./$(DEPDIR)/netconf_process.Plo \
+ ./$(DEPDIR)/parser_context.Plo ./$(DEPDIR)/simple_parser.Plo \
+ ./$(DEPDIR)/stdout_control_socket.Plo \
+ ./$(DEPDIR)/unix_control_socket.Plo
+am__mv = mv -f
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_@AM_V@)
+am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@)
+am__v_CXX_0 = @echo " CXX " $@;
+am__v_CXX_1 =
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
+am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+am__v_CXXLD_1 =
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo " LEX " $@;
+am__v_LEX_1 =
+YLWRAP = $(top_srcdir)/ylwrap
+am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
+ -e s/c++$$/h++/ -e s/c$$/h/
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo " YACC " $@;
+am__v_YACC_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 = $(libnetconf_la_SOURCES) $(kea_netconf_SOURCES)
+DIST_SOURCES = $(libnetconf_la_SOURCES) $(kea_netconf_SOURCES)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir distdir-am
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \
+ $(top_srcdir)/ylwrap netconf_lexer.cc netconf_parser.cc
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ASCIIDOC = @ASCIIDOC@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BOOST_INCLUDES = @BOOST_INCLUDES@
+BOOST_LIBS = @BOOST_LIBS@
+BOTAN_TOOL = @BOTAN_TOOL@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CONTRIB_DIR = @CONTRIB_DIR@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_INCLUDES = @CRYPTO_INCLUDES@
+CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CRYPTO_PACKAGE = @CRYPTO_PACKAGE@
+CRYPTO_RPATH = @CRYPTO_RPATH@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@
+DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@
+DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@
+DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@
+DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@
+DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@
+DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@
+DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GTEST_CONFIG = @GTEST_CONFIG@
+GTEST_INCLUDES = @GTEST_INCLUDES@
+GTEST_LDADD = @GTEST_LDADD@
+GTEST_LDFLAGS = @GTEST_LDFLAGS@
+GTEST_SOURCE = @GTEST_SOURCE@
+HAVE_SYSREPO = @HAVE_SYSREPO@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+KEA_CXXFLAGS = @KEA_CXXFLAGS@
+KEA_SRCID = @KEA_SRCID@
+KRB5_CONFIG = @KRB5_CONFIG@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@
+LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LIBS = @MYSQL_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PDFLATEX = @PDFLATEX@
+PERL = @PERL@
+PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@
+PGSQL_LIBS = @PGSQL_LIBS@
+PKGPYTHONDIR = @PKGPYTHONDIR@
+PKG_CONFIG = @PKG_CONFIG@
+PLANTUML = @PLANTUML@
+PREMIUM_DIR = @PREMIUM_DIR@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SEP = @SEP@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPHINXBUILD = @SPHINXBUILD@
+SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@
+SR_REPO_PATH = @SR_REPO_PATH@
+STRIP = @STRIP@
+SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@
+SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@
+SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@
+SYSREPO_LIBS = @SYSREPO_LIBS@
+SYSREPO_VERSION = @SYSREPO_VERSION@
+USE_LCOV = @USE_LCOV@
+VALGRIND = @VALGRIND@
+VERSION = @VERSION@
+WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@
+YACC = @YACC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUBDIRS = . tests
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib \
+ -I$(top_srcdir)/src/bin -I$(top_builddir)/src/bin \
+ -I$(top_srcdir)/src -I$(top_builddir)/src $(BOOST_INCLUDES) \
+ $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) $(SYSREPO_CPPFLAGS) \
+ $(SYSREPO_INCLUDEDIR)
+AM_CXXFLAGS = $(KEA_CXXFLAGS)
+@USE_STATIC_LINK_TRUE@AM_LDFLAGS = -static
+CLEANFILES = *.gcno *.gcda
+
+#EXTRA_DIST += netconf.dox netconf_hooks.dox
+
+# convenience archive
+noinst_LTLIBRARIES = libnetconf.la
+libnetconf_la_SOURCES = control_socket.cc control_socket.h \
+ http_control_socket.cc http_control_socket.h \
+ stdout_control_socket.cc stdout_control_socket.h \
+ unix_control_socket.cc unix_control_socket.h netconf.cc \
+ netconf.h netconf_cfg_mgr.cc netconf_cfg_mgr.h \
+ netconf_config.cc netconf_config.h netconf_controller.cc \
+ netconf_controller.h netconf_log.cc netconf_log.h \
+ netconf_parser.cc netconf_parser.h netconf_process.cc \
+ netconf_process.h parser_context.cc parser_context.h \
+ parser_context_decl.h simple_parser.cc simple_parser.h \
+ location.hh netconf_lexer.ll netconf_parser.yy \
+ netconf_messages.h netconf_messages.cc
+EXTRA_DIST = netconf_messages.mes netconf_lexer.ll netconf_parser.yy
+kea_netconf_SOURCES = main.cc
+kea_netconf_LDADD = libnetconf.la \
+ $(top_builddir)/src/lib/cfgrpt/libcfgrpt.la \
+ $(top_builddir)/src/lib/process/libkea-process.la \
+ $(top_builddir)/src/lib/yang/libkea-yang.la \
+ $(top_builddir)/src/lib/config/libkea-cfgclient.la \
+ $(top_builddir)/src/lib/http/libkea-http.la \
+ $(top_builddir)/src/lib/hooks/libkea-hooks.la \
+ $(top_builddir)/src/lib/cc/libkea-cc.la \
+ $(top_builddir)/src/lib/asiolink/libkea-asiolink.la \
+ $(top_builddir)/src/lib/log/libkea-log.la \
+ $(top_builddir)/src/lib/util/libkea-util.la \
+ $(top_builddir)/src/lib/exceptions/libkea-exceptions.la \
+ $(LOG4CPLUS_LIBS) $(CRYPTO_LIBS) $(BOOST_LIBS) $(SYSREPO_LIBS)
+kea_netconfdir = $(pkgdatadir)
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .cc .ll .lo .o .obj .yy
+$(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/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/bin/netconf/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_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}; \
+ }
+
+libnetconf.la: $(libnetconf_la_OBJECTS) $(libnetconf_la_DEPENDENCIES) $(EXTRA_libnetconf_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(CXXLINK) $(libnetconf_la_OBJECTS) $(libnetconf_la_LIBADD) $(LIBS)
+
+kea-netconf$(EXEEXT): $(kea_netconf_OBJECTS) $(kea_netconf_DEPENDENCIES) $(EXTRA_kea_netconf_DEPENDENCIES)
+ @rm -f kea-netconf$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(kea_netconf_OBJECTS) $(kea_netconf_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/control_socket.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http_control_socket.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_cfg_mgr.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_config.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_controller.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_lexer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_log.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_messages.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netconf_process.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parser_context.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/simple_parser.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stdout_control_socket.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/unix_control_socket.Plo@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+
+.ll.cc:
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+
+.yy.cc:
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS) $(LTLIBRARIES)
+installdirs: installdirs-recursive
+installdirs-am:
+ for dir in "$(DESTDIR)$(sbindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -f netconf_lexer.cc
+ -rm -f netconf_parser.cc
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \
+ clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f ./$(DEPDIR)/control_socket.Plo
+ -rm -f ./$(DEPDIR)/http_control_socket.Plo
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/netconf.Plo
+ -rm -f ./$(DEPDIR)/netconf_cfg_mgr.Plo
+ -rm -f ./$(DEPDIR)/netconf_config.Plo
+ -rm -f ./$(DEPDIR)/netconf_controller.Plo
+ -rm -f ./$(DEPDIR)/netconf_lexer.Plo
+ -rm -f ./$(DEPDIR)/netconf_log.Plo
+ -rm -f ./$(DEPDIR)/netconf_messages.Plo
+ -rm -f ./$(DEPDIR)/netconf_parser.Plo
+ -rm -f ./$(DEPDIR)/netconf_process.Plo
+ -rm -f ./$(DEPDIR)/parser_context.Plo
+ -rm -f ./$(DEPDIR)/simple_parser.Plo
+ -rm -f ./$(DEPDIR)/stdout_control_socket.Plo
+ -rm -f ./$(DEPDIR)/unix_control_socket.Plo
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+
+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)/control_socket.Plo
+ -rm -f ./$(DEPDIR)/http_control_socket.Plo
+ -rm -f ./$(DEPDIR)/main.Po
+ -rm -f ./$(DEPDIR)/netconf.Plo
+ -rm -f ./$(DEPDIR)/netconf_cfg_mgr.Plo
+ -rm -f ./$(DEPDIR)/netconf_config.Plo
+ -rm -f ./$(DEPDIR)/netconf_controller.Plo
+ -rm -f ./$(DEPDIR)/netconf_lexer.Plo
+ -rm -f ./$(DEPDIR)/netconf_log.Plo
+ -rm -f ./$(DEPDIR)/netconf_messages.Plo
+ -rm -f ./$(DEPDIR)/netconf_parser.Plo
+ -rm -f ./$(DEPDIR)/netconf_process.Plo
+ -rm -f ./$(DEPDIR)/parser_context.Plo
+ -rm -f ./$(DEPDIR)/simple_parser.Plo
+ -rm -f ./$(DEPDIR)/stdout_control_socket.Plo
+ -rm -f ./$(DEPDIR)/unix_control_socket.Plo
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic \
+ maintainer-clean-local
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-sbinPROGRAMS
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--depfiles check check-am clean clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES clean-sbinPROGRAMS 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-sbinPROGRAMS install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic \
+ maintainer-clean-local mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+# If we want to get rid of all generated messages files, we need to use
+# make maintainer-clean. The proper way to introduce custom commands for
+# that operation is to define maintainer-clean-local target. However,
+# make maintainer-clean also removes Makefile, so running configure script
+# is required. To make it easy to rebuild messages without going through
+# reconfigure, a new target messages-clean has been added.
+maintainer-clean-local:
+ rm -f netconf_messages.h netconf_messages.cc
+
+# To regenerate messages files, one can do:
+#
+# make messages-clean
+# make messages
+#
+# This is needed only when a .mes file is modified.
+messages-clean: maintainer-clean-local
+
+# Define rule to build logging source files from message file
+@GENERATE_MESSAGES_TRUE@messages: netconf_messages.h netconf_messages.cc
+@GENERATE_MESSAGES_TRUE@ @echo Message files regenerated
+
+@GENERATE_MESSAGES_TRUE@netconf_messages.h netconf_messages.cc: netconf_messages.mes
+@GENERATE_MESSAGES_TRUE@ $(top_builddir)/src/lib/log/compiler/kea-msg-compiler $(top_srcdir)/src/bin/netconf/netconf_messages.mes
+
+@GENERATE_MESSAGES_FALSE@messages netconf_messages.h netconf_messages.cc:
+@GENERATE_MESSAGES_FALSE@ @echo Messages generation disabled. Configure with --enable-generate-messages to enable it.
+
+# Generate parser first.
+@GENERATE_PARSER_TRUE@all-recursive: netconf_lexer.cc location.hh netconf_parser.cc netconf_parser.h
+
+@GENERATE_PARSER_TRUE@parser: netconf_lexer.cc location.hh netconf_parser.cc netconf_parser.h
+@GENERATE_PARSER_TRUE@ @echo "Flex/bison files regenerated"
+
+# --- Flex/Bison stuff below --------------------------------------------------
+# When debugging grammar issues, it's useful to add -v to bison parameters.
+# bison will generate parser.output file that explains the whole grammar.
+# It can be used to manually follow what's going on in the parser.
+# This is especially useful if yydebug_ is set to 1 as that variable
+# will cause parser to print out its internal state.
+# Call flex with -s to check that the default rule can be suppressed
+# Call bison with -W to get warnings like unmarked empty rules
+# Note C++11 deprecated register still used by flex < 2.6.0
+@GENERATE_PARSER_TRUE@location.hh netconf_parser.cc netconf_parser.h: netconf_parser.yy
+@GENERATE_PARSER_TRUE@ $(YACC) -Wno-yacc --defines=netconf_parser.h --report=all \
+@GENERATE_PARSER_TRUE@ --report-file=netconf_parser.report -o netconf_parser.cc \
+@GENERATE_PARSER_TRUE@ netconf_parser.yy
+
+@GENERATE_PARSER_TRUE@netconf_lexer.cc: netconf_lexer.ll
+@GENERATE_PARSER_TRUE@ $(LEX) --prefix netconf_ -o netconf_lexer.cc netconf_lexer.ll
+
+@GENERATE_PARSER_FALSE@parser location.hh netconf_parser.cc netconf_parser.h netconf_lexer.cc:
+@GENERATE_PARSER_FALSE@ @echo Parser generation disabled. Configure with --enable-generate-parser to enable it.
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/bin/netconf/control_socket.cc b/src/bin/netconf/control_socket.cc
new file mode 100644
index 0000000..56a90ac
--- /dev/null
+++ b/src/bin/netconf/control_socket.cc
@@ -0,0 +1,41 @@
+// 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/.
+
+/// @file stdout_control_socket.cc
+/// Contains the stdout derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/control_socket.h>
+#include <netconf/http_control_socket.h>
+#include <netconf/stdout_control_socket.h>
+#include <netconf/unix_control_socket.h>
+
+using namespace std;
+
+namespace isc {
+namespace netconf {
+
+ControlSocketBasePtr
+controlSocketFactory(CfgControlSocketPtr ctrl_sock) {
+ if (!ctrl_sock) {
+ isc_throw(BadValue, "null control socket configuration");
+ }
+ CfgControlSocket::Type sock_type = ctrl_sock->getType();
+ switch (sock_type) {
+ case CfgControlSocket::Type::UNIX:
+ return (createControlSocket<CfgControlSocket::Type::UNIX>(ctrl_sock));
+ case CfgControlSocket::Type::HTTP:
+ return (createControlSocket<CfgControlSocket::Type::HTTP>(ctrl_sock));
+ case CfgControlSocket::Type::STDOUT:
+ return (createControlSocket<CfgControlSocket::Type::STDOUT>(ctrl_sock));
+ default:
+ isc_throw(BadValue, "Unknown control socket type: " << sock_type);
+ }
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/control_socket.h b/src/bin/netconf/control_socket.h
new file mode 100644
index 0000000..a3741a6
--- /dev/null
+++ b/src/bin/netconf/control_socket.h
@@ -0,0 +1,131 @@
+// 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/.
+
+/// @file control_socket.h
+/// Contains declarations for control socket communication.
+
+#ifndef CONTROL_SOCKET_H
+#define CONTROL_SOCKET_H
+
+#include <netconf/netconf_config.h>
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Exception thrown when the error during communication.
+class ControlSocketError : public isc::Exception {
+public:
+ ControlSocketError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Base class for control socket communication.
+///
+/// This class is the base class for control socket communication.
+/// Derived classes implement config-get, config-test and config-set
+/// using control sockets of different types. Those classes are used to
+/// communicate with other Kea daemons.
+class ControlSocketBase {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param ctrl_sock The control socket configuration.
+ /// @throw ControlSocketError if ctrl_sock is null.
+ ControlSocketBase(CfgControlSocketPtr ctrl_sock) : socket_cfg_(ctrl_sock) {
+ if (!ctrl_sock) {
+ isc_throw(ControlSocketError, "ControlSocket constructor called "
+ "with a null configuration");
+ }
+ }
+
+ /// @brief Destructor (does nothing).
+ virtual ~ControlSocketBase() {
+ }
+
+ /// @brief Getter which returns the socket type.
+ ///
+ /// @return returns the socket type as a CfgControlSocket::Type.
+ CfgControlSocket::Type getType() const {
+ return (socket_cfg_->getType());
+ }
+
+ /// @brief Returns the Unix socket name.
+ ///
+ /// @return returns the Unix socket name as a std::string.
+ const std::string getName() const {
+ return (socket_cfg_->getName());
+ }
+
+ /// @brief Returns the HTTP server URL.
+ ///
+ /// @return returns the HTTP server URL as an isc::http::Url.
+ const isc::http::Url getUrl() const {
+ return (socket_cfg_->getUrl());
+ }
+
+ /// @brief Get configuration.
+ ///
+ /// Call config-get over the control socket.
+ ///
+ /// @param service The target service (used by http).
+ /// @return The JSON element answer of config-get.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configGet(const std::string& service) = 0;
+
+ /// @brief Test configuration.
+ ///
+ /// Call config-test over the control socket.
+ ///
+ /// @param config The configuration to test.
+ /// @param service The target service (used by http).
+ /// @return The JSON element answer of config-test.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+ const std::string& service) = 0;
+
+ /// @brief Set configuration.
+ ///
+ /// Call config-set over the control socket.
+ ///
+ /// @param config The configuration to set.
+ /// @param service The target service (used by http).
+ /// @return The JSON element answer of config-set.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+ const std::string& service) = 0;
+
+ /// @brief The control socket configuration.
+ CfgControlSocketPtr socket_cfg_;
+};
+
+/// @brief Type definition for the pointer to the @c ControlSocketBase.
+typedef boost::shared_ptr<ControlSocketBase> ControlSocketBasePtr;
+
+/// @brief Factory template for control sockets.
+///
+/// @tparam TYPE The control socket type.
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a control socket communication object.
+/// @throw NotImplemented if no specialization was called.
+template <CfgControlSocket::Type TYPE> ControlSocketBasePtr
+createControlSocket(CfgControlSocketPtr ctrl_sock) {
+ isc_throw(NotImplemented, "not specialized createControlSocket");
+}
+
+/// @brief Factory function for control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a control socket communication object.
+/// @throw BadValue if called with null or an unknown type.
+ControlSocketBasePtr
+controlSocketFactory(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // CONTROL_SOCKET_H
diff --git a/src/bin/netconf/http_control_socket.cc b/src/bin/netconf/http_control_socket.cc
new file mode 100644
index 0000000..ec1f9ae
--- /dev/null
+++ b/src/bin/netconf/http_control_socket.cc
@@ -0,0 +1,130 @@
+// 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/.
+
+/// @file http_control_socket.cc
+/// Contains the HTTP socket derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/http_control_socket.h>
+#include <cc/command_interpreter.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tls_socket.h>
+#include <http/client.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+#include <config/timeouts.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+
+namespace isc {
+namespace netconf {
+
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::HTTP>(CfgControlSocketPtr ctrl_sock) {
+ return (HttpControlSocketPtr(new HttpControlSocket(ctrl_sock)));
+}
+
+HttpControlSocket::HttpControlSocket(CfgControlSocketPtr ctrl_sock)
+ : ControlSocketBase(ctrl_sock) {
+}
+
+ConstElementPtr
+HttpControlSocket::configGet(const string& service) {
+ if (service == "ca") {
+ return (sendCommand(createCommand("config-get")));
+ } else {
+ return (sendCommand(createCommand("config-get", service)));
+ }
+}
+
+ConstElementPtr
+HttpControlSocket::configTest(ConstElementPtr config, const string& service) {
+ if (service == "ca") {
+ return (sendCommand(createCommand("config-test", config)));
+ } else {
+ return (sendCommand(createCommand("config-test", config, service)));
+ }
+}
+
+ConstElementPtr
+HttpControlSocket::configSet(ConstElementPtr config, const string& service) {
+ if (service == "ca") {
+ return (sendCommand(createCommand("config-set", config)));
+ } else {
+ return (sendCommand(createCommand("config-set", config, service)));
+ }
+}
+
+ConstElementPtr
+HttpControlSocket::sendCommand(ConstElementPtr command) {
+ PostHttpRequestJsonPtr request;
+ request.reset(new PostHttpRequestJson(HttpRequest::Method::HTTP_POST,
+ "/",
+ HttpVersion(1, 1)));
+ request->setBodyAsJson(command);
+ try {
+ request->finalize();
+ } catch (const std::exception& ex) {
+ isc_throw(ControlSocketError, "failed to create request: "
+ << ex.what());
+ }
+
+ IOServicePtr io_service(new IOService());
+ HttpClient client(*io_service);
+ boost::system::error_code received_ec;
+ string receive_errmsg;
+ HttpResponseJsonPtr response(new HttpResponseJson());
+
+ client.asyncSendRequest(getUrl(), TlsContextPtr(), request, response,
+ [&io_service, &received_ec, &receive_errmsg]
+ (const boost::system::error_code& ec,
+ const HttpResponsePtr&, const string& errmsg) {
+ // Capture error code and message.
+ received_ec = ec;
+ receive_errmsg = errmsg;
+ // Got the IO service so stop IO service.
+ // This causes to stop IO service when
+ // all handlers have been invoked.
+ io_service->stopWork();
+ },
+ HttpClient::RequestTimeout(TIMEOUT_AGENT_FORWARD_COMMAND));
+
+ // Perform this synchronously.
+ io_service->run();
+
+ if (received_ec) {
+ // Got an error code.
+ isc_throw(ControlSocketError, "communication error (code): "
+ << received_ec.message());
+ }
+
+ if (!receive_errmsg.empty()) {
+ // Got an error message.
+ isc_throw(ControlSocketError, "communication error (message): "
+ << receive_errmsg);
+ }
+
+ if (!response) {
+ // Failed to get the answer.
+ isc_throw(ControlSocketError, "empty response");
+ }
+
+ try {
+ return (response->getBodyAsJson());
+ } catch (const std::exception& ex) {
+ isc_throw(ControlSocketError, "unparsable response: " << ex.what());
+ }
+}
+
+} // namespace netconf
+} // namespace isc
+
diff --git a/src/bin/netconf/http_control_socket.h b/src/bin/netconf/http_control_socket.h
new file mode 100644
index 0000000..74c5761
--- /dev/null
+++ b/src/bin/netconf/http_control_socket.h
@@ -0,0 +1,89 @@
+// 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/.
+
+/// @file http_control_socket.h
+/// Contains declarations for HTTP control socket communication.
+
+#ifndef HTTP_CONTROL_SOCKET_H
+#define HTTP_CONTROL_SOCKET_H
+
+#include <netconf/control_socket.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Class for control socket communication over HTTP socket.
+///
+/// This class is the derived class for control socket communication
+/// over HTTP sockets.
+/// This class implements config-get, config-test and config-set.
+class HttpControlSocket : public ControlSocketBase {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param ctrl_sock The control socket configuration.
+ HttpControlSocket(CfgControlSocketPtr ctrl_sock);
+
+ /// @brief Destructor (does nothing).
+ virtual ~HttpControlSocket() = default;
+
+ /// @brief Get configuration.
+ ///
+ /// Call config-get over the control socket.
+ ///
+ /// @param service The target service.
+ /// @return The JSON element answer of config-get.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configGet(const std::string& service);
+
+ /// @brief Test configuration.
+ ///
+ /// Call config-test over the control socket.
+ ///
+ /// @param config The configuration to test.
+ /// @param service The target service.
+ /// @return The JSON element answer of config-test.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+ const std::string& service);
+
+ /// @brief Set configuration.
+ ///
+ /// Call config-set over the control socket.
+ ///
+ /// @param config The configuration to set.
+ /// @param service The target service.
+ /// @return The JSON element answer of config-set.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+ const std::string& service);
+
+private:
+ /// @brief Perform the actual communication.
+ ///
+ /// @todo Use persistent connections (vs. a new connection per call)
+ /// as the HTTP library supports them.
+ ///
+ /// @param command The command to send.
+ /// @return The answer.
+ data::ConstElementPtr sendCommand(data::ConstElementPtr command);
+};
+
+/// @brief Type definition for the pointer to the @c HttpControlSocket.
+typedef boost::shared_ptr<HttpControlSocket> HttpControlSocketPtr;
+
+/// @brief Factory template specialization for http control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a http control socket communication object.
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::HTTP>(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // HTTP_CONTROL_SOCKET_H
diff --git a/src/bin/netconf/location.hh b/src/bin/netconf/location.hh
new file mode 100644
index 0000000..842a111
--- /dev/null
+++ b/src/bin/netconf/location.hh
@@ -0,0 +1,306 @@
+// A Bison parser, made by GNU Bison 3.8.2.
+
+// Locations for Bison parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton. Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+/**
+ ** \file location.hh
+ ** Define the isc::netconf::location class.
+ */
+
+#ifndef YY_NETCONF_LOCATION_HH_INCLUDED
+# define YY_NETCONF_LOCATION_HH_INCLUDED
+
+# include <iostream>
+# include <string>
+
+# ifndef YY_NULLPTR
+# if defined __cplusplus
+# if 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# else
+# define YY_NULLPTR ((void*)0)
+# endif
+# endif
+
+#line 14 "netconf_parser.yy"
+namespace isc { namespace netconf {
+#line 59 "location.hh"
+
+ /// A point in a source file.
+ class position
+ {
+ public:
+ /// Type for file name.
+ typedef const std::string filename_type;
+ /// Type for line and column numbers.
+ typedef int counter_type;
+
+ /// Construct a position.
+ explicit position (filename_type* f = YY_NULLPTR,
+ counter_type l = 1,
+ counter_type c = 1)
+ : filename (f)
+ , line (l)
+ , column (c)
+ {}
+
+
+ /// Initialization.
+ void initialize (filename_type* fn = YY_NULLPTR,
+ counter_type l = 1,
+ counter_type c = 1)
+ {
+ filename = fn;
+ line = l;
+ column = c;
+ }
+
+ /** \name Line and Column related manipulators
+ ** \{ */
+ /// (line related) Advance to the COUNT next lines.
+ void lines (counter_type count = 1)
+ {
+ if (count)
+ {
+ column = 1;
+ line = add_ (line, count, 1);
+ }
+ }
+
+ /// (column related) Advance to the COUNT next columns.
+ void columns (counter_type count = 1)
+ {
+ column = add_ (column, count, 1);
+ }
+ /** \} */
+
+ /// File name to which this position refers.
+ filename_type* filename;
+ /// Current line number.
+ counter_type line;
+ /// Current column number.
+ counter_type column;
+
+ private:
+ /// Compute max (min, lhs+rhs).
+ static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min)
+ {
+ return lhs + rhs < min ? min : lhs + rhs;
+ }
+ };
+
+ /// Add \a width columns, in place.
+ inline position&
+ operator+= (position& res, position::counter_type width)
+ {
+ res.columns (width);
+ return res;
+ }
+
+ /// Add \a width columns.
+ inline position
+ operator+ (position res, position::counter_type width)
+ {
+ return res += width;
+ }
+
+ /// Subtract \a width columns, in place.
+ inline position&
+ operator-= (position& res, position::counter_type width)
+ {
+ return res += -width;
+ }
+
+ /// Subtract \a width columns.
+ inline position
+ operator- (position res, position::counter_type width)
+ {
+ return res -= width;
+ }
+
+ /** \brief Intercept output stream redirection.
+ ** \param ostr the destination output stream
+ ** \param pos a reference to the position to redirect
+ */
+ template <typename YYChar>
+ std::basic_ostream<YYChar>&
+ operator<< (std::basic_ostream<YYChar>& ostr, const position& pos)
+ {
+ if (pos.filename)
+ ostr << *pos.filename << ':';
+ return ostr << pos.line << '.' << pos.column;
+ }
+
+ /// Two points in a source file.
+ class location
+ {
+ public:
+ /// Type for file name.
+ typedef position::filename_type filename_type;
+ /// Type for line and column numbers.
+ typedef position::counter_type counter_type;
+
+ /// Construct a location from \a b to \a e.
+ location (const position& b, const position& e)
+ : begin (b)
+ , end (e)
+ {}
+
+ /// Construct a 0-width location in \a p.
+ explicit location (const position& p = position ())
+ : begin (p)
+ , end (p)
+ {}
+
+ /// Construct a 0-width location in \a f, \a l, \a c.
+ explicit location (filename_type* f,
+ counter_type l = 1,
+ counter_type c = 1)
+ : begin (f, l, c)
+ , end (f, l, c)
+ {}
+
+
+ /// Initialization.
+ void initialize (filename_type* f = YY_NULLPTR,
+ counter_type l = 1,
+ counter_type c = 1)
+ {
+ begin.initialize (f, l, c);
+ end = begin;
+ }
+
+ /** \name Line and Column related manipulators
+ ** \{ */
+ public:
+ /// Reset initial location to final location.
+ void step ()
+ {
+ begin = end;
+ }
+
+ /// Extend the current location to the COUNT next columns.
+ void columns (counter_type count = 1)
+ {
+ end += count;
+ }
+
+ /// Extend the current location to the COUNT next lines.
+ void lines (counter_type count = 1)
+ {
+ end.lines (count);
+ }
+ /** \} */
+
+
+ public:
+ /// Beginning of the located region.
+ position begin;
+ /// End of the located region.
+ position end;
+ };
+
+ /// Join two locations, in place.
+ inline location&
+ operator+= (location& res, const location& end)
+ {
+ res.end = end.end;
+ return res;
+ }
+
+ /// Join two locations.
+ inline location
+ operator+ (location res, const location& end)
+ {
+ return res += end;
+ }
+
+ /// Add \a width columns to the end position, in place.
+ inline location&
+ operator+= (location& res, location::counter_type width)
+ {
+ res.columns (width);
+ return res;
+ }
+
+ /// Add \a width columns to the end position.
+ inline location
+ operator+ (location res, location::counter_type width)
+ {
+ return res += width;
+ }
+
+ /// Subtract \a width columns to the end position, in place.
+ inline location&
+ operator-= (location& res, location::counter_type width)
+ {
+ return res += -width;
+ }
+
+ /// Subtract \a width columns to the end position.
+ inline location
+ operator- (location res, location::counter_type width)
+ {
+ return res -= width;
+ }
+
+ /** \brief Intercept output stream redirection.
+ ** \param ostr the destination output stream
+ ** \param loc a reference to the location to redirect
+ **
+ ** Avoid duplicate information.
+ */
+ template <typename YYChar>
+ std::basic_ostream<YYChar>&
+ operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
+ {
+ location::counter_type end_col
+ = 0 < loc.end.column ? loc.end.column - 1 : 0;
+ ostr << loc.begin;
+ if (loc.end.filename
+ && (!loc.begin.filename
+ || *loc.begin.filename != *loc.end.filename))
+ ostr << '-' << loc.end.filename << ':' << loc.end.line << '.' << end_col;
+ else if (loc.begin.line < loc.end.line)
+ ostr << '-' << loc.end.line << '.' << end_col;
+ else if (loc.begin.column < end_col)
+ ostr << '-' << end_col;
+ return ostr;
+ }
+
+#line 14 "netconf_parser.yy"
+} } // isc::netconf
+#line 305 "location.hh"
+
+#endif // !YY_NETCONF_LOCATION_HH_INCLUDED
diff --git a/src/bin/netconf/main.cc b/src/bin/netconf/main.cc
new file mode 100644
index 0000000..3481b5b
--- /dev/null
+++ b/src/bin/netconf/main.cc
@@ -0,0 +1,49 @@
+// 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_controller.h>
+#include <exceptions/exceptions.h>
+#include <cstdlib>
+#include <iostream>
+
+using namespace isc::netconf;
+using namespace isc::process;
+
+int main(int argc, char* argv[]) {
+ int ret = EXIT_SUCCESS;
+
+ // Launch the controller passing in command line arguments.
+ // Exit program with the controller's return code.
+ try {
+ // Instantiate/fetch the application controller singleton.
+ DControllerBasePtr& controller = NetconfController::instance();
+
+ // 'false' value disables test mode.
+ controller->launch(argc, argv, false);
+ } catch (const VersionMessage& ex) {
+ std::string msg(ex.what());
+ if (!msg.empty()) {
+ std::cout << msg << std::endl;
+ }
+ } catch (const InvalidUsage& ex) {
+ std::string msg(ex.what());
+ if (!msg.empty()) {
+ std::cerr << msg << std::endl;
+ }
+ ret = EXIT_FAILURE;
+ } catch (const std::exception& ex) {
+ std::cerr << "Service failed: " << ex.what() << std::endl;
+ ret = EXIT_FAILURE;
+ } catch (...) {
+ std::cerr << "Service failed" << std::endl;
+ ret = EXIT_FAILURE;
+ }
+
+ NetconfController::instance().reset();
+
+ return (ret);
+}
diff --git a/src/bin/netconf/netconf.cc b/src/bin/netconf/netconf.cc
new file mode 100644
index 0000000..42b6d3e
--- /dev/null
+++ b/src/bin/netconf/netconf.cc
@@ -0,0 +1,797 @@
+// 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/.
+
+/// @file netconf.cc
+/// Contains the Netconf agent methods.
+
+#include <config.h>
+
+#include <netconf/netconf.h>
+#include <netconf/netconf_controller.h>
+#include <netconf/netconf_log.h>
+#include <cc/command_interpreter.h>
+#include <yang/translator_config.h>
+#include <yang/yang_revisions.h>
+
+#include <boost/algorithm/string.hpp>
+
+#include <sstream>
+
+using namespace std;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::netconf;
+using namespace isc::yang;
+using namespace sysrepo;
+
+using libyang::S_Context;
+using libyang::S_Module;
+
+namespace {
+
+/// @brief Module change subscription callback.
+class NetconfAgentCallback {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param service_pair The service name and configuration pair.
+ NetconfAgentCallback(const CfgServersMapPair& service_pair)
+ : service_pair_(service_pair) {
+ }
+
+ /// @brief Server name and configuration pair.
+ CfgServersMapPair const service_pair_;
+
+ /// @brief Module configuration change callback.
+ ///
+ /// This callback is called by sysrepo when there is a change to
+ /// module configuration.
+ ///
+ /// @param sess The running datastore session.
+ /// @param module_name The module name.
+ /// @param event The event.
+ /// @param private_ctx The private context.
+ /// @return the sysrepo return code.
+ sr_error_t module_change(S_Session sess,
+ const char* module_name,
+ const char* /* xpath */,
+ sr_event_t event,
+ void* /* private_ctx */) {
+ ostringstream event_type;
+ switch (event) {
+ case SR_EV_UPDATE:
+ // This could potentially be a hook point for mid-flight
+ // configuration changes.
+ event_type << "SR_EV_UPDATE";
+ break;
+ case SR_EV_CHANGE:
+ event_type << "SR_EV_CHANGE";
+ break;
+ case SR_EV_DONE:
+ event_type << "SR_EV_DONE";
+ break;
+ case SR_EV_ABORT:
+ event_type << "SR_EV_ABORT";
+ break;
+ case SR_EV_ENABLED:
+ event_type << "SR_EV_ENABLED";
+ break;
+ case SR_EV_RPC:
+ event_type << "SR_EV_RPC";
+ break;
+ default:
+ event_type << "UNKNOWN (" << event << ")";
+ break;
+ }
+ LOG_INFO(netconf_logger, NETCONF_CONFIG_CHANGE_EVENT)
+ .arg(event_type.str());
+ NetconfAgent::logChanges(sess, module_name);
+ switch (event) {
+ case SR_EV_CHANGE:
+ return (NetconfAgent::change(sess, service_pair_));
+ case SR_EV_DONE:
+ return (NetconfAgent::done(sess, service_pair_));
+ default:
+ return (SR_ERR_OK);
+ }
+ }
+
+ void event_notif(sysrepo::S_Session /* session */,
+ sr_ev_notif_type_t const notification_type,
+ char const* /* path */,
+ sysrepo::S_Vals const vals,
+ time_t /* timestamp */,
+ void* /* private_data */) {
+ string n;
+ switch (notification_type) {
+ case SR_EV_NOTIF_REALTIME:
+ n = "SR_EV_NOTIF_REALTIME";
+ break;
+ case SR_EV_NOTIF_REPLAY:
+ n = "SR_EV_NOTIF_REPLAY";
+ break;
+ case SR_EV_NOTIF_REPLAY_COMPLETE:
+ n = "SR_EV_NOTIF_REPLAY_COMPLETE";
+ break;
+ case SR_EV_NOTIF_STOP:
+ n = "SR_EV_NOTIF_STOP";
+ break;
+ case SR_EV_NOTIF_SUSPENDED:
+ n = "SR_EV_NOTIF_SUSPENDED";
+ break;
+ case SR_EV_NOTIF_RESUMED:
+ n = "SR_EV_NOTIF_RESUMED";
+ break;
+ }
+
+ stringstream s;
+ for (size_t i(0); i < vals->val_cnt(); ++i) {
+ if (i != 0) {
+ s << ", ";
+ }
+ s << vals->val(i)->to_string();
+ }
+
+ LOG_INFO(netconf_logger, NETCONF_NOTIFICATION_RECEIVED)
+ .arg(n)
+ .arg(service_pair_.first)
+ .arg(s.str());
+ }
+};
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace netconf {
+
+NetconfAgent::~NetconfAgent() {
+ clear();
+}
+
+void
+NetconfAgent::init(NetconfCfgMgrPtr cfg_mgr) {
+ // Check for a configuration manager.
+ if (!cfg_mgr) {
+ isc_throw(Unexpected, "missing configuration manager");
+ return;
+ }
+
+ // Retrieve configuration from existing running DHCP daemons.
+ const CfgServersMapPtr& servers =
+ cfg_mgr->getNetconfConfig()->getCfgServersMap();
+ for (auto const& pair : *servers) {
+ keaConfig(pair);
+ }
+
+ // Initialize sysrepo.
+ initSysrepo();
+
+ // Check modules / revisions.
+ checkModules(servers);
+
+ for (auto const& pair : *servers) {
+ yangConfig(pair);
+ subscribeConfig(pair);
+ subscribeToNotifications(pair);
+ }
+}
+
+void
+NetconfAgent::clear() {
+ for (auto subs : subscriptions_) {
+ subs.second.reset();
+ }
+ subscriptions_.clear();
+ running_sess_.reset();
+ startup_sess_.reset();
+ conn_.reset();
+}
+
+void
+NetconfAgent::keaConfig(const CfgServersMapPair& service_pair) {
+ // If the boot-update flag is not set.
+ if (!service_pair.second->getBootUpdate()) {
+ return;
+ }
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return;
+ }
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "createControlSocket failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ ConstElementPtr answer;
+ int rcode;
+ ConstElementPtr config;
+ LOG_INFO(netconf_logger, NETCONF_GET_CONFIG_STARTED)
+ .arg(service_pair.first);
+ try {
+ answer = comm->configGet(service_pair.first);
+ config = parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "config-get command failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ ostringstream msg;
+ msg << "config-get command returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ if (!config) {
+ LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg("config-get command returned an empty configuration");
+ return;
+ }
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_GET_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+}
+
+void
+NetconfAgent::initSysrepo() {
+ try {
+ conn_ = make_shared<Connection>();
+ } catch (const std::exception& ex) {
+ isc_throw(Unexpected, "Can't connect to sysrepo: " << ex.what());
+ }
+
+ try {
+ startup_sess_.reset(new Session(conn_, SR_DS_STARTUP));
+ running_sess_.reset(new Session(conn_, SR_DS_RUNNING));
+ } catch (const std::exception& ex) {
+ isc_throw(Unexpected, "Can't establish a sysrepo session: "
+ << ex.what());
+ }
+
+ // Retrieve names and revisions of installed modules from sysrepo.
+ getModules();
+}
+
+void NetconfAgent::getModules() {
+ vector<S_Module> modules;
+ try {
+ S_Context context(running_sess_->get_context());
+ modules = context->get_module_iter();
+ } catch (const sysrepo_exception& ex) {
+ isc_throw(Unexpected, "can't retrieve available modules: " << ex.what());
+ }
+
+ for (S_Module const& module : modules) {
+ if (!module->name()) {
+ isc_throw(Unexpected, "could not retrieve module name");
+ }
+ string const name(module->name());
+ if (!module->rev() || !module->rev()->date()) {
+ isc_throw(Unexpected,
+ "could not retrieve module revision for module " << name);
+ }
+ string const revision(module->rev()->date());
+ modules_.emplace(name, revision);
+ }
+}
+
+bool
+NetconfAgent::checkModule(const string& module_name) const {
+ auto module = modules_.find(module_name);
+ if (module == modules_.end()) {
+ LOG_ERROR(netconf_logger, NETCONF_MODULE_MISSING_ERR)
+ .arg(module_name);
+ return (false);
+ }
+ auto modrev = YANG_REVISIONS.find(module_name);
+ if (modrev == YANG_REVISIONS.end()) {
+ // Can't check revision?!
+ // It can happen only with a module which is not in
+ // YANG_REVISIONS but installed so likely on purpose.
+ return (true);
+ }
+ if (modrev->second != module->second) {
+ LOG_ERROR(netconf_logger, NETCONF_MODULE_REVISION_ERR)
+ .arg(module_name)
+ .arg(modrev->second)
+ .arg(module->second);
+ return (false);
+ }
+ return (true);
+}
+
+void
+NetconfAgent::checkModules(CfgServersMapPtr const& servers /* = {} */) const {
+ bool faulty_model(false);
+ if (servers) {
+ for (auto pair : *servers) {
+ if (!checkModule(pair.second->getModel())) {
+ faulty_model = true;
+ }
+ }
+ }
+
+ if (faulty_model) {
+ isc_throw(Unexpected, "YANG module is missing or its revision is not "
+ "supported. Check logs for details.");
+ }
+
+ for (auto modrev : YANG_REVISIONS) {
+ auto module = modules_.find(modrev.first);
+ if (module == modules_.end()) {
+ LOG_WARN(netconf_logger, NETCONF_MODULE_MISSING_WARN)
+ .arg(modrev.first);
+ continue;
+ }
+ if (modrev.second != module->second) {
+ LOG_WARN(netconf_logger, NETCONF_MODULE_REVISION_WARN)
+ .arg(modrev.first)
+ .arg(modrev.second)
+ .arg(module->second);
+ }
+ }
+}
+
+void
+NetconfAgent::yangConfig(const CfgServersMapPair& service_pair) {
+ // If we're shutting down, or the boot-update flag is not set or the model
+ // associated with it is not specified.
+ if (!service_pair.second->getBootUpdate() ||
+ service_pair.second->getModel().empty()) {
+ return;
+ }
+
+ // First we need a way to reach the actual servers.
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return;
+ }
+
+ LOG_INFO(netconf_logger, NETCONF_SET_CONFIG_STARTED)
+ .arg(service_pair.first);
+ ConstElementPtr config;
+ try {
+ // Retrieve configuration from Sysrepo.
+ TranslatorConfig tc(startup_sess_, service_pair.second->getModel());
+ config = tc.getConfig();
+ if (!config) {
+ ostringstream msg;
+ msg << "YANG configuration for "
+ << service_pair.second->getModel()
+ << " is empty";
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ } else {
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_SET_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+ }
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "get YANG configuration for " << service_pair.first
+ << " failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "control socket creation failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ ConstElementPtr answer;
+ int rcode;
+ try {
+ answer = comm->configSet(config, service_pair.first);
+ parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "config-set command failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ ostringstream msg;
+ msg << "config-set command returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return;
+ }
+ LOG_INFO(netconf_logger, NETCONF_BOOT_UPDATE_COMPLETED)
+ .arg(service_pair.first);
+}
+
+void
+NetconfAgent::subscribeConfig(const CfgServersMapPair& service_pair) {
+ std::string const& model(service_pair.second->getModel());
+
+ // If we're shutting down, or the subscribe-changes flag is not set or
+ // the model associated with it is not specified.
+ if (!service_pair.second->getSubscribeChanges() ||
+ model.empty()) {
+ return;
+ }
+ LOG_INFO(netconf_logger, NETCONF_SUBSCRIBE_CONFIG)
+ .arg(service_pair.first)
+ .arg(model);
+ S_Subscribe subs(new Subscribe(running_sess_));
+ auto callback = [=](sysrepo::S_Session sess, const char* module_name,
+ const char* xpath, sr_event_t event,
+ uint32_t /* request_id */) {
+ NetconfAgentCallback agent(service_pair);
+ return agent.module_change(sess, module_name, xpath, event, nullptr);
+ };
+ try {
+ sr_subscr_options_t options = SR_SUBSCR_DEFAULT;
+ if (!service_pair.second->getValidateChanges()) {
+ options |= SR_SUBSCR_DONE_ONLY;
+ }
+ // Note the API stores the module name so do not put it
+ // in a short lifetime variable!
+ subs->module_change_subscribe(model.c_str(), callback, nullptr, 0,
+ options);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "module change subscribe failed with " << ex.what();
+ msg << "change subscription for model " << model <<
+ " failed with: " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SUBSCRIBE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(service_pair.second->getModel())
+ .arg(msg.str());
+ return;
+ }
+ subscriptions_.insert(make_pair(service_pair.first, subs));
+}
+
+
+void
+NetconfAgent::subscribeToNotifications(const CfgServersMapPair& service_pair) {
+ std::string const& model(service_pair.second->getModel());
+ // If we're shutting down, or the subscribe-changes flag is not set or
+ // the model associated with it is not specified.
+ if (!service_pair.second->getSubscribeNotifications() ||
+ model.empty()) {
+ return;
+ }
+ LOG_INFO(netconf_logger, NETCONF_SUBSCRIBE_NOTIFICATIONS)
+ .arg(service_pair.first)
+ .arg(model);
+
+ S_Subscribe subscription(std::make_shared<Subscribe>(running_sess_));
+ auto callback = [=](sysrepo::S_Session session,
+ sr_ev_notif_type_t const notification_type,
+ char const* path,
+ sysrepo::S_Vals const vals,
+ time_t timestamp) {
+ NetconfAgentCallback agent(service_pair);
+ return agent.event_notif(session, notification_type, path, vals, timestamp, nullptr);
+ };
+ try {
+ subscription->event_notif_subscribe(model.c_str(), callback);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "event notification subscription for model " << model <<
+ " failed with: " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED)
+ .arg(service_pair.first)
+ .arg(service_pair.second->getModel())
+ .arg(msg.str());
+ return;
+ }
+ subscriptions_.emplace(service_pair.first, subscription);
+}
+
+sr_error_t
+NetconfAgent::change(S_Session sess, const CfgServersMapPair& service_pair) {
+ // If we're shutting down, or the subscribe-changes or the
+ // validate-changes flag is not set or the model associated with
+ // it is not specified.
+ if (!service_pair.second->getSubscribeChanges() ||
+ !service_pair.second->getValidateChanges() ||
+ service_pair.second->getModel().empty()) {
+ return (SR_ERR_OK);
+ }
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return (SR_ERR_OK);
+ }
+ LOG_INFO(netconf_logger, NETCONF_VALIDATE_CONFIG_STARTED)
+ .arg(service_pair.first);
+ ConstElementPtr config;
+ try {
+ TranslatorConfig tc(sess, service_pair.second->getModel());
+ config = tc.getConfig();
+ if (!config) {
+ ostringstream msg;
+ msg << "YANG configuration for "
+ << service_pair.second->getModel()
+ << " is empty";
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_OPERATION_FAILED);
+ } else {
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_VALIDATE_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+ }
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "get YANG configuration for " << service_pair.first
+ << " failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);;
+ }
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "createControlSocket failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_OK);
+ }
+ ConstElementPtr answer;
+ int rcode;
+ try {
+ answer = comm->configTest(config, service_pair.first);
+ parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ stringstream msg;
+ msg << "configTest failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ stringstream msg;
+ msg << "configTest returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_REJECTED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+ LOG_INFO(netconf_logger, NETCONF_VALIDATE_CONFIG_COMPLETED)
+ .arg(service_pair.first);
+ return (SR_ERR_OK);
+}
+
+sr_error_t
+NetconfAgent::done(S_Session sess, const CfgServersMapPair& service_pair) {
+ // Check if we should and can process this update.
+ if (!service_pair.second->getSubscribeChanges() ||
+ service_pair.second->getModel().empty()) {
+ return (SR_ERR_OK);
+ }
+ CfgControlSocketPtr ctrl_sock = service_pair.second->getCfgControlSocket();
+ if (!ctrl_sock) {
+ return (SR_ERR_OK);
+ }
+
+ // All looks good, let's get started. Print an info that we're about
+ // to update the configuration.
+ LOG_INFO(netconf_logger, NETCONF_UPDATE_CONFIG_STARTED)
+ .arg(service_pair.first);
+
+ // Retrieve the configuration from SYSREPO first.
+ ConstElementPtr config;
+ try {
+ TranslatorConfig tc(sess, service_pair.second->getModel());
+ config = tc.getConfig();
+ if (!config) {
+ ostringstream msg;
+ msg << "YANG configuration for "
+ << service_pair.second->getModel()
+ << " is empty";
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ } else {
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_UPDATE_CONFIG)
+ .arg(service_pair.first)
+ .arg(prettyPrint(config));
+ }
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "get YANG configuration for " << service_pair.first
+ << " failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+
+ // Ok, now open the control socket. We need this to send the config to
+ // the server.
+ ControlSocketBasePtr comm;
+ try {
+ comm = controlSocketFactory(ctrl_sock);
+ } catch (const std::exception& ex) {
+ ostringstream msg;
+ msg << "createControlSocket failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_OK);
+ }
+
+ // Now apply the config using config-set command.
+ ConstElementPtr answer;
+ int rcode;
+ try {
+ answer = comm->configSet(config, service_pair.first);
+ parseAnswer(rcode, answer);
+ } catch (const std::exception& ex) {
+ stringstream msg;
+ msg << "configSet failed with " << ex.what();
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+
+ // rcode == CONTROL_RESULT_SUCCESS, unless the docs say otherwise :).
+ if (rcode != CONTROL_RESULT_SUCCESS) {
+ stringstream msg;
+ msg << "configSet returned " << answerToText(answer);
+ LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED)
+ .arg(service_pair.first)
+ .arg(msg.str());
+ return (SR_ERR_VALIDATION_FAILED);
+ }
+ LOG_INFO(netconf_logger, NETCONF_UPDATE_CONFIG_COMPLETED)
+ .arg(service_pair.first);
+ return (SR_ERR_OK);
+}
+
+void
+NetconfAgent::logChanges(S_Session sess, const string& model) {
+ ostringstream stream;
+ stream << "/" << model << ":*//.";
+ std::string const xpath(stream.str());
+ S_Iter_Change iter = sess->get_changes_iter(xpath.c_str());
+ if (!iter) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("no iterator");
+ return;
+ }
+ for (;;) {
+ S_Change change;
+ ostringstream msg;
+ try {
+ change = sess->get_change_next(iter);
+ } catch (const sysrepo_exception& ex) {
+ msg << "get change iterator next failed: " << ex.what();
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg(msg.str());
+ return;
+ }
+ if (!change) {
+ // End of changes, not an error.
+ return;
+ }
+ S_Val new_val = change->new_val();
+ S_Val old_val = change->old_val();
+ string report;
+ switch (change->oper()) {
+ case SR_OP_CREATED:
+ if (!new_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("created but without a new value");
+ break;
+ }
+ msg << "created: " << new_val->to_string();
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ case SR_OP_MODIFIED:
+ if (!old_val || !new_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("modified but without an old or new value");
+ break;
+ }
+ msg << "modified: " << old_val->to_string()
+ << " => " << new_val->to_string();
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ case SR_OP_DELETED:
+ if (!old_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("deleted but without an old value");
+ break;
+ }
+ msg << "deleted: " << old_val->to_string();
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ case SR_OP_MOVED:
+ if (!new_val) {
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg("moved but without a new value");
+ break;
+ }
+ msg << "moved: " << new_val->xpath();
+ if (!old_val) {
+ msg << " first";
+ } else {
+ msg << " after " << old_val->xpath();
+ }
+ report = msg.str();
+ boost::erase_all(report, "\n");
+ LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA,
+ NETCONF_CONFIG_CHANGED_DETAIL)
+ .arg(report);
+ break;
+ default:
+ msg << "unknown operation (" << change->oper() << ")";
+ LOG_WARN(netconf_logger, NETCONF_LOG_CHANGE_FAIL)
+ .arg(msg.str());
+ }
+ }
+}
+
+void
+NetconfAgent::announceShutdown() const {
+ isc::process::DControllerBasePtr& controller(NetconfController::instance());
+ if (controller) {
+ boost::dynamic_pointer_cast<NetconfController>(controller)
+ ->getNetconfProcess()
+ ->setShutdownFlag(true);
+ }
+}
+
+bool NetconfAgent::shouldShutdown() const {
+ return boost::dynamic_pointer_cast<NetconfController>(NetconfController::instance())
+ ->getNetconfProcess()
+ ->shouldShutdown();
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/netconf.h b/src/bin/netconf/netconf.h
new file mode 100644
index 0000000..8162116
--- /dev/null
+++ b/src/bin/netconf/netconf.h
@@ -0,0 +1,183 @@
+// 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/.
+
+/// @file netconf.h
+
+#ifndef NETCONF_H
+#define NETCONF_H
+
+#include <netconf/netconf_cfg_mgr.h>
+#include <netconf/control_socket.h>
+#include <netconf/http_control_socket.h>
+#include <netconf/stdout_control_socket.h>
+#include <netconf/unix_control_socket.h>
+
+#include <sysrepo-cpp/Session.hpp>
+
+#include <map>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Forward declaration to the @c NetconfAgent.
+class NetconfAgent;
+
+/// @brief Type definition for the pointer to the @c NetconfAgent.
+typedef boost::shared_ptr<NetconfAgent> NetconfAgentPtr;
+
+/// @brief Netconf agent.
+///
+/// Service performed by the Netconf agent:
+/// - at boot get and display Kea server configurations.
+/// - load Kea server configurations from YANG datastore.
+/// - validate YANG datastore changes using Kea configuration test.
+/// - load updated Kea server configurations from YANG datastore.
+/// - on shutdown close subscriptions.
+class NetconfAgent {
+public:
+ /// @brief Destructor (call clear).
+ virtual ~NetconfAgent();
+
+ /// @brief Initialize sysrepo sessions.
+ ///
+ /// Must be called before init. Collect the list of available
+ /// modules with their revisions.
+ void initSysrepo();
+
+ /// @brief Initialization.
+ ///
+ /// Check available modules / revisions.
+ /// Get and display Kea server configurations.
+ /// Load Kea server configurations from YANG datastore.
+ /// Subscribe configuration changes in YANG datastore.
+ ///
+ /// @param cfg_mgr The configuration manager (can be null).
+ void init(NetconfCfgMgrPtr cfg_mgr);
+
+ /// @brief Clear.
+ ///
+ /// Close subscriptions and sysrepo.
+ void clear();
+
+ /// @brief SR_EV_CHANGE callback.
+ ///
+ /// Validate YANG datastore changes using Kea configuration test.
+ ///
+ /// @param sess The sysrepo running datastore session.
+ /// @param service_pair The service name and configuration pair.
+ /// @return return code for sysrepo.
+ static sr_error_t
+ change(sysrepo::S_Session sess, const CfgServersMapPair& service_pair);
+
+ /// @brief SR_EV_DONE callback.
+ ///
+ /// Get notified that a Kea configuration has been written to the YANG
+ /// datastore.
+ ///
+ /// @param sess The sysrepo running datastore session.
+ /// @param service_pair The service name and configuration pair.
+ /// @return return code for sysrepo.
+ static sr_error_t
+ done(sysrepo::S_Session sess, const CfgServersMapPair& service_pair);
+
+ /// @brief Log changes.
+ ///
+ /// Iterate on changes logging them. Sysrepo changes are an operation
+ /// (created, modified, deleted or moved) with old and new values
+ /// (cf sr_change_oper_e sysrepo documentation).
+ ///
+ /// @param sess The sysrepo running datastore session.
+ /// @param model The model name.
+ static void logChanges(sysrepo::S_Session sess, const std::string& model);
+
+protected:
+ /// @brief Get and display Kea server configuration.
+ ///
+ /// Retrieves current configuration via control socket (unix or http)
+ /// from a running Kea server. If boot-update is set to false, this
+ /// operation is a no-op.
+ ///
+ /// @param service_pair The service name and configuration pair.
+ void keaConfig(const CfgServersMapPair& service_pair);
+
+ /// @brief Check essential module availability.
+ ///
+ /// Emit a fatal error if an essential one (i.e. required in
+ /// a further phase) is missing or does not have the expected revision.
+ ///
+ /// @param module_name The module name.
+ /// @return true if available, false if not.
+ bool checkModule(const std::string& module_name) const;
+
+ /// @brief Retrieve names and revisions of installed modules through the
+ /// sysrepo API.
+ ///
+ /// @throw Unexpected if module information cannot be retrieved from sysrepo
+ void getModules();
+
+ /// @brief Check module availability.
+ ///
+ /// Emit a warning if a module is missing or does not have
+ /// the expected revision.
+ ///
+ /// @param servers the configured servers to check against YANG_REVISIONS.
+ /// Is empty by default for when the caller only wants to check
+ /// installed modules.
+ ///
+ /// @throw Unexpected if a module from YANG_REVISIONS is not installed or
+ /// has the wrong revision.
+ void checkModules(CfgServersMapPtr const& servers = {}) const;
+
+ /// @brief Retrieve Kea server configuration from the YANG startup
+ /// datastore and applies it to servers.
+ ///
+ /// This method retrieves the configuation from sysrepo first, then
+ /// established control socket connection to Kea servers (currently
+ /// dhcp4 and/or dhcp6) and then attempts to send configuration
+ /// using config-set.
+ ///
+ /// If boot-update is set to false, this operation is a no-op.
+ ///
+ /// @param service_pair The service name and configuration pair.
+ void yangConfig(const CfgServersMapPair& service_pair);
+
+ /// @brief Subscribe changes for a module in YANG datastore.
+ ///
+ /// @param service_pair The service name and configuration pair.
+ void subscribeConfig(const CfgServersMapPair& service_pair);
+
+ /// @brief Subscribe to notifications for a given YANG module.
+ ///
+ /// @param service_pair the service name and configuration pair
+ void subscribeToNotifications(const CfgServersMapPair& service_pair);
+
+ /// @brief Set the shutdown flag of the process to true so that it can exit
+ /// at the earliest convenient time.
+ void announceShutdown() const;
+
+ /// @brief Check the shutdown flag of the process.
+ bool shouldShutdown() const;
+
+ /// @brief Sysrepo connection.
+ sysrepo::S_Connection conn_;
+
+ /// @brief Sysrepo startup datastore session.
+ sysrepo::S_Session startup_sess_;
+
+ /// @brief Sysrepo running datastore session.
+ sysrepo::S_Session running_sess_;
+
+ /// @brief Available modules and revisions in Sysrepo.
+ std::map<const std::string, const std::string> modules_;
+
+ /// @brief Subscription map.
+ std::map<const std::string, sysrepo::S_Subscribe> subscriptions_;
+};
+
+} // namespace netconf
+} // namespace isc
+
+#endif // NETCONF_H
diff --git a/src/bin/netconf/netconf_cfg_mgr.cc b/src/bin/netconf/netconf_cfg_mgr.cc
new file mode 100644
index 0000000..82a19f2
--- /dev/null
+++ b/src/bin/netconf/netconf_cfg_mgr.cc
@@ -0,0 +1,173 @@
+// 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_log.h>
+#include <netconf/simple_parser.h>
+#include <cc/simple_parser.h>
+#include <cc/command_interpreter.h>
+#include <exceptions/exceptions.h>
+
+using namespace isc::config;
+using namespace isc::dhcp;
+using namespace isc::process;
+using namespace isc::data;
+
+namespace isc {
+namespace netconf {
+
+NetconfConfig::NetconfConfig()
+ : configured_globals_(Element::createMap()),
+ servers_map_(new CfgServersMap()) {
+}
+
+NetconfConfig::NetconfConfig(const NetconfConfig& orig)
+ : ConfigBase(), configured_globals_(orig.configured_globals_),
+ servers_map_(orig.servers_map_), hooks_config_(orig.hooks_config_) {
+}
+
+void
+NetconfConfig::extractConfiguredGlobals(ConstElementPtr config) {
+ if (config->getType() != Element::map) {
+ isc_throw(BadValue,
+ "extractConfiguredGlobals must be given a map element");
+ }
+
+ const std::map<std::string, ConstElementPtr>& values = config->mapValue();
+ for (auto value = values.begin(); value != values.end(); ++value) {
+ if (value->second->getType() != Element::list &&
+ value->second->getType() != Element::map) {
+ addConfiguredGlobal(value->first, value->second);
+ }
+ }
+}
+
+NetconfCfgMgr::NetconfCfgMgr()
+ : DCfgMgrBase(ConfigPtr(new NetconfConfig())) {
+}
+
+std::string
+NetconfCfgMgr::getConfigSummary(const uint32_t /*selection*/) {
+
+ NetconfConfigPtr ctx = getNetconfConfig();
+
+ // No globals to print.
+ std::ostringstream s;
+
+ // Then print managed servers.
+ for (auto serv : *ctx->getCfgServersMap()) {
+ if (s.tellp() != 0) {
+ s << " ";
+ }
+ s << serv.first;
+ }
+
+ if (s.tellp() == 0) {
+ s << "none";
+ }
+
+ // Finally, print the hook libraries names
+ const isc::hooks::HookLibsCollection libs = ctx->getHooksConfig().get();
+ s << ", " << libs.size() << " lib(s):";
+ for (auto lib = libs.begin(); lib != libs.end(); ++lib) {
+ s << lib->first << " ";
+ }
+
+ return (s.str());
+}
+
+ConfigPtr
+NetconfCfgMgr::createNewContext() {
+ return (ConfigPtr(new NetconfConfig()));
+}
+
+isc::data::ConstElementPtr
+NetconfCfgMgr::parse(isc::data::ConstElementPtr config_set,
+ bool check_only) {
+ // Do a sanity check first.
+ if (!config_set) {
+ isc_throw(ConfigError, "Mandatory config parameter not provided");
+ }
+
+ NetconfConfigPtr ctx = getNetconfConfig();
+
+ // Preserve all scalar global parameters.
+ ctx->extractConfiguredGlobals(config_set);
+
+ // Set the defaults and derive parameters.
+ ElementPtr cfg = boost::const_pointer_cast<Element>(config_set);
+ NetconfSimpleParser::setAllDefaults(cfg);
+ NetconfSimpleParser::deriveParameters(cfg);
+
+ // And parse the configuration.
+ ConstElementPtr answer;
+ std::string excuse;
+ try {
+ // Do the actual parsing
+ NetconfSimpleParser parser;
+ parser.parse(ctx, cfg, check_only);
+ } catch (const isc::Exception& ex) {
+ excuse = ex.what();
+ answer = createAnswer(CONTROL_RESULT_ERROR, excuse);
+ } catch (...) {
+ excuse = "undefined configuration parsing error";
+ answer = createAnswer(CONTROL_RESULT_ERROR, excuse);
+ }
+
+ // At this stage the answer was created only in case of exception.
+ if (answer) {
+ if (check_only) {
+ LOG_ERROR(netconf_logger, NETCONF_CONFIG_CHECK_FAIL).arg(excuse);
+ } else {
+ LOG_ERROR(netconf_logger, NETCONF_CONFIG_FAIL).arg(excuse);
+ }
+ return (answer);
+ }
+
+ if (check_only) {
+ answer = createAnswer(CONTROL_RESULT_SUCCESS,
+ "Configuration check successful");
+ } else {
+ answer = createAnswer(CONTROL_RESULT_SUCCESS,
+ "Configuration applied successfully.");
+ }
+
+ return (answer);
+}
+
+ElementPtr
+NetconfConfig::toElement() const {
+ ElementPtr netconf = ConfigBase::toElement();
+ // Set user-context
+ contextToElement(netconf);
+ // Add in explicitly configured globals.
+ netconf->setValue(configured_globals_->mapValue());
+ // Set hooks-libraries
+ netconf->set("hooks-libraries", hooks_config_.toElement());
+ // Set managed-servers
+ ElementPtr servers = Element::createMap();
+ for (auto serv : *servers_map_) {
+ ConstElementPtr server = serv.second->toElement();
+ servers->set(serv.first, server);
+ }
+ netconf->set("managed-servers", servers);
+ // Set Netconf
+ ElementPtr result = Element::createMap();
+ result->set("Netconf", netconf);
+ return (result);
+}
+
+std::list<std::list<std::string>>
+NetconfCfgMgr::jsonPathsToRedact() const {
+ static std::list<std::list<std::string>> const list({
+ {"hooks-libraries", "[]", "parameters", "*"},
+ });
+ return list;
+}
+
+} // namespace isc::netconf
+} // namespace isc
diff --git a/src/bin/netconf/netconf_cfg_mgr.h b/src/bin/netconf/netconf_cfg_mgr.h
new file mode 100644
index 0000000..ca9e80c
--- /dev/null
+++ b/src/bin/netconf/netconf_cfg_mgr.h
@@ -0,0 +1,186 @@
+// 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/.
+
+#ifndef NETCONF_CFG_MGR_H
+#define NETCONF_CFG_MGR_H
+
+#include <cc/data.h>
+#include <hooks/hooks_config.h>
+#include <process/d_cfg_mgr.h>
+#include <netconf/netconf_config.h>
+#include <boost/pointer_cast.hpp>
+#include <map>
+#include <string>
+
+namespace isc {
+namespace netconf {
+
+class NetconfConfig;
+/// @brief Pointer to a configuration context.
+typedef boost::shared_ptr<NetconfConfig> NetconfConfigPtr;
+
+/// @brief Netconf Configuration Context.
+///
+/// Implement the storage container for configuration context.
+/// It provides a single enclosure for the storage of configuration parameters
+/// and any other Netconf specific information that needs to be accessible
+/// during configuration parsing as well as to the application as a whole.
+/// It is derived from the context base class, ConfigBase.
+class NetconfConfig : public process::ConfigBase {
+public:
+
+ /// @brief Default constructor
+ NetconfConfig();
+
+ /// @brief Returns pointer to configured global parameters.
+ ///
+ /// @todo revisit this at the toElement first use.
+ isc::data::ConstElementPtr getConfiguredGlobals() const {
+ return (isc::data::ConstElementPtr(configured_globals_));
+ }
+
+ /// @brief Saves scalar elements from the global scope of a configuration.
+ void extractConfiguredGlobals(isc::data::ConstElementPtr config);
+
+ /// @brief Adds a parameter to the collection configured globals.
+ ///
+ /// @param name std::string name of the global to add.
+ /// @param value ElementPtr containing the value of the global.
+ void addConfiguredGlobal(const std::string& name,
+ isc::data::ConstElementPtr value) {
+ configured_globals_->set(name, value);
+ }
+
+ /// @brief Returns non-const reference to the managed servers map.
+ ///
+ /// @return non-const reference to the managed servers map.
+ CfgServersMapPtr& getCfgServersMap() {
+ return (servers_map_);
+ }
+
+ /// @brief Returns const reference to the managed servers map.
+ ///
+ /// @return const reference to the managed servers map.
+ const CfgServersMapPtr& getCfgServersMap() const {
+ return (servers_map_);
+ }
+
+ /// @brief Returns non-const reference to configured hooks libraries.
+ ///
+ /// @return non-const reference to configured hooks libraries.
+ isc::hooks::HooksConfig& getHooksConfig() {
+ return (hooks_config_);
+ }
+
+ /// @brief Returns const reference to configured hooks libraries.
+ ///
+ /// @return const reference to configured hooks libraries.
+ const isc::hooks::HooksConfig& getHooksConfig() const {
+ return (hooks_config_);
+ }
+
+ /// @brief Unparse a configuration object
+ ///
+ /// Returns an element which must parse into the same object, i.e.
+ /// @code
+ /// for all valid config C parse(parse(C)->toElement()) == parse(C)
+ /// @endcode
+ ///
+ /// @return a pointer to a configuration which can be parsed into
+ /// the initial configuration object
+ virtual isc::data::ElementPtr toElement() const;
+
+private:
+
+ /// @brief Private copy constructor
+ ///
+ /// It is private to forbid anyone outside of this class to make copies.
+ ///
+ /// @param orig the original context to copy from
+ NetconfConfig(const NetconfConfig& orig);
+
+ /// @brief Private assignment operator to avoid potential for slicing.
+ ///
+ /// @param rhs Context to be assigned.
+ NetconfConfig& operator=(const NetconfConfig& rhs);
+
+ /// @brief Stores the global parameters specified via configuration.
+ isc::data::ElementPtr configured_globals_;
+
+ /// @brief CfgServers map.
+ CfgServersMapPtr servers_map_;
+
+ /// @brief Configured hooks libraries.
+ isc::hooks::HooksConfig hooks_config_;
+};
+
+/// @brief Ctrl Netconf Configuration Manager.
+///
+/// Provides the mechanisms for managing the Netconf application's
+/// configuration.
+class NetconfCfgMgr : public process::DCfgMgrBase {
+public:
+
+ /// @brief Constructor.
+ NetconfCfgMgr();
+
+ /// @brief Destructor
+ virtual ~NetconfCfgMgr() = default;
+
+ /// @brief Convenience method that returns the Netconf configuration
+ /// context.
+ ///
+ /// @return returns a pointer to the configuration context.
+ NetconfConfigPtr getNetconfConfig() {
+ return (boost::dynamic_pointer_cast<NetconfConfig>(getContext()));
+ }
+
+ /// @brief Returns configuration summary in the textual format.
+ ///
+ /// @param selection Bitfield which describes the parts of the configuration
+ /// to be returned. This parameter is ignored for Netconf.
+ ///
+ /// @return Summary of the configuration in the textual format.
+ virtual std::string getConfigSummary(const uint32_t selection);
+
+ /// @brief Return a list of all paths that contain passwords or secrets for
+ /// kea-netconf.
+ ///
+ /// @return the list of lists of sequential JSON map keys needed to reach
+ /// the passwords and secrets.
+ std::list<std::list<std::string>> jsonPathsToRedact() const;
+
+protected:
+
+ /// @brief Parses configuration of Netconf.
+ ///
+ /// @param config Pointer to a configuration specified for netconf.
+ /// @param check_only Boolean flag indicating if this method should
+ /// only verify correctness of the provided configuration.
+ /// @return Pointer to a result of configuration parsing.
+ virtual isc::data::ConstElementPtr
+ parse(isc::data::ConstElementPtr config, bool check_only);
+
+ /// @brief Creates a new, blank NetconfConfig context.
+ ///
+ ///
+ /// This method is used at the beginning of configuration process to
+ /// create a fresh, empty copy of a NetconfConfig. This new context
+ /// will be populated during the configuration process and will replace the
+ /// existing context provided the configuration process completes without
+ /// error.
+ ///
+ /// @return Returns a ConfigPtr to the new context instance.
+ virtual process::ConfigPtr createNewContext();
+};
+
+/// @brief Defines a shared pointer to NetconfCfgMgr.
+typedef boost::shared_ptr<NetconfCfgMgr> NetconfCfgMgrPtr;
+
+} // namespace isc::netconf
+} // namespace isc
+
+#endif // NETCONF_CFG_MGR_H
diff --git a/src/bin/netconf/netconf_config.cc b/src/bin/netconf/netconf_config.cc
new file mode 100644
index 0000000..f5a0531
--- /dev/null
+++ b/src/bin/netconf/netconf_config.cc
@@ -0,0 +1,211 @@
+// 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_log.h>
+#include <netconf/netconf_cfg_mgr.h>
+#include <exceptions/exceptions.h>
+#include <asiolink/io_error.h>
+
+#include <boost/foreach.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
+#include <sstream>
+#include <string>
+
+using namespace std;
+using namespace isc::process;
+using namespace isc::data;
+using namespace isc::http;
+
+namespace isc {
+namespace netconf {
+
+// *********************** CfgControlSocket *************************
+
+CfgControlSocket::CfgControlSocket(Type type, const string& name,
+ const Url& url)
+ : type_(type), name_(name), url_(url) {
+}
+
+CfgControlSocket::Type
+CfgControlSocket::stringToType(const string& type) {
+ if (type == "unix") {
+ return (CfgControlSocket::Type::UNIX);
+ } else if (type == "http") {
+ return (CfgControlSocket::Type::HTTP);
+ } else if (type == "stdout") {
+ return (CfgControlSocket::Type::STDOUT);
+ }
+
+ isc_throw(BadValue, "Unknown control socket type: " << type);
+}
+
+const string
+CfgControlSocket::typeToString(CfgControlSocket::Type type) {
+ switch (type) {
+ case CfgControlSocket::Type::UNIX:
+ return ("unix");
+ case CfgControlSocket::Type::HTTP:
+ return ("http");
+ case CfgControlSocket::Type::STDOUT:
+ return ("stdout");
+ default:
+ isc_throw(BadValue, "Unknown control socket type: " << type);
+ }
+}
+
+ElementPtr
+CfgControlSocket::toElement() const {
+ ElementPtr result = Element::createMap();
+ // Set user-context
+ contextToElement(result);
+ // Set type
+ result->set("socket-type", Element::create(typeToString(type_)));
+ // Set name
+ result->set("socket-name", Element::create(name_));
+ // Set url
+ result->set("socket-url", Element::create(url_.toText()));
+ return (result);
+}
+
+// *********************** CfgServer *************************
+CfgServer::CfgServer(const string& model, CfgControlSocketPtr ctrl_sock)
+ : model_(model), boot_update_(true), subscribe_changes_(true),
+ subscribe_notifications_(true), validate_changes_(true),
+ control_socket_(ctrl_sock) {
+}
+
+string
+CfgServer::toText() const {
+ ostringstream s;
+ s << "model: " << model_ << ", control socker: ";
+ if (!control_socket_) {
+ s << "none";
+ } else {
+ switch (control_socket_->getType()) {
+ case CfgControlSocket::Type::UNIX:
+ s << "UNIX:'" << control_socket_->getName() << "'";
+ break;
+ case CfgControlSocket::Type::HTTP:
+ s << "HTTP:'" << control_socket_->getUrl().toText() << "'";
+ break;
+ case CfgControlSocket::Type::STDOUT:
+ s << "STDOUT";
+ break;
+ }
+ }
+ return (s.str());
+}
+
+ElementPtr
+CfgServer::toElement() const {
+ ElementPtr result = Element::createMap();
+ // Set user-context
+ contextToElement(result);
+ // Set model
+ result->set("model", Element::create(model_));
+ // Set boot-update
+ result->set("boot-update", Element::create(boot_update_));
+ // Set subscribe-changes
+ result->set("subscribe-changes", Element::create(subscribe_changes_));
+ // Set validate-changes
+ result->set("validate-changes", Element::create(validate_changes_));
+ // Set control-socket
+ if (control_socket_) {
+ result->set("control-socket", control_socket_->toElement());
+ }
+ return (result);
+}
+
+ostream&
+operator<<(ostream& os, const CfgServer& server) {
+ os << server.toText();
+ return (os);
+}
+
+// *************************** PARSERS ***********************************
+
+// *********************** ControlSocketConfigParser *************************
+
+CfgControlSocketPtr
+ControlSocketConfigParser::parse(ConstElementPtr ctrl_sock_config) {
+ CfgControlSocketPtr result;
+ string type_str = getString(ctrl_sock_config, "socket-type");
+ string name = getString(ctrl_sock_config, "socket-name");
+ string url_str = getString(ctrl_sock_config, "socket-url");
+ ConstElementPtr user_context = ctrl_sock_config->get("user-context");
+
+ // Type must be valid.
+ CfgControlSocket::Type type;
+ try {
+ type = CfgControlSocket::stringToType(type_str);
+ } catch (const std::exception& ex) {
+ isc_throw(ConfigError, ex.what() << " '" << type_str << "' ("
+ << getPosition("socket-type", ctrl_sock_config) << ")");
+ }
+
+ // Url must be valid.
+ Url url(url_str);
+ if (!url.isValid()) {
+ isc_throw(ConfigError, "invalid control socket url: "
+ << url.getErrorMessage() << " '" << url_str << "' ("
+ << getPosition("socket-url", ctrl_sock_config) << ")");
+ }
+
+ // Create the control socket.
+ try {
+ result.reset(new CfgControlSocket(type, name, url));
+ } catch (const std::exception& ex) {
+ isc_throw(ConfigError, ex.what() << " ("
+ << ctrl_sock_config->getPosition() << ")");
+ }
+
+ // Add user-context.
+ if (user_context) {
+ result->setContext(user_context);
+ }
+
+ return (result);
+}
+
+// *********************** ServerConfigParser *************************
+
+CfgServerPtr
+ServerConfigParser::parse(ConstElementPtr server_config) {
+ CfgServerPtr result;
+ string model = getString(server_config, "model");
+ ConstElementPtr user_context = server_config->get("user-context");
+ ConstElementPtr ctrl_sock_config = server_config->get("control-socket");
+ CfgControlSocketPtr ctrl_sock;
+ if (ctrl_sock_config) {
+ ControlSocketConfigParser parser;
+ ctrl_sock = parser.parse(ctrl_sock_config);
+ }
+ try {
+ result.reset(new CfgServer(model, ctrl_sock));
+ } catch (const std::exception& ex) {
+ isc_throw(ConfigError, ex.what() << " ("
+ << server_config->getPosition() << ")");
+ }
+
+ // Add flags.
+ result->setBootUpdate(getBoolean(server_config, "boot-update"));
+ result->setSubscribeChanges(getBoolean(server_config, "subscribe-changes"));
+ result->setValidateChanges(getBoolean(server_config, "validate-changes"));
+
+ // Add user-context.
+ if (user_context) {
+ result->setContext(user_context);
+ }
+
+ return (result);
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/netconf_config.h b/src/bin/netconf/netconf_config.h
new file mode 100644
index 0000000..18e7a9d
--- /dev/null
+++ b/src/bin/netconf/netconf_config.h
@@ -0,0 +1,328 @@
+// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef NETCONF_CONFIG_H
+#define NETCONF_CONFIG_H
+
+#include <cc/data.h>
+#include <cc/cfg_to_element.h>
+#include <cc/user_context.h>
+#include <cc/simple_parser.h>
+#include <http/url.h>
+#include <exceptions/exceptions.h>
+
+#include <boost/foreach.hpp>
+
+#include <stdint.h>
+#include <string>
+
+namespace isc {
+namespace netconf {
+
+/// @file netconf_config.h
+/// @brief A collection of classes for housing and parsing the application
+/// configuration necessary for the Netconf application.
+///
+/// @note NetconfConfig is not here: this file contains component of
+/// this class but not the class itself.
+///
+/// This file contains the class declarations for the class hierarchy created
+/// from the Netconf configuration and the parser classes used to create it.
+/// The application configuration consists of a list of managed server.
+///
+/// The parsing class hierarchy reflects this same scheme. Working top down:
+///
+/// A ServerMapParser handles the managed servers map invoking a
+/// ServerConfigParser to parse each server.
+///
+/// A ServerConfigParser handles the scalars which belong to the server as well
+/// as creating and invoking a CtrlSocketParser to parse its control socket.
+///
+/// A CtrlSocketParser handles the scalars which belong to the control socket.
+///
+/// The following is sample configuration in JSON form with extra spacing
+/// for clarity:
+///
+/// @code
+/// {
+/// "managed-servers" :
+/// {
+/// "dhcp4":
+/// {
+/// "model": "kea-dhcp4-server",
+/// "control-socket":
+/// {
+/// "socket-type": "unix",
+/// "socket-name": "/tmp/server-v4.sock"
+/// }
+/// }
+/// }
+/// }
+/// @endcode
+
+/// @brief Represents a Control Socket.
+///
+/// Acts as a storage class containing the basic attributes which
+/// describe a Control Socket.
+class CfgControlSocket : public isc::data::UserContext,
+ public isc::data::CfgToElement {
+public:
+ /// @brief Defines the list of possible control socket types.
+ enum Type {
+ UNIX, //< Unix socket.
+ HTTP, //< HTTP socket.
+ STDOUT //< standard output.
+ };
+
+ /// @brief Constructor.
+ ///
+ /// @param type The socket type.
+ /// @param name The Unix socket name.
+ /// @param url The HTTP server URL.
+ CfgControlSocket(Type type, const std::string& name,
+ const isc::http::Url& url);
+
+ /// @brief Destructor (doing nothing).
+ virtual ~CfgControlSocket() = default;
+
+ /// @brief Getter which returns the socket type.
+ ///
+ /// @return returns the socket type as a CfgControlSocket::Type.
+ Type getType() const {
+ return (type_);
+ }
+
+ /// @brief Getter which returns the Unix socket name.
+ ///
+ /// @return returns the Unix socket name as a std::string.
+ const std::string getName() const {
+ return (name_);
+ }
+
+ /// @brief Getter which returns the HTTP server URL.
+ ///
+ /// @return returns the HTTP server URL as an isc::http::Url.
+ const isc::http::Url getUrl() const {
+ return (url_);
+ }
+
+ /// @brief Converts socket type name to CfgControlSocket::Type.
+ ///
+ /// @param type The type name.
+ /// Currently supported values are "unix", "http" and "stdout".
+ ///
+ /// @return The CfgControlSocket::Type corresponding to the type name.
+ /// @throw BadValue if the type name isn't recognized.
+ static Type stringToType(const std::string& type);
+
+ /// @brief Converts CfgControlSocket::Type to string.
+ ///
+ /// @param type The CfgControlSocket::Type type.
+ /// @return The type name corresponding to the enumeration element.
+ static const std::string typeToString(CfgControlSocket::Type type);
+
+ /// @brief Unparse a configuration object
+ ///
+ /// @return a pointer to a configuration
+ virtual isc::data::ElementPtr toElement() const;
+
+private:
+ /// @brief The socket type.
+ Type type_;
+
+ /// @brief The UNIX socket name.
+ const std::string name_;
+
+ /// @brief The HTTP server URL.
+ const isc::http::Url url_;
+};
+
+/// @brief Defines a pointer for CfgControlSocket instances.
+typedef boost::shared_ptr<CfgControlSocket> CfgControlSocketPtr;
+
+/// @brief Represents a Managed CfgServer.
+///
+/// Acts as a storage class containing the basic attributes and
+/// the Control Socket which describe a Managed CfgServer.
+class CfgServer : public isc::data::UserContext, public isc::data::CfgToElement {
+public:
+ /// @brief Constructor.
+ ///
+ /// @param model The model name.
+ /// @param ctrl_sock The control socket.
+ CfgServer(const std::string& model, CfgControlSocketPtr ctrl_sock);
+
+ /// @brief Destructor (doing nothing).
+ virtual ~CfgServer() = default;
+
+ /// @brief Getter which returns the model name.
+ ///
+ /// @return returns the model name as a std::string
+ const std::string getModel() const {
+ return (model_);
+ }
+
+ /// @brief Getter which returns the control socket.
+ ///
+ /// @return returns the control socket as a CfgControlSocketPtr.
+ const CfgControlSocketPtr& getCfgControlSocket() const {
+ return (control_socket_);
+ }
+
+ /// @brief Getter which returns the boot-update flag.
+ ///
+ /// @return returns the boot-update flag as a bool.
+ bool getBootUpdate() const {
+ return (boot_update_);
+ }
+
+ /// @brief Set the boot-update flag.
+ ///
+ /// @param boot_update The boot-update flag.
+ void setBootUpdate(bool boot_update) {
+ boot_update_ = boot_update;
+ }
+
+ /// @brief Getter which returns the subscribe-changes flag.
+ ///
+ /// @return returns the subscribe-changes flag as a bool.
+ bool getSubscribeChanges() const {
+ return (subscribe_changes_);
+ }
+
+ /// @brief Getter which returns the subscribe-changes flag.
+ ///
+ /// @return returns the subscribe-changes flag as a bool.
+ bool getSubscribeNotifications() const {
+ return (subscribe_notifications_);
+ }
+
+ /// @brief Set the subscribe-changes flag.
+ ///
+ /// @param subscribe_changes The subscribe-changes flag.
+ void setSubscribeChanges(bool subscribe_changes) {
+ subscribe_changes_ = subscribe_changes;
+ }
+
+ /// @brief Set the subscribe-notifications flag.
+ ///
+ /// @param subscribe_notifications the subscribe-notifications flag
+ void setSubscribeNotifications(bool subscribe_notifications) {
+ subscribe_notifications_ = subscribe_notifications;
+ }
+
+ /// @brief Getter which returns the validate-changes flag.
+ ///
+ /// @return returns the validate-changes flag as a bool.
+ bool getValidateChanges() const {
+ return (validate_changes_);
+ }
+
+ /// @brief Set the validate-changes flag.
+ ///
+ /// @param validate_changes The validate-changes flag.
+ void setValidateChanges(bool validate_changes) {
+ validate_changes_ = validate_changes;
+ }
+
+ /// @brief Returns a text representation for the server.
+ std::string toText() const;
+
+ /// @brief Unparse a configuration object
+ ///
+ /// @return a pointer to a configuration
+ virtual isc::data::ElementPtr toElement() const;
+
+private:
+ /// @brief The model name.
+ const std::string model_;
+
+ /// @brief The boot-update flag.
+ ///
+ /// If true (the default) Kea server configuration is updated at (netconf
+ /// agent) boot time.
+ bool boot_update_;
+
+ /// @brief The subscribe-changes flag.
+ ///
+ /// If true (the default) the netconf agent subscribes module changes
+ /// so will be notified when the YANG running configuration is changed.
+ bool subscribe_changes_;
+
+ /// @brief The subscribe-notifications flag.
+ ///
+ /// If true (the default) the netconf agent subscribes to the notifications
+ /// API so it will be notified on various events like module installations
+ /// and uninstallations.
+ bool subscribe_notifications_;
+
+ /// @brief The validate-changes flag.
+ ///
+ /// If true (the default) the netconf agent validates module changes
+ /// and can reject bad configurations.
+ bool validate_changes_;
+
+ /// @brief The control socket.
+ CfgControlSocketPtr control_socket_;
+};
+
+/// @brief Defines a pointer for CfgServer instances.
+typedef boost::shared_ptr<CfgServer> CfgServerPtr;
+
+/// @brief Defines a map of CfgServers, keyed by the name.
+typedef std::map<std::string, CfgServerPtr> CfgServersMap;
+
+/// @brief Defines a iterator pairing of name and CfgServer
+typedef std::pair<std::string, CfgServerPtr> CfgServersMapPair;
+
+/// @brief Defines a pointer to map of CfgServers.
+typedef boost::shared_ptr<CfgServersMap> CfgServersMapPtr;
+
+/// @brief Dumps the contents of a CfgServer as text to a output stream.
+///
+/// @param os The output stream to which text should be sent.
+/// @param server The CfgServer instance to dump.
+std::ostream& operator<<(std::ostream& os, const CfgServer& server);
+
+/// @brief Parser for CfgControlSocket.
+///
+/// This class parses the configuration element "control-socket"
+/// and creates an instance of a CfgControlSocket.
+class ControlSocketConfigParser : public data::SimpleParser {
+public:
+ /// @brief Performs the actual parsing of the given "control-socket" element.
+ ///
+ /// Parses a configuration for the elements needed to instantiate a
+ /// CfgControlSocket, validates those entries, creates a CfgControlSocket
+ /// instance.
+ ///
+ /// @param ctrl_sock_config is the "control-socket" configuration to parse.
+ ///
+ /// @return pointer to the new CfgControlSocket instance.
+ CfgControlSocketPtr parse(data::ConstElementPtr ctrl_sock_config);
+};
+
+/// @brief Parser for CfgServer.
+///
+/// This class parses the configuration value from the "managed-servers" map
+/// and creates an instance of a CfgServer.
+class ServerConfigParser : public data::SimpleParser {
+public:
+ /// @brief Performs the actual parsing of the given value from
+ /// the "managed-servers" map.
+ ///
+ /// Parses a configuration for the elements needed to instantiate a
+ /// CfgServer, validates those entries, creates a CfgServer instance.
+ ///
+ /// @param server_config is the value from the "managed-servers" map to parse.
+ /// @return pointer to the new CfgServer instance.
+ CfgServerPtr parse(data::ConstElementPtr server_config);
+};
+
+} // namespace netconf
+} // namespace isc
+
+#endif // NETCONF_CONFIG_H
diff --git a/src/bin/netconf/netconf_controller.cc b/src/bin/netconf/netconf_controller.cc
new file mode 100644
index 0000000..7d85329
--- /dev/null
+++ b/src/bin/netconf/netconf_controller.cc
@@ -0,0 +1,75 @@
+// 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 <cfgrpt/config_report.h>
+#include <netconf/netconf_controller.h>
+#include <netconf/netconf_process.h>
+#include <netconf/parser_context.h>
+
+#include <signal.h>
+
+using namespace isc::process;
+
+namespace isc {
+namespace netconf {
+
+/// @brief Defines the application name, this is passed into base class
+/// it may be used to locate configuration data and appears in log statement.
+const char* NetconfController::netconf_app_name_ = "Netconf";
+
+/// @brief Defines the executable name. This is passed into the base class
+const char* NetconfController::netconf_bin_name_ = "kea-netconf";
+
+DControllerBasePtr&
+NetconfController::instance() {
+ // If the instance hasn't been created yet, create it. Note this method
+ // must use the base class singleton instance methods.
+ if (!getController()) {
+ DControllerBasePtr controller_ptr(new NetconfController());
+ setController(controller_ptr);
+ }
+
+ return (getController());
+}
+
+void
+NetconfController::processSignal(int signum) {
+ if (signum == SIGHUP) {
+ LOG_WARN(dctl_logger, DCTL_UNSUPPORTED_SIGNAL).arg(signum);
+ } else {
+ DControllerBase::processSignal(signum);
+ }
+}
+
+DProcessBase*
+NetconfController::createProcess() {
+ // Instantiate and return an instance of the D2 application process. Note
+ // that the process is passed the controller's io_service.
+ return (new NetconfProcess(getAppName().c_str(), getIOService()));
+}
+
+isc::data::ConstElementPtr
+NetconfController::parseFile(const std::string& name) {
+ ParserContext parser;
+ return (parser.parseFile(name, ParserContext::PARSER_NETCONF));
+}
+
+NetconfController::NetconfController()
+ : DControllerBase(netconf_app_name_, netconf_bin_name_) {
+}
+
+NetconfProcessPtr
+NetconfController::getNetconfProcess() {
+ return (boost::dynamic_pointer_cast<NetconfProcess>(getProcess()));
+}
+
+// Refer to config_report so it will be embedded in the binary.
+const char* const* netconf_config_report = isc::detail::config_report;
+
+} // namespace isc::netconf
+} // namespace isc
diff --git a/src/bin/netconf/netconf_controller.h b/src/bin/netconf/netconf_controller.h
new file mode 100644
index 0000000..b1d8cf6
--- /dev/null
+++ b/src/bin/netconf/netconf_controller.h
@@ -0,0 +1,85 @@
+// 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/.
+
+#ifndef NETCONF_CONTROLLER_H
+#define NETCONF_CONTROLLER_H
+
+#include <netconf/netconf_process.h>
+#include <process/d_controller.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Process Controller for Netconf Process.
+///
+/// This class is the Netconf specific derivation of the DControllerBase.
+/// It creates and manages an instance of the Netconf application process,
+/// NetconfProcess.
+class NetconfController : public process::DControllerBase {
+public:
+
+ /// @brief Static singleton instance method.
+ ///
+ /// This method returns the base class singleton instance member.
+ /// It instantiates the singleton and sets the base class instance
+ /// member upon first invocation.
+ ///
+ /// @return returns the pointer reference to the singleton instance.
+ static process::DControllerBasePtr& instance();
+
+ /// @brief Destructor
+ virtual ~NetconfController() = default;
+
+ /// @brief Returns pointer to an instance of the underlying process object.
+ NetconfProcessPtr getNetconfProcess();
+
+ /// @brief Defines the application name, this is passed into base class
+ /// and appears in log statements.
+ static const char* netconf_app_name_;
+
+ /// @brief Defines the executable name. This is passed into the base class
+ /// by convention this should match the executable name.
+ static const char* netconf_bin_name_;
+
+ /// @brief Parses the configuration file using Netconf::ParserContext (bison)
+ ///
+ /// @param name name of the text file to be parsed
+ /// @return Element tree structure representing parsed configuration
+ isc::data::ConstElementPtr
+ parseFile(const std::string& name);
+
+ /// @brief Redefined application-level signal processing method.
+ ///
+ /// This method ignores SIGHUP as configuration reloading is not yet
+ /// supported.
+ /// @param signum signal number to process.
+ virtual void processSignal(int signum);
+
+private:
+
+ /// @brief Creates an instance of the Netconf application process.
+ ///
+ /// This method is invoked during the process initialization step of
+ /// the controller launch.
+ ///
+ /// @return returns a DProcessBase* to the application process created.
+ /// Note the caller is responsible for destructing the process. This
+ /// is handled by the base class, which wraps this pointer with a smart
+ /// pointer.
+ virtual process::DProcessBase* createProcess();
+
+ /// @brief Constructor is declared private to maintain the integrity of
+ /// the singleton instance.
+ NetconfController();
+};
+
+// @Defines a shared pointer to NetconfController
+typedef boost::shared_ptr<NetconfController> NetconfControllerPtr;
+
+} // namespace isc::netconf
+} // namespace isc
+
+#endif // NETCONF_CONTROLLER_H
diff --git a/src/bin/netconf/netconf_lexer.cc b/src/bin/netconf/netconf_lexer.cc
new file mode 100644
index 0000000..7e21e12
--- /dev/null
+++ b/src/bin/netconf/netconf_lexer.cc
@@ -0,0 +1,3949 @@
+#line 1 "netconf_lexer.cc"
+
+#line 3 "netconf_lexer.cc"
+
+#define YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+/* %not-for-header */
+/* %if-c-only */
+/* %if-not-reentrant */
+#define yy_create_buffer netconf__create_buffer
+#define yy_delete_buffer netconf__delete_buffer
+#define yy_scan_buffer netconf__scan_buffer
+#define yy_scan_string netconf__scan_string
+#define yy_scan_bytes netconf__scan_bytes
+#define yy_init_buffer netconf__init_buffer
+#define yy_flush_buffer netconf__flush_buffer
+#define yy_load_buffer_state netconf__load_buffer_state
+#define yy_switch_to_buffer netconf__switch_to_buffer
+#define yypush_buffer_state netconf_push_buffer_state
+#define yypop_buffer_state netconf_pop_buffer_state
+#define yyensure_buffer_stack netconf_ensure_buffer_stack
+#define yy_flex_debug netconf__flex_debug
+#define yyin netconf_in
+#define yyleng netconf_leng
+#define yylex netconf_lex
+#define yylineno netconf_lineno
+#define yyout netconf_out
+#define yyrestart netconf_restart
+#define yytext netconf_text
+#define yywrap netconf_wrap
+#define yyalloc netconf_alloc
+#define yyrealloc netconf_realloc
+#define yyfree netconf_free
+
+/* %endif */
+/* %endif */
+/* %ok-for-header */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* %if-c++-only */
+/* %endif */
+
+/* %if-c-only */
+#ifdef yy_create_buffer
+#define netconf__create_buffer_ALREADY_DEFINED
+#else
+#define yy_create_buffer netconf__create_buffer
+#endif
+
+#ifdef yy_delete_buffer
+#define netconf__delete_buffer_ALREADY_DEFINED
+#else
+#define yy_delete_buffer netconf__delete_buffer
+#endif
+
+#ifdef yy_scan_buffer
+#define netconf__scan_buffer_ALREADY_DEFINED
+#else
+#define yy_scan_buffer netconf__scan_buffer
+#endif
+
+#ifdef yy_scan_string
+#define netconf__scan_string_ALREADY_DEFINED
+#else
+#define yy_scan_string netconf__scan_string
+#endif
+
+#ifdef yy_scan_bytes
+#define netconf__scan_bytes_ALREADY_DEFINED
+#else
+#define yy_scan_bytes netconf__scan_bytes
+#endif
+
+#ifdef yy_init_buffer
+#define netconf__init_buffer_ALREADY_DEFINED
+#else
+#define yy_init_buffer netconf__init_buffer
+#endif
+
+#ifdef yy_flush_buffer
+#define netconf__flush_buffer_ALREADY_DEFINED
+#else
+#define yy_flush_buffer netconf__flush_buffer
+#endif
+
+#ifdef yy_load_buffer_state
+#define netconf__load_buffer_state_ALREADY_DEFINED
+#else
+#define yy_load_buffer_state netconf__load_buffer_state
+#endif
+
+#ifdef yy_switch_to_buffer
+#define netconf__switch_to_buffer_ALREADY_DEFINED
+#else
+#define yy_switch_to_buffer netconf__switch_to_buffer
+#endif
+
+#ifdef yypush_buffer_state
+#define netconf_push_buffer_state_ALREADY_DEFINED
+#else
+#define yypush_buffer_state netconf_push_buffer_state
+#endif
+
+#ifdef yypop_buffer_state
+#define netconf_pop_buffer_state_ALREADY_DEFINED
+#else
+#define yypop_buffer_state netconf_pop_buffer_state
+#endif
+
+#ifdef yyensure_buffer_stack
+#define netconf_ensure_buffer_stack_ALREADY_DEFINED
+#else
+#define yyensure_buffer_stack netconf_ensure_buffer_stack
+#endif
+
+#ifdef yylex
+#define netconf_lex_ALREADY_DEFINED
+#else
+#define yylex netconf_lex
+#endif
+
+#ifdef yyrestart
+#define netconf_restart_ALREADY_DEFINED
+#else
+#define yyrestart netconf_restart
+#endif
+
+#ifdef yylex_init
+#define netconf_lex_init_ALREADY_DEFINED
+#else
+#define yylex_init netconf_lex_init
+#endif
+
+#ifdef yylex_init_extra
+#define netconf_lex_init_extra_ALREADY_DEFINED
+#else
+#define yylex_init_extra netconf_lex_init_extra
+#endif
+
+#ifdef yylex_destroy
+#define netconf_lex_destroy_ALREADY_DEFINED
+#else
+#define yylex_destroy netconf_lex_destroy
+#endif
+
+#ifdef yyget_debug
+#define netconf_get_debug_ALREADY_DEFINED
+#else
+#define yyget_debug netconf_get_debug
+#endif
+
+#ifdef yyset_debug
+#define netconf_set_debug_ALREADY_DEFINED
+#else
+#define yyset_debug netconf_set_debug
+#endif
+
+#ifdef yyget_extra
+#define netconf_get_extra_ALREADY_DEFINED
+#else
+#define yyget_extra netconf_get_extra
+#endif
+
+#ifdef yyset_extra
+#define netconf_set_extra_ALREADY_DEFINED
+#else
+#define yyset_extra netconf_set_extra
+#endif
+
+#ifdef yyget_in
+#define netconf_get_in_ALREADY_DEFINED
+#else
+#define yyget_in netconf_get_in
+#endif
+
+#ifdef yyset_in
+#define netconf_set_in_ALREADY_DEFINED
+#else
+#define yyset_in netconf_set_in
+#endif
+
+#ifdef yyget_out
+#define netconf_get_out_ALREADY_DEFINED
+#else
+#define yyget_out netconf_get_out
+#endif
+
+#ifdef yyset_out
+#define netconf_set_out_ALREADY_DEFINED
+#else
+#define yyset_out netconf_set_out
+#endif
+
+#ifdef yyget_leng
+#define netconf_get_leng_ALREADY_DEFINED
+#else
+#define yyget_leng netconf_get_leng
+#endif
+
+#ifdef yyget_text
+#define netconf_get_text_ALREADY_DEFINED
+#else
+#define yyget_text netconf_get_text
+#endif
+
+#ifdef yyget_lineno
+#define netconf_get_lineno_ALREADY_DEFINED
+#else
+#define yyget_lineno netconf_get_lineno
+#endif
+
+#ifdef yyset_lineno
+#define netconf_set_lineno_ALREADY_DEFINED
+#else
+#define yyset_lineno netconf_set_lineno
+#endif
+
+#ifdef yywrap
+#define netconf_wrap_ALREADY_DEFINED
+#else
+#define yywrap netconf_wrap
+#endif
+
+/* %endif */
+
+#ifdef yyalloc
+#define netconf_alloc_ALREADY_DEFINED
+#else
+#define yyalloc netconf_alloc
+#endif
+
+#ifdef yyrealloc
+#define netconf_realloc_ALREADY_DEFINED
+#else
+#define yyrealloc netconf_realloc
+#endif
+
+#ifdef yyfree
+#define netconf_free_ALREADY_DEFINED
+#else
+#define yyfree netconf_free
+#endif
+
+/* %if-c-only */
+
+#ifdef yytext
+#define netconf_text_ALREADY_DEFINED
+#else
+#define yytext netconf_text
+#endif
+
+#ifdef yyleng
+#define netconf_leng_ALREADY_DEFINED
+#else
+#define yyleng netconf_leng
+#endif
+
+#ifdef yyin
+#define netconf_in_ALREADY_DEFINED
+#else
+#define yyin netconf_in
+#endif
+
+#ifdef yyout
+#define netconf_out_ALREADY_DEFINED
+#else
+#define yyout netconf_out
+#endif
+
+#ifdef yy_flex_debug
+#define netconf__flex_debug_ALREADY_DEFINED
+#else
+#define yy_flex_debug netconf__flex_debug
+#endif
+
+#ifdef yylineno
+#define netconf_lineno_ALREADY_DEFINED
+#else
+#define yylineno netconf_lineno
+#endif
+
+/* %endif */
+
+/* First, we deal with platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+/* %if-c-only */
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+/* %endif */
+
+/* %if-tables-serialization */
+/* %endif */
+/* end standard C headers. */
+
+/* %if-c-or-c++ */
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
+ * if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t;
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* %endif */
+
+/* begin standard C++ headers. */
+/* %if-c++-only */
+/* %endif */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* %not-for-header */
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+/* %ok-for-header */
+
+/* %not-for-header */
+/* Promotes a possibly negative, possibly signed char to an
+ * integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+/* %ok-for-header */
+
+/* %if-reentrant */
+/* %endif */
+
+/* %if-not-reentrant */
+
+/* %endif */
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+/* %if-not-reentrant */
+extern int yyleng;
+/* %endif */
+
+/* %if-c-only */
+/* %if-not-reentrant */
+extern FILE *yyin, *yyout;
+/* %endif */
+/* %endif */
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+ #define YY_LINENO_REWIND_TO(ptr)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = (yy_hold_char); \
+ YY_RESTORE_YY_MORE_OFFSET \
+ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+#define unput(c) yyunput( c, (yytext_ptr) )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+ {
+/* %if-c-only */
+ FILE *yy_input_file;
+/* %endif */
+
+/* %if-c++-only */
+/* %endif */
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ int yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ int yy_bs_lineno; /**< The line count. */
+ int yy_bs_column; /**< The column count. */
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
+/* %if-not-reentrant */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */
+/* %endif */
+/* %ok-for-header */
+
+/* %endif */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+ : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* %if-c-only Standard (non-C++) definition */
+
+/* %if-not-reentrant */
+/* %not-for-header */
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = NULL;
+static int yy_init = 0; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+/* %ok-for-header */
+
+/* %endif */
+
+void yyrestart ( FILE *input_file );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size );
+void yy_delete_buffer ( YY_BUFFER_STATE b );
+void yy_flush_buffer ( YY_BUFFER_STATE b );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer );
+void yypop_buffer_state ( void );
+
+static void yyensure_buffer_stack ( void );
+static void yy_load_buffer_state ( void );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len );
+
+/* %endif */
+
+void *yyalloc ( yy_size_t );
+void *yyrealloc ( void *, yy_size_t );
+void yyfree ( void * );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ yyensure_buffer_stack (); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* %% [1.0] yytext/yyin/yyout/yy_state_type/yylineno etc. def's & init go here */
+/* Begin user sect3 */
+
+#define netconf_wrap() (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+
+#define FLEX_DEBUG
+typedef flex_uint8_t YY_CHAR;
+
+FILE *yyin = NULL, *yyout = NULL;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+int yylineno = 1;
+
+extern char *yytext;
+#ifdef yytext_ptr
+#undef yytext_ptr
+#endif
+#define yytext_ptr yytext
+
+/* %% [1.5] DFA */
+
+/* %if-c-only Standard (non-C++) definition */
+
+static yy_state_type yy_get_previous_state ( void );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state );
+static int yy_get_next_buffer ( void );
+static void yynoreturn yy_fatal_error ( const char* msg );
+
+/* %endif */
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ (yytext_ptr) = yy_bp; \
+/* %% [2.0] code to fiddle yytext and yyleng for yymore() goes here \ */\
+ yyleng = (int) (yy_cp - yy_bp); \
+ (yy_hold_char) = *yy_cp; \
+ *yy_cp = '\0'; \
+/* %% [3.0] code to copy yytext_ptr to yytext[] goes here, if %array \ */\
+ (yy_c_buf_p) = yy_cp;
+/* %% [4.0] data tables for the DFA and the user's section 1 definitions go here */
+#define YY_NUM_RULES 63
+#define YY_END_OF_BUFFER 64
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static const flex_int16_t yy_accept[362] =
+ { 0,
+ 56, 56, 0, 0, 0, 0, 0, 0, 0, 0,
+ 64, 62, 10, 11, 62, 1, 56, 53, 56, 56,
+ 62, 55, 54, 62, 62, 62, 62, 62, 49, 50,
+ 62, 62, 62, 51, 52, 5, 5, 5, 62, 62,
+ 62, 10, 11, 0, 0, 44, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 1, 56, 56, 0, 55, 56, 3, 2, 6,
+ 0, 56, 0, 0, 0, 0, 0, 0, 4, 0,
+ 0, 9, 0, 45, 0, 0, 0, 47, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
+ 8, 0, 0, 46, 48, 0, 0, 22, 0, 0,
+ 21, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 61, 59, 0, 58, 57, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 60, 57, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 26,
+
+ 0, 0, 0, 0, 0, 0, 35, 0, 0, 0,
+ 0, 0, 0, 0, 25, 0, 0, 0, 0, 0,
+ 0, 0, 0, 19, 20, 38, 0, 0, 0, 0,
+ 0, 0, 23, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 7, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 40, 37, 0, 0, 0, 0, 0,
+ 27, 0, 0, 0, 12, 0, 14, 0, 0, 0,
+ 32, 34, 0, 39, 0, 0, 41, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 43, 0, 0, 0, 0, 0, 0, 0, 0,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 42, 0, 0, 0, 33, 0, 0,
+ 30, 0, 0, 0, 15, 0, 0, 0, 0, 29,
+ 28, 0, 0, 0, 0, 0, 0, 0, 0, 13,
+ 0, 0, 0, 0, 0, 0, 0, 24, 0, 0,
+ 36, 0, 0, 31, 18, 0, 0, 0, 17, 16,
+ 0
+ } ;
+
+static const YY_CHAR yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 4, 5, 6, 7, 5, 5, 5, 5, 5,
+ 5, 8, 9, 10, 11, 12, 13, 14, 14, 15,
+ 14, 16, 14, 17, 14, 14, 14, 18, 5, 19,
+ 5, 20, 21, 5, 22, 23, 23, 23, 24, 25,
+ 5, 5, 5, 5, 5, 26, 5, 27, 5, 5,
+ 5, 28, 29, 30, 31, 5, 5, 5, 5, 5,
+ 32, 33, 34, 5, 35, 5, 36, 37, 38, 39,
+
+ 40, 41, 42, 43, 44, 5, 45, 46, 47, 48,
+ 49, 50, 5, 51, 52, 53, 54, 55, 5, 56,
+ 57, 58, 59, 5, 60, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5
+ } ;
+
+static const YY_CHAR yy_meta[61] =
+ { 0,
+ 1, 1, 2, 1, 1, 3, 1, 1, 1, 1,
+ 1, 1, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 4, 1, 1, 1, 4, 1, 1, 1,
+ 4, 1, 1, 1, 1, 1, 1, 4, 1, 1,
+ 4, 1, 4, 4, 1, 1, 1, 1, 1, 1
+ } ;
+
+static const flex_int16_t yy_base[371] =
+ { 0,
+ 0, 0, 59, 62, 65, 0, 63, 67, 47, 64,
+ 286, 2435, 84, 259, 125, 0, 105, 2435, 120, 125,
+ 85, 169, 2435, 238, 139, 65, 69, 82, 2435, 2435,
+ 80, 84, 118, 2435, 2435, 2435, 92, 205, 168, 0,
+ 193, 110, 205, 97, 186, 2435, 193, 184, 201, 209,
+ 221, 238, 245, 264, 254, 271, 279, 290, 299, 308,
+ 314, 0, 314, 347, 358, 364, 368, 2435, 0, 2435,
+ 214, 320, 121, 152, 128, 174, 217, 160, 2435, 168,
+ 199, 2435, 137, 2435, 0, 388, 195, 394, 436, 400,
+ 410, 422, 429, 462, 477, 483, 490, 499, 505, 511,
+
+ 518, 524, 544, 553, 559, 566, 578, 587, 594, 601,
+ 616, 622, 0, 223, 223, 255, 224, 242, 259, 130,
+ 2435, 634, 155, 2435, 2435, 659, 656, 2435, 645, 684,
+ 2435, 700, 709, 717, 723, 729, 737, 743, 771, 780,
+ 788, 794, 800, 808, 814, 823, 836, 851, 858, 869,
+ 876, 884, 282, 2435, 2435, 300, 2435, 2435, 103, 891,
+ 927, 902, 913, 928, 953, 968, 974, 986, 996, 1003,
+ 1009, 1019, 1025, 1031, 1042, 1053, 1064, 1075, 1081, 1090,
+ 1097, 1103, 1109, 1123, 1132, 1143, 2435, 2435, 112, 1152,
+ 1187, 1160, 1172, 1166, 1185, 1231, 1237, 1243, 1249, 2435,
+
+ 1255, 1265, 1271, 1277, 1288, 1294, 2435, 1311, 1317, 1323,
+ 1335, 1345, 1352, 1359, 2435, 1369, 1380, 64, 1387, 1393,
+ 1416, 1428, 1435, 2435, 2435, 2435, 1444, 1450, 1456, 1464,
+ 1478, 1490, 2435, 1498, 1511, 1518, 1524, 1534, 1540, 1546,
+ 1552, 1559, 2435, 1569, 1580, 1592, 1603, 1614, 1620, 1626,
+ 1632, 1638, 1649, 2435, 2435, 1655, 1672, 1678, 1684, 1690,
+ 2435, 1696, 1707, 1713, 2435, 1724, 2435, 1733, 1746, 1761,
+ 2435, 2435, 1767, 2435, 1774, 1781, 2435, 1787, 1800, 1807,
+ 1815, 1825, 1836, 1843, 1849, 1871, 1877, 1884, 1891, 1897,
+ 1905, 2435, 1912, 1931, 1938, 1945, 1952, 1960, 1966, 1974,
+
+ 1985, 1994, 2002, 2008, 2014, 2022, 2030, 2036, 2042, 2048,
+ 2055, 2070, 2076, 2435, 2083, 2089, 2099, 2435, 2105, 2112,
+ 2435, 2122, 2134, 2140, 2435, 2148, 2156, 2168, 2176, 2435,
+ 2435, 2189, 2196, 2204, 2210, 2217, 2225, 2232, 2238, 2435,
+ 2245, 2258, 2266, 2279, 2287, 2294, 2300, 2435, 2308, 2320,
+ 2435, 2336, 2342, 2435, 2435, 2348, 2354, 2360, 2435, 2435,
+ 2435, 2400, 2404, 2408, 2412, 2416, 2420, 2424, 2426, 2430
+ } ;
+
+static const flex_int16_t yy_def[371] =
+ { 0,
+ 361, 1, 362, 362, 1, 5, 5, 5, 5, 5,
+ 361, 361, 361, 361, 363, 364, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 365,
+ 361, 361, 361, 366, 363, 361, 363, 367, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 364, 361, 361, 361, 361, 361, 361, 368, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 365, 361, 366, 361, 369, 363, 370, 363, 367, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+
+ 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 368, 361, 361, 361, 361, 361, 361, 361,
+ 361, 363, 370, 361, 361, 89, 363, 361, 363, 363,
+ 361, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 361, 361, 361, 361, 361, 361, 361, 363,
+ 89, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 361, 361, 361, 363,
+ 89, 363, 363, 363, 363, 363, 363, 363, 363, 361,
+
+ 363, 363, 363, 363, 363, 363, 361, 363, 363, 363,
+ 363, 363, 363, 363, 361, 363, 363, 361, 363, 363,
+ 363, 363, 363, 361, 361, 361, 363, 363, 363, 363,
+ 363, 363, 361, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 361, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 361, 361, 363, 363, 363, 363, 363,
+ 361, 363, 363, 363, 361, 363, 361, 363, 363, 363,
+ 361, 361, 363, 361, 363, 363, 361, 363, 363, 363,
+ 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 361, 363, 363, 363, 363, 363, 363, 363, 363,
+
+ 363, 363, 363, 363, 363, 363, 363, 363, 363, 363,
+ 363, 363, 363, 361, 363, 363, 363, 361, 363, 363,
+ 361, 363, 363, 363, 361, 363, 363, 363, 363, 361,
+ 361, 363, 363, 363, 363, 363, 363, 363, 363, 361,
+ 363, 363, 363, 363, 363, 363, 363, 361, 363, 363,
+ 361, 363, 363, 361, 361, 363, 363, 363, 361, 361,
+ 0, 361, 361, 361, 361, 361, 361, 361, 361, 361
+ } ;
+
+static const flex_int16_t yy_nxt[2496] =
+ { 0,
+ 12, 13, 14, 13, 12, 15, 16, 12, 17, 18,
+ 19, 20, 21, 22, 22, 22, 22, 23, 24, 12,
+ 12, 12, 12, 25, 26, 12, 27, 12, 12, 28,
+ 12, 29, 12, 30, 12, 12, 12, 12, 12, 25,
+ 31, 12, 12, 12, 12, 12, 12, 32, 12, 12,
+ 12, 12, 33, 12, 12, 12, 12, 12, 34, 35,
+ 37, 14, 37, 37, 14, 37, 38, 41, 40, 38,
+ 12, 12, 40, 12, 12, 12, 12, 12, 12, 12,
+ 12, 12, 12, 12, 41, 42, 73, 42, 12, 12,
+ 12, 12, 68, 42, 12, 42, 12, 69, 12, 74,
+
+ 73, 73, 84, 243, 12, 12, 12, 12, 39, 75,
+ 12, 42, 12, 42, 74, 76, 63, 12, 64, 64,
+ 64, 64, 74, 12, 12, 44, 44, 44, 65, 85,
+ 46, 63, 75, 66, 66, 66, 66, 77, 67, 67,
+ 67, 67, 84, 65, 65, 75, 114, 71, 65, 71,
+ 218, 47, 72, 72, 72, 72, 189, 48, 116, 65,
+ 124, 49, 50, 51, 65, 52, 114, 53, 78, 85,
+ 54, 55, 56, 57, 58, 159, 59, 115, 60, 61,
+ 63, 116, 66, 66, 66, 66, 44, 44, 44, 88,
+ 116, 46, 65, 44, 44, 44, 45, 115, 46, 114,
+
+ 124, 44, 44, 44, 121, 120, 46, 43, 65, 44,
+ 44, 44, 82, 119, 46, 80, 45, 79, 48, 117,
+ 45, 44, 44, 44, 45, 48, 46, 72, 72, 72,
+ 72, 45, 86, 48, 45, 93, 45, 89, 44, 44,
+ 44, 48, 115, 46, 91, 44, 44, 44, 154, 90,
+ 46, 153, 153, 48, 44, 44, 44, 92, 70, 46,
+ 94, 43, 118, 95, 44, 44, 44, 154, 154, 46,
+ 48, 44, 44, 44, 153, 156, 46, 48, 155, 44,
+ 44, 44, 155, 96, 46, 361, 48, 157, 361, 101,
+ 44, 44, 44, 97, 155, 46, 48, 98, 158, 44,
+
+ 44, 44, 102, 48, 46, 187, 103, 99, 44, 44,
+ 44, 48, 100, 46, 44, 44, 44, 361, 361, 46,
+ 361, 187, 48, 187, 361, 105, 361, 67, 67, 67,
+ 67, 48, 104, 72, 72, 72, 72, 65, 106, 188,
+ 48, 361, 361, 361, 361, 361, 48, 107, 361, 112,
+ 361, 108, 109, 65, 361, 110, 361, 361, 63, 111,
+ 64, 64, 64, 64, 361, 361, 71, 361, 71, 361,
+ 65, 72, 72, 72, 72, 63, 361, 66, 66, 66,
+ 66, 67, 67, 67, 67, 361, 65, 65, 44, 44,
+ 44, 65, 361, 46, 44, 44, 44, 361, 361, 46,
+
+ 44, 44, 44, 65, 361, 46, 361, 65, 361, 361,
+ 44, 44, 44, 361, 361, 128, 361, 361, 361, 361,
+ 48, 361, 44, 44, 44, 361, 48, 46, 361, 44,
+ 44, 44, 48, 361, 131, 361, 361, 361, 361, 361,
+ 122, 125, 48, 361, 361, 361, 361, 361, 127, 126,
+ 126, 126, 126, 361, 48, 361, 361, 126, 126, 126,
+ 126, 48, 44, 44, 44, 361, 361, 46, 129, 130,
+ 361, 126, 126, 126, 126, 126, 126, 44, 44, 44,
+ 361, 361, 46, 44, 44, 44, 361, 361, 46, 361,
+ 44, 44, 44, 361, 48, 46, 361, 361, 132, 44,
+
+ 44, 44, 361, 361, 46, 44, 44, 44, 361, 48,
+ 46, 44, 44, 44, 133, 48, 46, 361, 44, 44,
+ 44, 361, 48, 46, 44, 44, 44, 361, 361, 46,
+ 361, 48, 361, 361, 361, 361, 134, 48, 135, 361,
+ 361, 137, 361, 48, 44, 44, 44, 361, 361, 46,
+ 48, 136, 138, 44, 44, 44, 48, 361, 46, 44,
+ 44, 44, 141, 361, 46, 139, 44, 44, 44, 361,
+ 361, 46, 361, 140, 361, 361, 48, 361, 44, 44,
+ 44, 361, 361, 46, 361, 48, 361, 44, 44, 44,
+ 142, 48, 46, 361, 44, 44, 44, 361, 48, 46,
+
+ 361, 44, 44, 44, 361, 143, 46, 361, 361, 144,
+ 48, 145, 361, 361, 361, 147, 44, 44, 44, 48,
+ 146, 46, 44, 44, 44, 148, 48, 46, 361, 361,
+ 149, 361, 361, 48, 44, 44, 44, 361, 361, 46,
+ 361, 361, 361, 361, 150, 44, 44, 44, 48, 361,
+ 46, 361, 361, 361, 48, 151, 44, 44, 44, 361,
+ 361, 46, 361, 361, 361, 361, 48, 152, 361, 361,
+ 361, 160, 161, 161, 161, 161, 361, 48, 361, 361,
+ 161, 161, 161, 161, 44, 44, 44, 361, 48, 46,
+ 361, 163, 361, 361, 161, 161, 161, 161, 161, 161,
+
+ 44, 44, 44, 361, 361, 46, 361, 361, 162, 44,
+ 44, 44, 361, 361, 46, 361, 48, 44, 44, 44,
+ 361, 361, 46, 44, 44, 44, 361, 361, 46, 44,
+ 44, 44, 48, 361, 46, 361, 164, 44, 44, 44,
+ 361, 48, 46, 44, 44, 44, 361, 361, 46, 48,
+ 361, 361, 361, 165, 361, 48, 361, 361, 166, 361,
+ 361, 48, 361, 361, 361, 361, 361, 168, 167, 48,
+ 361, 44, 44, 44, 361, 48, 46, 361, 169, 361,
+ 44, 44, 44, 361, 171, 46, 361, 170, 44, 44,
+ 44, 361, 361, 46, 44, 44, 44, 361, 361, 46,
+
+ 44, 44, 44, 48, 361, 46, 172, 361, 44, 44,
+ 44, 361, 48, 46, 44, 44, 44, 361, 361, 46,
+ 48, 361, 361, 44, 44, 44, 48, 175, 46, 361,
+ 361, 173, 48, 176, 174, 361, 44, 44, 44, 361,
+ 48, 46, 361, 178, 361, 361, 48, 361, 361, 177,
+ 361, 44, 44, 44, 361, 48, 46, 361, 44, 44,
+ 44, 361, 180, 46, 361, 361, 179, 361, 48, 44,
+ 44, 44, 361, 361, 46, 361, 44, 44, 44, 361,
+ 181, 46, 361, 48, 44, 44, 44, 361, 361, 46,
+ 48, 44, 44, 44, 361, 361, 46, 361, 361, 182,
+
+ 361, 48, 44, 44, 44, 361, 361, 46, 48, 183,
+ 361, 361, 192, 44, 44, 44, 48, 361, 46, 361,
+ 361, 361, 361, 48, 184, 361, 185, 186, 44, 44,
+ 44, 361, 361, 46, 48, 361, 361, 361, 361, 190,
+ 191, 191, 191, 191, 361, 48, 361, 361, 191, 191,
+ 191, 191, 193, 44, 44, 44, 361, 361, 46, 361,
+ 48, 361, 191, 191, 191, 191, 191, 191, 44, 44,
+ 44, 361, 361, 46, 44, 44, 44, 361, 194, 46,
+ 361, 361, 361, 196, 197, 48, 44, 44, 44, 361,
+ 361, 46, 361, 361, 195, 361, 44, 44, 44, 361,
+
+ 48, 200, 361, 44, 44, 44, 48, 361, 46, 44,
+ 44, 44, 361, 361, 46, 361, 198, 361, 48, 44,
+ 44, 44, 361, 361, 46, 44, 44, 44, 48, 361,
+ 46, 44, 44, 44, 361, 48, 46, 199, 201, 361,
+ 361, 48, 44, 44, 44, 361, 361, 46, 202, 361,
+ 361, 48, 361, 44, 44, 44, 361, 48, 207, 361,
+ 203, 361, 361, 48, 44, 44, 44, 361, 204, 46,
+ 205, 361, 361, 361, 48, 44, 44, 44, 361, 361,
+ 46, 44, 44, 44, 361, 48, 46, 206, 361, 361,
+ 44, 44, 44, 361, 361, 46, 48, 44, 44, 44,
+
+ 361, 361, 46, 44, 44, 44, 361, 48, 46, 44,
+ 44, 44, 361, 48, 46, 361, 361, 208, 361, 361,
+ 210, 209, 48, 44, 44, 44, 361, 361, 215, 48,
+ 361, 361, 44, 44, 44, 48, 212, 46, 361, 361,
+ 211, 48, 216, 44, 44, 44, 214, 361, 46, 361,
+ 361, 361, 44, 44, 44, 48, 213, 46, 361, 361,
+ 44, 44, 44, 361, 48, 46, 44, 44, 44, 361,
+ 361, 46, 44, 44, 44, 48, 361, 46, 361, 361,
+ 361, 217, 361, 361, 48, 44, 44, 44, 361, 361,
+ 46, 361, 48, 361, 361, 361, 361, 361, 48, 219,
+
+ 45, 45, 45, 45, 48, 361, 361, 361, 45, 45,
+ 45, 45, 361, 220, 222, 361, 361, 48, 361, 221,
+ 361, 361, 45, 45, 45, 45, 45, 45, 361, 361,
+ 223, 44, 44, 44, 361, 361, 224, 44, 44, 44,
+ 361, 361, 225, 44, 44, 44, 361, 361, 226, 44,
+ 44, 44, 361, 361, 46, 44, 44, 44, 361, 227,
+ 46, 361, 361, 48, 361, 44, 44, 44, 361, 48,
+ 46, 44, 44, 44, 361, 48, 46, 44, 44, 44,
+ 361, 48, 46, 361, 361, 361, 361, 48, 44, 44,
+ 44, 361, 361, 46, 44, 44, 44, 48, 361, 233,
+
+ 361, 361, 361, 48, 361, 228, 361, 361, 361, 48,
+ 230, 44, 44, 44, 361, 229, 46, 44, 44, 44,
+ 48, 361, 46, 44, 44, 44, 48, 361, 46, 361,
+ 361, 361, 361, 361, 231, 44, 44, 44, 232, 361,
+ 46, 361, 361, 48, 361, 44, 44, 44, 361, 48,
+ 46, 361, 44, 44, 44, 48, 235, 46, 361, 44,
+ 44, 44, 361, 234, 46, 361, 361, 48, 361, 44,
+ 44, 44, 361, 236, 46, 361, 361, 48, 237, 361,
+ 44, 44, 44, 361, 48, 46, 361, 44, 44, 44,
+ 361, 48, 46, 44, 44, 44, 361, 238, 46, 361,
+
+ 361, 48, 361, 361, 239, 361, 241, 361, 361, 240,
+ 361, 361, 48, 361, 361, 242, 44, 44, 44, 48,
+ 361, 46, 361, 361, 361, 48, 361, 244, 44, 44,
+ 44, 361, 361, 46, 361, 44, 44, 44, 361, 361,
+ 46, 361, 245, 361, 44, 44, 44, 361, 48, 46,
+ 44, 44, 44, 361, 361, 46, 44, 44, 44, 361,
+ 48, 46, 361, 361, 44, 44, 44, 48, 246, 46,
+ 361, 361, 361, 247, 248, 361, 48, 361, 44, 44,
+ 44, 361, 48, 46, 361, 361, 361, 361, 48, 249,
+ 44, 44, 44, 361, 361, 254, 48, 361, 44, 44,
+
+ 44, 361, 252, 255, 361, 361, 250, 251, 361, 361,
+ 48, 44, 44, 44, 361, 361, 46, 253, 44, 44,
+ 44, 361, 48, 46, 44, 44, 44, 361, 361, 46,
+ 48, 361, 256, 361, 44, 44, 44, 361, 361, 46,
+ 44, 44, 44, 48, 260, 261, 44, 44, 44, 361,
+ 48, 46, 44, 44, 44, 361, 48, 46, 361, 44,
+ 44, 44, 361, 257, 46, 258, 48, 361, 361, 44,
+ 44, 44, 48, 361, 265, 361, 259, 361, 48, 361,
+ 44, 44, 44, 361, 48, 46, 361, 361, 361, 262,
+ 361, 48, 44, 44, 44, 361, 361, 267, 361, 361,
+
+ 263, 48, 361, 44, 44, 44, 361, 361, 46, 361,
+ 361, 264, 48, 268, 44, 44, 44, 361, 266, 46,
+ 44, 44, 44, 361, 48, 46, 44, 44, 44, 361,
+ 361, 271, 44, 44, 44, 48, 361, 272, 44, 44,
+ 44, 361, 361, 46, 361, 361, 48, 361, 273, 44,
+ 44, 44, 48, 361, 274, 44, 44, 44, 48, 361,
+ 46, 361, 361, 270, 48, 361, 361, 361, 269, 361,
+ 48, 361, 44, 44, 44, 361, 361, 46, 44, 44,
+ 44, 48, 361, 277, 44, 44, 44, 48, 361, 46,
+ 44, 44, 44, 361, 361, 46, 44, 44, 44, 361,
+
+ 361, 46, 361, 275, 48, 361, 361, 44, 44, 44,
+ 48, 276, 46, 44, 44, 44, 48, 361, 46, 361,
+ 361, 361, 48, 361, 44, 44, 44, 361, 48, 46,
+ 361, 361, 282, 44, 44, 44, 361, 279, 46, 48,
+ 278, 361, 280, 281, 361, 48, 44, 44, 44, 361,
+ 361, 46, 284, 361, 283, 361, 48, 361, 361, 285,
+ 361, 44, 44, 44, 361, 48, 46, 44, 44, 44,
+ 361, 361, 46, 361, 44, 44, 44, 361, 48, 46,
+ 361, 44, 44, 44, 286, 287, 46, 44, 44, 44,
+ 361, 361, 292, 48, 361, 361, 361, 288, 361, 48,
+
+ 44, 44, 44, 361, 361, 46, 48, 44, 44, 44,
+ 361, 361, 46, 48, 361, 44, 44, 44, 289, 48,
+ 46, 361, 361, 290, 361, 44, 44, 44, 361, 361,
+ 46, 291, 48, 361, 361, 293, 44, 44, 44, 48,
+ 361, 46, 361, 44, 44, 44, 361, 48, 46, 44,
+ 44, 44, 361, 298, 46, 361, 361, 48, 361, 361,
+ 361, 361, 361, 294, 296, 295, 361, 361, 48, 361,
+ 361, 44, 44, 44, 361, 48, 46, 44, 44, 44,
+ 361, 48, 46, 361, 44, 44, 44, 361, 297, 46,
+ 361, 44, 44, 44, 361, 361, 46, 44, 44, 44,
+
+ 361, 299, 46, 48, 361, 44, 44, 44, 361, 48,
+ 46, 361, 44, 44, 44, 361, 48, 46, 361, 300,
+ 361, 361, 301, 48, 361, 361, 361, 361, 361, 48,
+ 303, 44, 44, 44, 302, 361, 46, 48, 44, 44,
+ 44, 361, 361, 46, 48, 44, 44, 44, 361, 304,
+ 46, 361, 44, 44, 44, 309, 305, 46, 306, 361,
+ 44, 44, 44, 48, 361, 46, 44, 44, 44, 361,
+ 48, 46, 361, 361, 44, 44, 44, 48, 361, 46,
+ 307, 361, 361, 308, 48, 44, 44, 44, 361, 361,
+ 314, 310, 48, 361, 44, 44, 44, 311, 48, 46,
+
+ 361, 361, 44, 44, 44, 312, 48, 46, 44, 44,
+ 44, 313, 361, 46, 44, 44, 44, 48, 361, 318,
+ 361, 361, 44, 44, 44, 361, 48, 46, 361, 315,
+ 44, 44, 44, 361, 48, 46, 44, 44, 44, 361,
+ 48, 321, 44, 44, 44, 361, 48, 46, 44, 44,
+ 44, 317, 316, 46, 48, 44, 44, 44, 361, 361,
+ 46, 319, 48, 361, 361, 361, 361, 361, 48, 320,
+ 44, 44, 44, 361, 48, 325, 44, 44, 44, 322,
+ 48, 46, 361, 44, 44, 44, 361, 48, 46, 44,
+ 44, 44, 361, 361, 46, 361, 361, 324, 361, 44,
+
+ 44, 44, 48, 323, 46, 44, 44, 44, 48, 361,
+ 330, 361, 44, 44, 44, 48, 361, 331, 361, 361,
+ 326, 48, 44, 44, 44, 361, 361, 46, 361, 361,
+ 361, 48, 361, 327, 44, 44, 44, 48, 361, 46,
+ 44, 44, 44, 328, 48, 46, 361, 329, 44, 44,
+ 44, 361, 361, 46, 48, 361, 44, 44, 44, 361,
+ 361, 46, 361, 361, 332, 361, 48, 361, 44, 44,
+ 44, 361, 48, 46, 361, 334, 44, 44, 44, 361,
+ 48, 46, 361, 361, 361, 361, 333, 335, 48, 44,
+ 44, 44, 361, 361, 46, 361, 44, 44, 44, 336,
+
+ 48, 340, 361, 361, 44, 44, 44, 337, 48, 46,
+ 44, 44, 44, 361, 361, 46, 361, 44, 44, 44,
+ 361, 48, 46, 338, 339, 44, 44, 44, 48, 361,
+ 46, 361, 44, 44, 44, 361, 48, 46, 44, 44,
+ 44, 361, 48, 46, 361, 44, 44, 44, 361, 48,
+ 46, 341, 361, 361, 361, 361, 343, 48, 44, 44,
+ 44, 361, 342, 348, 48, 361, 44, 44, 44, 361,
+ 48, 46, 361, 361, 361, 344, 361, 48, 361, 44,
+ 44, 44, 361, 345, 46, 346, 347, 44, 44, 44,
+ 48, 361, 351, 361, 44, 44, 44, 361, 48, 46,
+
+ 44, 44, 44, 361, 361, 46, 361, 361, 44, 44,
+ 44, 48, 361, 354, 361, 361, 361, 349, 361, 48,
+ 44, 44, 44, 361, 361, 355, 48, 361, 361, 361,
+ 350, 361, 48, 361, 361, 352, 44, 44, 44, 353,
+ 48, 46, 44, 44, 44, 361, 361, 46, 44, 44,
+ 44, 361, 48, 46, 44, 44, 44, 361, 361, 359,
+ 44, 44, 44, 361, 361, 360, 361, 361, 48, 361,
+ 361, 361, 361, 361, 48, 356, 361, 361, 361, 361,
+ 48, 361, 361, 361, 361, 361, 48, 361, 361, 361,
+ 361, 361, 48, 357, 361, 361, 361, 361, 361, 358,
+
+ 36, 36, 36, 36, 45, 45, 45, 45, 62, 361,
+ 62, 62, 81, 361, 361, 81, 83, 83, 83, 83,
+ 87, 87, 87, 87, 113, 361, 113, 113, 83, 83,
+ 123, 123, 123, 123, 11, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361
+
+ } ;
+
+static const flex_int16_t yy_chk[2496] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 3, 3, 3, 4, 4, 4, 3, 9, 7, 4,
+ 5, 5, 8, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 10, 13, 26, 13, 5, 5,
+ 9, 5, 21, 37, 5, 37, 5, 21, 5, 27,
+
+ 26, 31, 44, 218, 5, 5, 7, 10, 5, 28,
+ 8, 42, 5, 42, 32, 31, 17, 5, 17, 17,
+ 17, 17, 27, 5, 5, 15, 15, 15, 17, 44,
+ 15, 19, 28, 19, 19, 19, 19, 32, 20, 20,
+ 20, 20, 83, 19, 17, 33, 73, 25, 20, 25,
+ 189, 15, 25, 25, 25, 25, 159, 15, 75, 19,
+ 123, 15, 15, 15, 20, 15, 73, 15, 33, 83,
+ 15, 15, 15, 15, 15, 120, 15, 74, 15, 15,
+ 22, 75, 22, 22, 22, 22, 45, 45, 45, 48,
+ 78, 45, 22, 47, 47, 47, 48, 74, 47, 76,
+
+ 87, 49, 49, 49, 81, 80, 49, 43, 22, 50,
+ 50, 50, 41, 78, 50, 39, 48, 38, 45, 76,
+ 48, 51, 51, 51, 48, 47, 51, 71, 71, 71,
+ 71, 48, 47, 49, 48, 51, 48, 48, 52, 52,
+ 52, 50, 77, 52, 50, 53, 53, 53, 115, 49,
+ 53, 114, 117, 51, 55, 55, 55, 50, 24, 55,
+ 51, 14, 77, 51, 54, 54, 54, 118, 115, 54,
+ 52, 56, 56, 56, 114, 117, 56, 53, 116, 57,
+ 57, 57, 119, 52, 57, 11, 55, 118, 0, 55,
+ 58, 58, 58, 53, 116, 58, 54, 53, 119, 59,
+
+ 59, 59, 55, 56, 59, 153, 56, 54, 60, 60,
+ 60, 57, 54, 60, 61, 61, 61, 0, 0, 61,
+ 0, 153, 58, 156, 0, 58, 0, 63, 63, 63,
+ 63, 59, 57, 72, 72, 72, 72, 63, 59, 156,
+ 60, 0, 0, 0, 0, 0, 61, 59, 0, 61,
+ 0, 59, 59, 63, 0, 60, 0, 0, 64, 60,
+ 64, 64, 64, 64, 0, 0, 65, 0, 65, 0,
+ 64, 65, 65, 65, 65, 66, 0, 66, 66, 66,
+ 66, 67, 67, 67, 67, 0, 64, 66, 86, 86,
+ 86, 67, 0, 86, 88, 88, 88, 0, 0, 88,
+
+ 90, 90, 90, 66, 0, 90, 0, 67, 0, 0,
+ 91, 91, 91, 0, 0, 91, 0, 0, 0, 0,
+ 86, 0, 92, 92, 92, 0, 88, 92, 0, 93,
+ 93, 93, 90, 0, 93, 0, 0, 0, 0, 0,
+ 86, 89, 91, 0, 0, 0, 0, 0, 90, 89,
+ 89, 89, 89, 0, 92, 0, 0, 89, 89, 89,
+ 89, 93, 94, 94, 94, 0, 0, 94, 92, 92,
+ 0, 89, 89, 89, 89, 89, 89, 95, 95, 95,
+ 0, 0, 95, 96, 96, 96, 0, 0, 96, 0,
+ 97, 97, 97, 0, 94, 97, 0, 0, 94, 98,
+
+ 98, 98, 0, 0, 98, 99, 99, 99, 0, 95,
+ 99, 100, 100, 100, 95, 96, 100, 0, 101, 101,
+ 101, 0, 97, 101, 102, 102, 102, 0, 0, 102,
+ 0, 98, 0, 0, 0, 0, 96, 99, 97, 0,
+ 0, 99, 0, 100, 103, 103, 103, 0, 0, 103,
+ 101, 98, 100, 104, 104, 104, 102, 0, 104, 105,
+ 105, 105, 102, 0, 105, 101, 106, 106, 106, 0,
+ 0, 106, 0, 101, 0, 0, 103, 0, 107, 107,
+ 107, 0, 0, 107, 0, 104, 0, 108, 108, 108,
+ 103, 105, 108, 0, 109, 109, 109, 0, 106, 109,
+
+ 0, 110, 110, 110, 0, 104, 110, 0, 0, 105,
+ 107, 105, 0, 0, 0, 107, 111, 111, 111, 108,
+ 106, 111, 112, 112, 112, 108, 109, 112, 0, 0,
+ 109, 0, 0, 110, 122, 122, 122, 0, 0, 122,
+ 0, 0, 0, 0, 110, 129, 129, 129, 111, 0,
+ 129, 0, 0, 0, 112, 111, 127, 127, 127, 0,
+ 0, 127, 0, 0, 0, 0, 122, 112, 0, 0,
+ 0, 122, 126, 126, 126, 126, 0, 129, 0, 0,
+ 126, 126, 126, 126, 130, 130, 130, 0, 127, 130,
+ 0, 129, 0, 0, 126, 126, 126, 126, 126, 126,
+
+ 132, 132, 132, 0, 0, 132, 0, 0, 127, 133,
+ 133, 133, 0, 0, 133, 0, 130, 134, 134, 134,
+ 0, 0, 134, 135, 135, 135, 0, 0, 135, 136,
+ 136, 136, 132, 0, 136, 0, 130, 137, 137, 137,
+ 0, 133, 137, 138, 138, 138, 0, 0, 138, 134,
+ 0, 0, 0, 132, 0, 135, 0, 0, 133, 0,
+ 0, 136, 0, 0, 0, 0, 0, 135, 134, 137,
+ 0, 139, 139, 139, 0, 138, 139, 0, 136, 0,
+ 140, 140, 140, 0, 138, 140, 0, 137, 141, 141,
+ 141, 0, 0, 141, 142, 142, 142, 0, 0, 142,
+
+ 143, 143, 143, 139, 0, 143, 139, 0, 144, 144,
+ 144, 0, 140, 144, 145, 145, 145, 0, 0, 145,
+ 141, 0, 0, 146, 146, 146, 142, 141, 146, 0,
+ 0, 140, 143, 142, 140, 0, 147, 147, 147, 0,
+ 144, 147, 0, 144, 0, 0, 145, 0, 0, 143,
+ 0, 148, 148, 148, 0, 146, 148, 0, 149, 149,
+ 149, 0, 146, 149, 0, 0, 145, 0, 147, 150,
+ 150, 150, 0, 0, 150, 0, 151, 151, 151, 0,
+ 147, 151, 0, 148, 152, 152, 152, 0, 0, 152,
+ 149, 160, 160, 160, 0, 0, 160, 0, 0, 148,
+
+ 0, 150, 162, 162, 162, 0, 0, 162, 151, 149,
+ 0, 0, 162, 163, 163, 163, 152, 0, 163, 0,
+ 0, 0, 0, 160, 150, 0, 151, 152, 164, 164,
+ 164, 0, 0, 164, 162, 0, 0, 0, 0, 160,
+ 161, 161, 161, 161, 0, 163, 0, 0, 161, 161,
+ 161, 161, 163, 165, 165, 165, 0, 0, 165, 0,
+ 164, 0, 161, 161, 161, 161, 161, 161, 166, 166,
+ 166, 0, 0, 166, 167, 167, 167, 0, 164, 167,
+ 0, 0, 0, 166, 166, 165, 168, 168, 168, 0,
+ 0, 168, 0, 0, 165, 0, 169, 169, 169, 0,
+
+ 166, 169, 0, 170, 170, 170, 167, 0, 170, 171,
+ 171, 171, 0, 0, 171, 0, 167, 0, 168, 172,
+ 172, 172, 0, 0, 172, 173, 173, 173, 169, 0,
+ 173, 174, 174, 174, 0, 170, 174, 168, 170, 0,
+ 0, 171, 175, 175, 175, 0, 0, 175, 171, 0,
+ 0, 172, 0, 176, 176, 176, 0, 173, 176, 0,
+ 172, 0, 0, 174, 177, 177, 177, 0, 173, 177,
+ 174, 0, 0, 0, 175, 178, 178, 178, 0, 0,
+ 178, 179, 179, 179, 0, 176, 179, 175, 0, 0,
+ 180, 180, 180, 0, 0, 180, 177, 181, 181, 181,
+
+ 0, 0, 181, 182, 182, 182, 0, 178, 182, 183,
+ 183, 183, 0, 179, 183, 0, 0, 177, 0, 0,
+ 179, 178, 180, 184, 184, 184, 0, 0, 184, 181,
+ 0, 0, 185, 185, 185, 182, 181, 185, 0, 0,
+ 180, 183, 185, 186, 186, 186, 183, 0, 186, 0,
+ 0, 0, 190, 190, 190, 184, 182, 190, 0, 0,
+ 192, 192, 192, 0, 185, 192, 194, 194, 194, 0,
+ 0, 194, 193, 193, 193, 186, 0, 193, 0, 0,
+ 0, 186, 0, 0, 190, 195, 195, 195, 0, 0,
+ 195, 0, 192, 0, 0, 0, 0, 0, 194, 190,
+
+ 191, 191, 191, 191, 193, 0, 0, 0, 191, 191,
+ 191, 191, 0, 192, 194, 0, 0, 195, 0, 193,
+ 0, 0, 191, 191, 191, 191, 191, 191, 0, 0,
+ 195, 196, 196, 196, 0, 0, 196, 197, 197, 197,
+ 0, 0, 197, 198, 198, 198, 0, 0, 198, 199,
+ 199, 199, 0, 0, 199, 201, 201, 201, 0, 199,
+ 201, 0, 0, 196, 0, 202, 202, 202, 0, 197,
+ 202, 203, 203, 203, 0, 198, 203, 204, 204, 204,
+ 0, 199, 204, 0, 0, 0, 0, 201, 205, 205,
+ 205, 0, 0, 205, 206, 206, 206, 202, 0, 206,
+
+ 0, 0, 0, 203, 0, 201, 0, 0, 0, 204,
+ 203, 208, 208, 208, 0, 202, 208, 209, 209, 209,
+ 205, 0, 209, 210, 210, 210, 206, 0, 210, 0,
+ 0, 0, 0, 0, 204, 211, 211, 211, 205, 0,
+ 211, 0, 0, 208, 0, 212, 212, 212, 0, 209,
+ 212, 0, 213, 213, 213, 210, 209, 213, 0, 214,
+ 214, 214, 0, 208, 214, 0, 0, 211, 0, 216,
+ 216, 216, 0, 210, 216, 0, 0, 212, 211, 0,
+ 217, 217, 217, 0, 213, 217, 0, 219, 219, 219,
+ 0, 214, 219, 220, 220, 220, 0, 212, 220, 0,
+
+ 0, 216, 0, 0, 213, 0, 216, 0, 0, 214,
+ 0, 0, 217, 0, 0, 217, 221, 221, 221, 219,
+ 0, 221, 0, 0, 0, 220, 0, 219, 222, 222,
+ 222, 0, 0, 222, 0, 223, 223, 223, 0, 0,
+ 223, 0, 220, 0, 227, 227, 227, 0, 221, 227,
+ 228, 228, 228, 0, 0, 228, 229, 229, 229, 0,
+ 222, 229, 0, 0, 230, 230, 230, 223, 221, 230,
+ 0, 0, 0, 222, 223, 0, 227, 0, 231, 231,
+ 231, 0, 228, 231, 0, 0, 0, 0, 229, 227,
+ 232, 232, 232, 0, 0, 232, 230, 0, 234, 234,
+
+ 234, 0, 230, 234, 0, 0, 228, 229, 0, 0,
+ 231, 235, 235, 235, 0, 0, 235, 231, 236, 236,
+ 236, 0, 232, 236, 237, 237, 237, 0, 0, 237,
+ 234, 0, 234, 0, 238, 238, 238, 0, 0, 238,
+ 239, 239, 239, 235, 238, 239, 240, 240, 240, 0,
+ 236, 240, 241, 241, 241, 0, 237, 241, 0, 242,
+ 242, 242, 0, 235, 242, 236, 238, 0, 0, 244,
+ 244, 244, 239, 0, 244, 0, 237, 0, 240, 0,
+ 245, 245, 245, 0, 241, 245, 0, 0, 0, 240,
+ 0, 242, 246, 246, 246, 0, 0, 246, 0, 0,
+
+ 241, 244, 0, 247, 247, 247, 0, 0, 247, 0,
+ 0, 242, 245, 247, 248, 248, 248, 0, 245, 248,
+ 249, 249, 249, 0, 246, 249, 250, 250, 250, 0,
+ 0, 250, 251, 251, 251, 247, 0, 251, 252, 252,
+ 252, 0, 0, 252, 0, 0, 248, 0, 252, 253,
+ 253, 253, 249, 0, 253, 256, 256, 256, 250, 0,
+ 256, 0, 0, 249, 251, 0, 0, 0, 248, 0,
+ 252, 0, 257, 257, 257, 0, 0, 257, 258, 258,
+ 258, 253, 0, 258, 259, 259, 259, 256, 0, 259,
+ 260, 260, 260, 0, 0, 260, 262, 262, 262, 0,
+
+ 0, 262, 0, 256, 257, 0, 0, 263, 263, 263,
+ 258, 257, 263, 264, 264, 264, 259, 0, 264, 0,
+ 0, 0, 260, 0, 266, 266, 266, 0, 262, 266,
+ 0, 0, 262, 268, 268, 268, 0, 260, 268, 263,
+ 259, 0, 260, 260, 0, 264, 269, 269, 269, 0,
+ 0, 269, 264, 0, 263, 0, 266, 0, 0, 266,
+ 0, 270, 270, 270, 0, 268, 270, 273, 273, 273,
+ 0, 0, 273, 0, 275, 275, 275, 0, 269, 275,
+ 0, 276, 276, 276, 268, 269, 276, 278, 278, 278,
+ 0, 0, 278, 270, 0, 0, 0, 270, 0, 273,
+
+ 279, 279, 279, 0, 0, 279, 275, 280, 280, 280,
+ 0, 0, 280, 276, 0, 281, 281, 281, 273, 278,
+ 281, 0, 0, 275, 0, 282, 282, 282, 0, 0,
+ 282, 276, 279, 0, 0, 279, 283, 283, 283, 280,
+ 0, 283, 0, 284, 284, 284, 0, 281, 284, 285,
+ 285, 285, 0, 284, 285, 0, 0, 282, 0, 0,
+ 0, 0, 0, 280, 282, 281, 0, 0, 283, 0,
+ 0, 286, 286, 286, 0, 284, 286, 287, 287, 287,
+ 0, 285, 287, 0, 288, 288, 288, 0, 283, 288,
+ 0, 289, 289, 289, 0, 0, 289, 290, 290, 290,
+
+ 0, 285, 290, 286, 0, 291, 291, 291, 0, 287,
+ 291, 0, 293, 293, 293, 0, 288, 293, 0, 286,
+ 0, 0, 287, 289, 0, 0, 0, 0, 0, 290,
+ 289, 294, 294, 294, 288, 0, 294, 291, 295, 295,
+ 295, 0, 0, 295, 293, 296, 296, 296, 0, 290,
+ 296, 0, 297, 297, 297, 296, 291, 297, 293, 0,
+ 298, 298, 298, 294, 0, 298, 299, 299, 299, 0,
+ 295, 299, 0, 0, 300, 300, 300, 296, 0, 300,
+ 294, 0, 0, 295, 297, 301, 301, 301, 0, 0,
+ 301, 297, 298, 0, 302, 302, 302, 298, 299, 302,
+
+ 0, 0, 303, 303, 303, 299, 300, 303, 304, 304,
+ 304, 300, 0, 304, 305, 305, 305, 301, 0, 305,
+ 0, 0, 306, 306, 306, 0, 302, 306, 0, 302,
+ 307, 307, 307, 0, 303, 307, 308, 308, 308, 0,
+ 304, 308, 309, 309, 309, 0, 305, 309, 310, 310,
+ 310, 304, 303, 310, 306, 311, 311, 311, 0, 0,
+ 311, 306, 307, 0, 0, 0, 0, 0, 308, 307,
+ 312, 312, 312, 0, 309, 312, 313, 313, 313, 309,
+ 310, 313, 0, 315, 315, 315, 0, 311, 315, 316,
+ 316, 316, 0, 0, 316, 0, 0, 311, 0, 317,
+
+ 317, 317, 312, 310, 317, 319, 319, 319, 313, 0,
+ 319, 0, 320, 320, 320, 315, 0, 320, 0, 0,
+ 313, 316, 322, 322, 322, 0, 0, 322, 0, 0,
+ 0, 317, 0, 315, 323, 323, 323, 319, 0, 323,
+ 324, 324, 324, 316, 320, 324, 0, 317, 326, 326,
+ 326, 0, 0, 326, 322, 0, 327, 327, 327, 0,
+ 0, 327, 0, 0, 322, 0, 323, 0, 328, 328,
+ 328, 0, 324, 328, 0, 324, 329, 329, 329, 0,
+ 326, 329, 0, 0, 0, 0, 323, 326, 327, 332,
+ 332, 332, 0, 0, 332, 0, 333, 333, 333, 327,
+
+ 328, 333, 0, 0, 334, 334, 334, 328, 329, 334,
+ 335, 335, 335, 0, 0, 335, 0, 336, 336, 336,
+ 0, 332, 336, 329, 332, 337, 337, 337, 333, 0,
+ 337, 0, 338, 338, 338, 0, 334, 338, 339, 339,
+ 339, 0, 335, 339, 0, 341, 341, 341, 0, 336,
+ 341, 334, 0, 0, 0, 0, 336, 337, 342, 342,
+ 342, 0, 335, 342, 338, 0, 343, 343, 343, 0,
+ 339, 343, 0, 0, 0, 337, 0, 341, 0, 344,
+ 344, 344, 0, 338, 344, 339, 341, 345, 345, 345,
+ 342, 0, 345, 0, 346, 346, 346, 0, 343, 346,
+
+ 347, 347, 347, 0, 0, 347, 0, 0, 349, 349,
+ 349, 344, 0, 349, 0, 0, 0, 343, 0, 345,
+ 350, 350, 350, 0, 0, 350, 346, 0, 0, 0,
+ 344, 0, 347, 0, 0, 346, 352, 352, 352, 347,
+ 349, 352, 353, 353, 353, 0, 0, 353, 356, 356,
+ 356, 0, 350, 356, 357, 357, 357, 0, 0, 357,
+ 358, 358, 358, 0, 0, 358, 0, 0, 352, 0,
+ 0, 0, 0, 0, 353, 352, 0, 0, 0, 0,
+ 356, 0, 0, 0, 0, 0, 357, 0, 0, 0,
+ 0, 0, 358, 353, 0, 0, 0, 0, 0, 356,
+
+ 362, 362, 362, 362, 363, 363, 363, 363, 364, 0,
+ 364, 364, 365, 0, 0, 365, 366, 366, 366, 366,
+ 367, 367, 367, 367, 368, 0, 368, 368, 369, 369,
+ 370, 370, 370, 370, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361, 361, 361, 361, 361, 361,
+ 361, 361, 361, 361, 361
+
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+extern int yy_flex_debug;
+int yy_flex_debug = 1;
+
+static const flex_int16_t yy_rule_linenum[63] =
+ { 0,
+ 134, 136, 138, 143, 144, 149, 150, 151, 163, 166,
+ 171, 178, 187, 199, 211, 221, 231, 241, 250, 259,
+ 268, 277, 286, 295, 304, 313, 322, 331, 340, 349,
+ 358, 367, 376, 385, 394, 403, 412, 421, 430, 439,
+ 448, 457, 466, 475, 576, 592, 641, 649, 664, 665,
+ 666, 667, 668, 669, 671, 689, 702, 707, 711, 713,
+ 715, 717
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "netconf_lexer.ll"
+/* Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#line 8 "netconf_lexer.ll"
+
+/* Generated files do not make clang static analyser so happy */
+#ifndef __clang_analyzer__
+
+#include <cctype>
+#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <string>
+#include <netconf/parser_context.h>
+#include <asiolink/io_address.h>
+#include <boost/lexical_cast.hpp>
+#include <exceptions/exceptions.h>
+#include <cc/dhcp_config_error.h>
+
+/* Please avoid C++ style comments (// ... eol) as they break flex 2.6.2 */
+
+/* Work around an incompatibility in flex (at least versions
+ 2.5.31 through 2.5.33): it generates code that does
+ not conform to C89. See Debian bug 333231
+ <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>. */
+# undef yywrap
+# define yywrap() 1
+
+namespace {
+
+bool start_token_flag = false;
+
+isc::netconf::ParserContext::ParserType start_token_value;
+unsigned int comment_start_line = 0;
+
+using namespace isc;
+using isc::netconf::NetconfParser;
+
+};
+
+/* To avoid the call to exit... oops! */
+#define YY_FATAL_ERROR(msg) isc::netconf::ParserContext::fatal(msg)
+#line 1505 "netconf_lexer.cc"
+/* noyywrap disables automatic rewinding for the next file to parse. Since we
+ always parse only a single string, there's no need to do any wraps. And
+ using yywrap requires linking with -lfl, which provides the default yywrap
+ implementation that always returns 1 anyway. */
+/* nounput simplifies the lexer, by removing support for putting a character
+ back into the input stream. We never use such capability anyway. */
+/* batch means that we'll never use the generated lexer interactively. */
+/* avoid to get static global variables to remain with C++. */
+/* in last resort %option reentrant */
+/* Enables debug mode. To see the debug messages, one needs to also set
+ yy_flex_debug to 1, then the debug messages will be printed on stderr. */
+/* I have no idea what this option does, except it was specified in the bison
+ examples and Postgres folks added it to remove gcc 4.3 warnings. Let's
+ be on the safe side and keep it. */
+#define YY_NO_INPUT 1
+
+/* These are not token expressions yet, just convenience expressions that
+ can be used during actual token definitions. Note some can match
+ incorrect inputs (e.g., IP addresses) which must be checked. */
+/* for errors */
+#line 97 "netconf_lexer.ll"
+/* This code run each time a pattern is matched. It updates the location
+ by moving it ahead by yyleng bytes. yyleng specifies the length of the
+ currently matched token. */
+#define YY_USER_ACTION driver.loc_.columns(yyleng);
+#line 1531 "netconf_lexer.cc"
+#line 1532 "netconf_lexer.cc"
+
+#define INITIAL 0
+#define COMMENT 1
+#define DIR_ENTER 2
+#define DIR_INCLUDE 3
+#define DIR_EXIT 4
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+/* %if-c-only */
+#include <unistd.h>
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+/* %if-c-only Reentrant structure and macros (non-C++). */
+/* %if-reentrant */
+/* %if-c-only */
+
+static int yy_init_globals ( void );
+
+/* %endif */
+/* %if-reentrant */
+/* %endif */
+/* %endif End reentrant structures and macros. */
+
+/* Accessor methods to globals.
+ These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( void );
+
+int yyget_debug ( void );
+
+void yyset_debug ( int debug_flag );
+
+YY_EXTRA_TYPE yyget_extra ( void );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined );
+
+FILE *yyget_in ( void );
+
+void yyset_in ( FILE * _in_str );
+
+FILE *yyget_out ( void );
+
+void yyset_out ( FILE * _out_str );
+
+ int yyget_leng ( void );
+
+char *yyget_text ( void );
+
+int yyget_lineno ( void );
+
+void yyset_lineno ( int _line_number );
+
+/* %if-bison-bridge */
+/* %endif */
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( void );
+#else
+extern int yywrap ( void );
+#endif
+#endif
+
+/* %not-for-header */
+#ifndef YY_NO_UNPUT
+
+#endif
+/* %ok-for-header */
+
+/* %endif */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * );
+#endif
+
+#ifndef YY_NO_INPUT
+/* %if-c-only Standard (non-C++) definition */
+/* %not-for-header */
+#ifdef __cplusplus
+static int yyinput ( void );
+#else
+static int input ( void );
+#endif
+/* %ok-for-header */
+
+/* %endif */
+#endif
+
+/* %if-c-only */
+
+/* %endif */
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* %if-c-only Standard (non-C++) definition */
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+/* %endif */
+/* %if-c++-only C++ definition */
+/* %endif */
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+/* %% [5.0] fread()/read() definition of YY_INPUT goes here unless we're doing C++ \ */\
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, yyin)) == 0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+/* %if-c++-only C++ definition \ */\
+/* %endif */
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+/* %if-c-only */
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+#endif
+
+/* %if-tables-serialization structures and prototypes */
+/* %not-for-header */
+/* %ok-for-header */
+
+/* %not-for-header */
+/* %tables-yydmap generated elements */
+/* %endif */
+/* end tables serialization structures and prototypes */
+
+/* %ok-for-header */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+/* %if-c-only Standard (non-C++) definition */
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+/* %endif */
+/* %if-c++-only C++ definition */
+/* %endif */
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+/* %% [6.0] YY_RULE_SETUP definition goes here */
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/* %not-for-header */
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ yy_state_type yy_current_state;
+ char *yy_cp, *yy_bp;
+ int yy_act;
+
+ if ( !(yy_init) )
+ {
+ (yy_init) = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! (yy_start) )
+ (yy_start) = 1; /* first start state */
+
+ if ( ! yyin )
+/* %if-c-only */
+ yyin = stdin;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+ if ( ! yyout )
+/* %if-c-only */
+ yyout = stdout;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+ }
+
+ yy_load_buffer_state( );
+ }
+
+ {
+/* %% [7.0] user's declarations go here */
+#line 103 "netconf_lexer.ll"
+
+
+
+#line 107 "netconf_lexer.ll"
+ /* This part of the code is copied over to the verbatim to the top
+ of the generated yylex function. Explanation:
+ http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html */
+
+ /* Code run each time yylex is called. */
+ driver.loc_.step();
+
+ /* We currently have 3 points of entries defined:
+ START_JSON - which expects any valid JSON
+ START_NETCONF - which expects full configuration (with outer map and Netconf
+ object in it.
+ START_SUB_NETCONF - which expects only content of the Netconf, this is
+ primarily useful for testing. */
+ if (start_token_flag) {
+ start_token_flag = false;
+ switch (start_token_value) {
+ case ParserContext::PARSER_JSON:
+ default:
+ return NetconfParser::make_START_JSON(driver.loc_);
+ case ParserContext::PARSER_NETCONF:
+ return NetconfParser::make_START_NETCONF(driver.loc_);
+ case ParserContext::PARSER_SUB_NETCONF:
+ return NetconfParser::make_START_SUB_NETCONF(driver.loc_);
+ }
+ }
+
+
+#line 1846 "netconf_lexer.cc"
+
+ while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */
+ {
+/* %% [8.0] yymore()-related code goes here */
+ yy_cp = (yy_c_buf_p);
+
+ /* Support of yytext. */
+ *yy_cp = (yy_hold_char);
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+/* %% [9.0] code to set up and find next match goes here */
+ yy_current_state = (yy_start);
+yy_match:
+ do
+ {
+ YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 362 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ ++yy_cp;
+ }
+ while ( yy_current_state != 361 );
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+
+yy_find_action:
+/* %% [10.0] code to find the action number goes here */
+ yy_act = yy_accept[yy_current_state];
+
+ YY_DO_BEFORE_ACTION;
+
+/* %% [11.0] code for yylineno update goes here */
+
+do_action: /* This label is used only to access EOF actions. */
+
+/* %% [12.0] debug code goes here */
+ if ( yy_flex_debug )
+ {
+ if ( yy_act == 0 )
+ fprintf( stderr, "--scanner backing up\n" );
+ else if ( yy_act < 63 )
+ fprintf( stderr, "--accepting rule at line %ld (\"%s\")\n",
+ (long)yy_rule_linenum[yy_act], yytext );
+ else if ( yy_act == 63 )
+ fprintf( stderr, "--accepting default rule (\"%s\")\n",
+ yytext );
+ else if ( yy_act == 64 )
+ fprintf( stderr, "--(end of buffer or a NUL)\n" );
+ else
+ fprintf( stderr, "--EOF (start condition %d)\n", YY_START );
+ }
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+/* %% [13.0] actions go here */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = (yy_hold_char);
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 134 "netconf_lexer.ll"
+;
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 136 "netconf_lexer.ll"
+;
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 138 "netconf_lexer.ll"
+{
+ BEGIN(COMMENT);
+ comment_start_line = driver.loc_.end.line;;
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 143 "netconf_lexer.ll"
+BEGIN(INITIAL);
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 144 "netconf_lexer.ll"
+;
+ YY_BREAK
+case YY_STATE_EOF(COMMENT):
+#line 145 "netconf_lexer.ll"
+{
+ isc_throw(ParseError, "Comment not closed. (/* in line " << comment_start_line);
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 149 "netconf_lexer.ll"
+BEGIN(DIR_ENTER);
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 150 "netconf_lexer.ll"
+BEGIN(DIR_INCLUDE);
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 151 "netconf_lexer.ll"
+{
+ /* Include directive. */
+
+ /* Extract the filename. */
+ std::string tmp(yytext+1);
+ tmp.resize(tmp.size() - 1);
+
+ driver.includeFile(tmp);
+}
+ YY_BREAK
+case YY_STATE_EOF(DIR_ENTER):
+case YY_STATE_EOF(DIR_INCLUDE):
+case YY_STATE_EOF(DIR_EXIT):
+#line 160 "netconf_lexer.ll"
+{
+ isc_throw(ParseError, "Directive not closed.");
+}
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 163 "netconf_lexer.ll"
+BEGIN(INITIAL);
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 166 "netconf_lexer.ll"
+{
+ /* Ok, we found a with space. Let's ignore it and update loc variable. */
+ driver.loc_.step();
+}
+ YY_BREAK
+case 11:
+/* rule 11 can match eol */
+YY_RULE_SETUP
+#line 171 "netconf_lexer.ll"
+{
+ /* Newline found. Let's update the location and continue. */
+ driver.loc_.lines(yyleng);
+ driver.loc_.step();
+}
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 178 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONFIG:
+ return NetconfParser::make_NETCONF(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("Netconf", driver.loc_);
+ }
+}
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 187 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ case ParserContext::CONTROL_SOCKET:
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_USER_CONTEXT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("user-context", driver.loc_);
+ }
+}
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 199 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ case ParserContext::CONTROL_SOCKET:
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_COMMENT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("comment", driver.loc_);
+ }
+}
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 211 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ return NetconfParser::make_BOOT_UPDATE(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("boot-update", driver.loc_);
+ }
+}
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 221 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ return NetconfParser::make_SUBSCRIBE_CHANGES(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("subscribe-changes", driver.loc_);
+ }
+}
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 231 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ return NetconfParser::make_VALIDATE_CHANGES(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("validate-changes", driver.loc_);
+ }
+}
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 241 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ return NetconfParser::make_MANAGED_SERVERS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("managed-servers", driver.loc_);
+ }
+}
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 250 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_DHCP4_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("dhcp4", driver.loc_);
+ }
+}
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 259 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_DHCP6_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("dhcp6", driver.loc_);
+ }
+}
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 268 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_D2_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("d2", driver.loc_);
+ }
+}
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 277 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_CA_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("ca", driver.loc_);
+ }
+}
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 286 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return NetconfParser::make_MODEL(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("model", driver.loc_);
+ }
+}
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 295 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return NetconfParser::make_CONTROL_SOCKET(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("control-socket", driver.loc_);
+ }
+}
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 304 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return NetconfParser::make_UNIX(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("unix", driver.loc_);
+ }
+}
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 313 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return NetconfParser::make_HTTP(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("http", driver.loc_);
+ }
+}
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 322 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return NetconfParser::make_STDOUT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("stdout", driver.loc_);
+ }
+}
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 331 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKET:
+ return NetconfParser::make_SOCKET_TYPE(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("socket-type", driver.loc_);
+ }
+}
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 340 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKET:
+ return NetconfParser::make_SOCKET_NAME(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("socket-name", driver.loc_);
+ }
+}
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 349 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKET:
+ return NetconfParser::make_SOCKET_URL(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("socket-url", driver.loc_);
+ }
+}
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 358 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ return NetconfParser::make_HOOKS_LIBRARIES(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("hooks-libraries", driver.loc_);
+ }
+}
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 367 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return NetconfParser::make_LIBRARY(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("library", driver.loc_);
+ }
+}
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 376 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return NetconfParser::make_PARAMETERS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("parameters", driver.loc_);
+ }
+}
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 385 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ return NetconfParser::make_LOGGERS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("loggers", driver.loc_);
+ }
+}
+ YY_BREAK
+case 35:
+YY_RULE_SETUP
+#line 394 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_NAME(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("name", driver.loc_);
+ }
+}
+ YY_BREAK
+case 36:
+YY_RULE_SETUP
+#line 403 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_OUTPUT_OPTIONS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("output_options", driver.loc_);
+ }
+}
+ YY_BREAK
+case 37:
+YY_RULE_SETUP
+#line 412 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_OUTPUT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("output", driver.loc_);
+ }
+}
+ YY_BREAK
+case 38:
+YY_RULE_SETUP
+#line 421 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_FLUSH(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("flush", driver.loc_);
+ }
+}
+ YY_BREAK
+case 39:
+YY_RULE_SETUP
+#line 430 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_MAXSIZE(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("maxsize", driver.loc_);
+ }
+}
+ YY_BREAK
+case 40:
+YY_RULE_SETUP
+#line 439 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_MAXVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("maxver", driver.loc_);
+ }
+}
+ YY_BREAK
+case 41:
+YY_RULE_SETUP
+#line 448 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_PATTERN(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("pattern", driver.loc_);
+ }
+}
+ YY_BREAK
+case 42:
+YY_RULE_SETUP
+#line 457 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_DEBUGLEVEL(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("debuglevel", driver.loc_);
+ }
+}
+ YY_BREAK
+case 43:
+YY_RULE_SETUP
+#line 466 "netconf_lexer.ll"
+{
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_SEVERITY(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("severity", driver.loc_);
+ }
+}
+ YY_BREAK
+case 44:
+YY_RULE_SETUP
+#line 475 "netconf_lexer.ll"
+{
+ /* A string has been matched. It contains the actual string and single quotes.
+ We need to get those quotes out of the way and just use its content, e.g.
+ for 'foo' we should get foo */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ raw.resize(len);
+ std::string decoded;
+ decoded.reserve(len);
+ for (size_t pos = 0; pos < len; ++pos) {
+ int b = 0;
+ char c = raw[pos];
+ switch (c) {
+ case '"':
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad quote in \"" + raw + "\"");
+ break;
+ case '\\':
+ ++pos;
+ if (pos >= len) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Overflow escape in \"" + raw + "\"");
+ }
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ decoded.push_back(c);
+ break;
+ case 'b':
+ decoded.push_back('\b');
+ break;
+ case 'f':
+ decoded.push_back('\f');
+ break;
+ case 'n':
+ decoded.push_back('\n');
+ break;
+ case 'r':
+ decoded.push_back('\r');
+ break;
+ case 't':
+ decoded.push_back('\t');
+ break;
+ case 'u':
+ /* support only \u0000 to \u00ff */
+ ++pos;
+ if (pos + 4 > len) {
+ /* impossible condition */
+ driver.error(driver.loc_,
+ "Overflow unicode escape in \"" + raw + "\"");
+ }
+ if ((raw[pos] != '0') || (raw[pos + 1] != '0')) {
+ driver.error(driver.loc_,
+ "Unsupported unicode escape in \"" + raw + "\"",
+ pos + 1);
+ }
+ pos += 2;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b = (c - '0') << 4;
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b = (c - 'A' + 10) << 4;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b = (c - 'a' + 10) << 4;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ pos++;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b |= c - '0';
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b |= c - 'A' + 10;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b |= c - 'a' + 10;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ decoded.push_back(static_cast<char>(b & 0xff));
+ break;
+ default:
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad escape in \"" + raw + "\"");
+ }
+ break;
+ default:
+ if ((c >= 0) && (c < 0x20)) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Invalid control in \"" + raw + "\"");
+ }
+ decoded.push_back(c);
+ }
+ }
+
+ return NetconfParser::make_STRING(decoded, driver.loc_);
+}
+ YY_BREAK
+case 45:
+/* rule 45 can match eol */
+YY_RULE_SETUP
+#line 576 "netconf_lexer.ll"
+{
+ /* Bad string with a forbidden control character inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ for (; pos < len; ++pos) {
+ char c = raw[pos];
+ if ((c >= 0) && (c < 0x20)) {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Invalid control in " + std::string(yytext),
+ pos + 1);
+}
+ YY_BREAK
+case 46:
+/* rule 46 can match eol */
+YY_RULE_SETUP
+#line 592 "netconf_lexer.ll"
+{
+ /* Bad string with a bad escape inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ bool found = false;
+ for (; pos < len; ++pos) {
+ if (found) {
+ break;
+ }
+ char c = raw[pos];
+ if (c == '\\') {
+ ++pos;
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ break;
+ case 'u':
+ if ((pos + 4 > len) ||
+ !std::isxdigit(raw[pos + 1]) ||
+ !std::isxdigit(raw[pos + 2]) ||
+ !std::isxdigit(raw[pos + 3]) ||
+ !std::isxdigit(raw[pos + 4])) {
+ found = true;
+ }
+ break;
+ default:
+ found = true;
+ break;
+ }
+ }
+ }
+ /* The rule stops on the first " including on \" so add ... in this case */
+ std::string trailer = "";
+ if (raw[len - 1] == '\\') {
+ trailer = "...";
+ }
+ driver.error(driver.loc_,
+ "Bad escape in " + std::string(yytext) + trailer,
+ pos);
+}
+ YY_BREAK
+case 47:
+YY_RULE_SETUP
+#line 641 "netconf_lexer.ll"
+{
+ /* Bad string with an open escape at the end */
+ std::string raw(yytext+1);
+ driver.error(driver.loc_,
+ "Overflow escape in " + std::string(yytext),
+ raw.size() + 1);
+}
+ YY_BREAK
+case 48:
+YY_RULE_SETUP
+#line 649 "netconf_lexer.ll"
+{
+ /* Bad string with an open unicode escape at the end */
+ std::string raw(yytext+1);
+ size_t pos = raw.size() - 1;
+ for (; pos > 0; --pos) {
+ char c = raw[pos];
+ if (c == 'u') {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Overflow unicode escape in " + std::string(yytext),
+ pos + 1);
+}
+ YY_BREAK
+case 49:
+YY_RULE_SETUP
+#line 664 "netconf_lexer.ll"
+{ return NetconfParser::make_LSQUARE_BRACKET(driver.loc_); }
+ YY_BREAK
+case 50:
+YY_RULE_SETUP
+#line 665 "netconf_lexer.ll"
+{ return NetconfParser::make_RSQUARE_BRACKET(driver.loc_); }
+ YY_BREAK
+case 51:
+YY_RULE_SETUP
+#line 666 "netconf_lexer.ll"
+{ return NetconfParser::make_LCURLY_BRACKET(driver.loc_); }
+ YY_BREAK
+case 52:
+YY_RULE_SETUP
+#line 667 "netconf_lexer.ll"
+{ return NetconfParser::make_RCURLY_BRACKET(driver.loc_); }
+ YY_BREAK
+case 53:
+YY_RULE_SETUP
+#line 668 "netconf_lexer.ll"
+{ return NetconfParser::make_COMMA(driver.loc_); }
+ YY_BREAK
+case 54:
+YY_RULE_SETUP
+#line 669 "netconf_lexer.ll"
+{ return NetconfParser::make_COLON(driver.loc_); }
+ YY_BREAK
+case 55:
+YY_RULE_SETUP
+#line 671 "netconf_lexer.ll"
+{
+ /* An integer was found. */
+ std::string tmp(yytext);
+ int64_t integer = 0;
+ try {
+ /* In substring we want to use negative values (e.g. -1).
+ In enterprise-id we need to use values up to 0xffffffff.
+ To cover both of those use cases, we need at least
+ int64_t. */
+ integer = boost::lexical_cast<int64_t>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to an integer.");
+ }
+
+ /* The parser needs the string form as double conversion is no lossless */
+ return NetconfParser::make_INTEGER(integer, driver.loc_);
+}
+ YY_BREAK
+case 56:
+YY_RULE_SETUP
+#line 689 "netconf_lexer.ll"
+{
+ /* A floating point was found. */
+ std::string tmp(yytext);
+ double fp = 0.0;
+ try {
+ fp = boost::lexical_cast<double>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to a floating point.");
+ }
+
+ return NetconfParser::make_FLOAT(fp, driver.loc_);
+}
+ YY_BREAK
+case 57:
+YY_RULE_SETUP
+#line 702 "netconf_lexer.ll"
+{
+ string tmp(yytext);
+ return NetconfParser::make_BOOLEAN(tmp == "true", driver.loc_);
+}
+ YY_BREAK
+case 58:
+YY_RULE_SETUP
+#line 707 "netconf_lexer.ll"
+{
+ return NetconfParser::make_NULL_TYPE(driver.loc_);
+}
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 711 "netconf_lexer.ll"
+driver.error (driver.loc_, "JSON true reserved keyword is lower case only");
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 713 "netconf_lexer.ll"
+driver.error (driver.loc_, "JSON false reserved keyword is lower case only");
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 715 "netconf_lexer.ll"
+driver.error (driver.loc_, "JSON null reserved keyword is lower case only");
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 717 "netconf_lexer.ll"
+driver.error (driver.loc_, "Invalid character: " + std::string(yytext));
+ YY_BREAK
+case YY_STATE_EOF(INITIAL):
+#line 719 "netconf_lexer.ll"
+{
+ if (driver.states_.empty()) {
+ return NetconfParser::make_END(driver.loc_);
+ }
+ driver.loc_ = driver.locs_.back();
+ driver.locs_.pop_back();
+ driver.file_ = driver.files_.back();
+ driver.files_.pop_back();
+ if (driver.sfile_) {
+ fclose(driver.sfile_);
+ driver.sfile_ = 0;
+ }
+ if (!driver.sfiles_.empty()) {
+ driver.sfile_ = driver.sfiles_.back();
+ driver.sfiles_.pop_back();
+ }
+ netconf__delete_buffer(YY_CURRENT_BUFFER);
+ netconf__switch_to_buffer(driver.states_.back());
+ driver.states_.pop_back();
+
+ BEGIN(DIR_EXIT);
+}
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 742 "netconf_lexer.ll"
+ECHO;
+ YY_BREAK
+#line 2741 "netconf_lexer.cc"
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = (yy_hold_char);
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+/* %if-c-only */
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++(yy_c_buf_p);
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+/* %% [14.0] code to do back-up for compressed tables and set up yy_cp goes here */
+ yy_cp = (yy_last_accepting_cpos);
+ yy_current_state = (yy_last_accepting_state);
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ (yy_did_buffer_switch_on_eof) = 0;
+
+ if ( yywrap( ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) =
+ (yytext_ptr) + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ (yy_c_buf_p) =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+ yy_current_state = yy_get_previous_state( );
+
+ yy_cp = (yy_c_buf_p);
+ yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of user's declarations */
+} /* end of yylex */
+/* %ok-for-header */
+
+/* %if-c++-only */
+/* %not-for-header */
+/* %ok-for-header */
+
+/* %endif */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+/* %if-c-only */
+static int yy_get_next_buffer (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ char *source = (yytext_ptr);
+ int number_to_move, i;
+ int ret_val;
+
+ if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1);
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+ int yy_c_buf_p_offset =
+ (int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yyrealloc( (void *) b->yy_ch_buf,
+ (yy_size_t) (b->yy_buf_size + 2) );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = NULL;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ (yy_n_chars), num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ if ( (yy_n_chars) == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+ (void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ /* "- 2" to take care of EOB's */
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+ }
+
+ (yy_n_chars) += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+ (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+/* %if-c-only */
+/* %not-for-header */
+ static yy_state_type yy_get_previous_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ yy_state_type yy_current_state;
+ char *yy_cp;
+
+/* %% [15.0] code to get the start state into yy_current_state goes here */
+ yy_current_state = (yy_start);
+
+ for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+ {
+/* %% [16.0] code to find the next state goes here */
+ YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 362 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+/* %if-c-only */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ int yy_is_jam;
+ /* %% [17.0] code to find the next state, and perhaps do backing up, goes here */
+ char *yy_cp = (yy_c_buf_p);
+
+ YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ (yy_last_accepting_state) = yy_current_state;
+ (yy_last_accepting_cpos) = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 362 )
+ yy_c = yy_meta[yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c];
+ yy_is_jam = (yy_current_state == 361);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+/* %if-c-only */
+
+/* %endif */
+#endif
+
+/* %if-c-only */
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (void)
+#else
+ static int input (void)
+#endif
+
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ int c;
+
+ *(yy_c_buf_p) = (yy_hold_char);
+
+ if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+ /* This was really a NUL. */
+ *(yy_c_buf_p) = '\0';
+
+ else
+ { /* need more input */
+ int offset = (int) ((yy_c_buf_p) - (yytext_ptr));
+ ++(yy_c_buf_p);
+
+ switch ( yy_get_next_buffer( ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin );
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap( ) )
+ return 0;
+
+ if ( ! (yy_did_buffer_switch_on_eof) )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ (yy_c_buf_p) = (yytext_ptr) + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */
+ *(yy_c_buf_p) = '\0'; /* preserve yytext */
+ (yy_hold_char) = *++(yy_c_buf_p);
+
+/* %% [19.0] update BOL and yylineno */
+
+ return c;
+}
+/* %if-c-only */
+#endif /* ifndef YY_NO_INPUT */
+/* %endif */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ *
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+/* %if-c-only */
+ void yyrestart (FILE * input_file )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+
+ if ( ! YY_CURRENT_BUFFER ){
+ yyensure_buffer_stack ();
+ YY_CURRENT_BUFFER_LVALUE =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+ }
+
+ yy_init_buffer( YY_CURRENT_BUFFER, input_file );
+ yy_load_buffer_state( );
+}
+
+/* %if-c++-only */
+/* %endif */
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ *
+ */
+/* %if-c-only */
+ void yy_switch_to_buffer (YY_BUFFER_STATE new_buffer )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * yypop_buffer_state();
+ * yypush_buffer_state(new_buffer);
+ */
+ yyensure_buffer_stack ();
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ yy_load_buffer_state( );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+
+/* %if-c-only */
+static void yy_load_buffer_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+/* %if-c-only */
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ (yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ *
+ * @return the allocated buffer state.
+ */
+/* %if-c-only */
+ YY_BUFFER_STATE yy_create_buffer (FILE * file, int size )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2) );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file );
+
+ return b;
+}
+
+/* %if-c++-only */
+/* %endif */
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ *
+ */
+/* %if-c-only */
+ void yy_delete_buffer (YY_BUFFER_STATE b )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yyfree( (void *) b->yy_ch_buf );
+
+ yyfree( (void *) b );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+/* %if-c-only */
+ static void yy_init_buffer (YY_BUFFER_STATE b, FILE * file )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+{
+ int oerrno = errno;
+
+ yy_flush_buffer( b );
+
+/* %if-c-only */
+ b->yy_input_file = file;
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then yy_init_buffer was _probably_
+ * called from yyrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+/* %if-c-only */
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ *
+ */
+/* %if-c-only */
+ void yy_flush_buffer (YY_BUFFER_STATE b )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ yy_load_buffer_state( );
+}
+
+/* %if-c-or-c++ */
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ *
+ */
+/* %if-c-only */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ if (new_buffer == NULL)
+ return;
+
+ yyensure_buffer_stack();
+
+ /* This block is copied from yy_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *(yy_c_buf_p) = (yy_hold_char);
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ (yy_buffer_stack_top)++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from yy_switch_to_buffer. */
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+}
+/* %endif */
+
+/* %if-c-or-c++ */
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ *
+ */
+/* %if-c-only */
+void yypop_buffer_state (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ yy_delete_buffer(YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if ((yy_buffer_stack_top) > 0)
+ --(yy_buffer_stack_top);
+
+ if (YY_CURRENT_BUFFER) {
+ yy_load_buffer_state( );
+ (yy_did_buffer_switch_on_eof) = 1;
+ }
+}
+/* %endif */
+
+/* %if-c-or-c++ */
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+/* %if-c-only */
+static void yyensure_buffer_stack (void)
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+{
+ yy_size_t num_to_alloc;
+
+ if (!(yy_buffer_stack)) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ (yy_buffer_stack_max) = num_to_alloc;
+ (yy_buffer_stack_top) = 0;
+ return;
+ }
+
+ if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = (yy_buffer_stack_max) + grow_size;
+ (yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+ ((yy_buffer_stack),
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ );
+ if ( ! (yy_buffer_stack) )
+ YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+ (yy_buffer_stack_max) = num_to_alloc;
+ }
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer (char * base, yy_size_t size )
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return NULL;
+
+ b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = NULL;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ *
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr )
+{
+
+ return yy_scan_bytes( yystr, (int) strlen(yystr) );
+}
+/* %endif */
+
+/* %if-c-only */
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ *
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes (const char * yybytes, int _yybytes_len )
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = (yy_size_t) (_yybytes_len + 2);
+ buf = (char *) yyalloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+/* %endif */
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+/* %if-c-only */
+static void yynoreturn yy_fatal_error (const char* msg )
+{
+ fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+/* %endif */
+/* %if-c++-only */
+/* %endif */
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = (yy_hold_char); \
+ (yy_c_buf_p) = yytext + yyless_macro_arg; \
+ (yy_hold_char) = *(yy_c_buf_p); \
+ *(yy_c_buf_p) = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/* %if-c-only */
+/* %if-reentrant */
+/* %endif */
+
+/** Get the current line number.
+ *
+ */
+int yyget_lineno (void)
+{
+
+ return yylineno;
+}
+
+/** Get the input stream.
+ *
+ */
+FILE *yyget_in (void)
+{
+ return yyin;
+}
+
+/** Get the output stream.
+ *
+ */
+FILE *yyget_out (void)
+{
+ return yyout;
+}
+
+/** Get the length of the current token.
+ *
+ */
+int yyget_leng (void)
+{
+ return yyleng;
+}
+
+/** Get the current token.
+ *
+ */
+
+char *yyget_text (void)
+{
+ return yytext;
+}
+
+/* %if-reentrant */
+/* %endif */
+
+/** Set the current line number.
+ * @param _line_number line number
+ *
+ */
+void yyset_lineno (int _line_number )
+{
+
+ yylineno = _line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ *
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE * _in_str )
+{
+ yyin = _in_str ;
+}
+
+void yyset_out (FILE * _out_str )
+{
+ yyout = _out_str ;
+}
+
+int yyget_debug (void)
+{
+ return yy_flex_debug;
+}
+
+void yyset_debug (int _bdebug )
+{
+ yy_flex_debug = _bdebug ;
+}
+
+/* %endif */
+
+/* %if-reentrant */
+/* %if-bison-bridge */
+/* %endif */
+/* %endif if-c-only */
+
+/* %if-c-only */
+static int yy_init_globals (void)
+{
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from yylex_destroy(), so don't allocate here.
+ */
+
+ (yy_buffer_stack) = NULL;
+ (yy_buffer_stack_top) = 0;
+ (yy_buffer_stack_max) = 0;
+ (yy_c_buf_p) = NULL;
+ (yy_init) = 0;
+ (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = NULL;
+ yyout = NULL;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * yylex_init()
+ */
+ return 0;
+}
+/* %endif */
+
+/* %if-c-only SNIP! this currently causes conflicts with the c++ scanner */
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy (void)
+{
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ yypop_buffer_state();
+ }
+
+ /* Destroy the stack itself. */
+ yyfree((yy_buffer_stack) );
+ (yy_buffer_stack) = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * yylex() is called, initialization will occur. */
+ yy_init_globals( );
+
+/* %if-reentrant */
+/* %endif */
+ return 0;
+}
+/* %endif */
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n )
+{
+
+ int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s )
+{
+ int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *yyalloc (yy_size_t size )
+{
+ return malloc(size);
+}
+
+void *yyrealloc (void * ptr, yy_size_t size )
+{
+
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return realloc(ptr, size);
+}
+
+void yyfree (void * ptr )
+{
+ free( (char *) ptr ); /* see yyrealloc() for (char *) cast */
+}
+
+/* %if-tables-serialization definitions */
+/* %define-yytables The name for this specific scanner's tables. */
+#define YYTABLES_NAME "yytables"
+/* %endif */
+
+/* %ok-for-header */
+
+#line 742 "netconf_lexer.ll"
+
+
+using namespace isc::dhcp;
+
+void
+ParserContext::scanStringBegin(const std::string& str, ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = "<string>";
+ sfile_ = 0;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+ buffer = netconf__scan_bytes(str.c_str(), str.size());
+ if (!buffer) {
+ fatal("cannot scan string");
+ /* fatal() throws an exception so this can't be reached */
+ }
+}
+
+void
+ParserContext::scanFileBegin(FILE * f,
+ const std::string& filename,
+ ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = filename;
+ sfile_ = f;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+
+ /* See netconf_lexer.cc header for available definitions */
+ buffer = netconf__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal("cannot scan file " + filename);
+ }
+ netconf__switch_to_buffer(buffer);
+}
+
+void
+ParserContext::scanEnd() {
+ if (sfile_)
+ fclose(sfile_);
+ sfile_ = 0;
+ static_cast<void>(netconf_lex_destroy());
+ /* Close files */
+ while (!sfiles_.empty()) {
+ FILE* f = sfiles_.back();
+ if (f) {
+ fclose(f);
+ }
+ sfiles_.pop_back();
+ }
+ /* Delete states */
+ while (!states_.empty()) {
+ netconf__delete_buffer(states_.back());
+ states_.pop_back();
+ }
+}
+
+void
+ParserContext::includeFile(const std::string& filename) {
+ if (states_.size() > 10) {
+ fatal("Too many nested include.");
+ }
+
+ FILE* f = fopen(filename.c_str(), "r");
+ if (!f) {
+ fatal("Can't open include file " + filename);
+ }
+ if (sfile_) {
+ sfiles_.push_back(sfile_);
+ }
+ sfile_ = f;
+ states_.push_back(YY_CURRENT_BUFFER);
+ YY_BUFFER_STATE buffer;
+ buffer = netconf__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal( "Can't scan include file " + filename);
+ }
+ netconf__switch_to_buffer(buffer);
+ files_.push_back(file_);
+ file_ = filename;
+ locs_.push_back(loc_);
+ loc_.initialize(&file_);
+
+ BEGIN(INITIAL);
+}
+
+namespace {
+/** To avoid unused function error */
+class Dummy {
+ /* cppcheck-suppress unusedPrivateFunction */
+ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); }
+};
+}
+#endif /* !__clang_analyzer__ */
+
diff --git a/src/bin/netconf/netconf_lexer.ll b/src/bin/netconf/netconf_lexer.ll
new file mode 100644
index 0000000..5f056a6
--- /dev/null
+++ b/src/bin/netconf/netconf_lexer.ll
@@ -0,0 +1,842 @@
+/* Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
+
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this
+ file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+%{ /* -*- C++ -*- */
+
+/* Generated files do not make clang static analyser so happy */
+#ifndef __clang_analyzer__
+
+#include <cctype>
+#include <cerrno>
+#include <climits>
+#include <cstdlib>
+#include <string>
+#include <netconf/parser_context.h>
+#include <asiolink/io_address.h>
+#include <boost/lexical_cast.hpp>
+#include <exceptions/exceptions.h>
+#include <cc/dhcp_config_error.h>
+
+/* Please avoid C++ style comments (// ... eol) as they break flex 2.6.2 */
+
+/* Work around an incompatibility in flex (at least versions
+ 2.5.31 through 2.5.33): it generates code that does
+ not conform to C89. See Debian bug 333231
+ <http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=333231>. */
+# undef yywrap
+# define yywrap() 1
+
+namespace {
+
+bool start_token_flag = false;
+
+isc::netconf::ParserContext::ParserType start_token_value;
+unsigned int comment_start_line = 0;
+
+using namespace isc;
+using isc::netconf::NetconfParser;
+
+};
+
+/* To avoid the call to exit... oops! */
+#define YY_FATAL_ERROR(msg) isc::netconf::ParserContext::fatal(msg)
+%}
+
+/* noyywrap disables automatic rewinding for the next file to parse. Since we
+ always parse only a single string, there's no need to do any wraps. And
+ using yywrap requires linking with -lfl, which provides the default yywrap
+ implementation that always returns 1 anyway. */
+%option noyywrap
+
+/* nounput simplifies the lexer, by removing support for putting a character
+ back into the input stream. We never use such capability anyway. */
+%option nounput
+
+/* batch means that we'll never use the generated lexer interactively. */
+%option batch
+
+/* avoid to get static global variables to remain with C++. */
+/* in last resort %option reentrant */
+
+/* Enables debug mode. To see the debug messages, one needs to also set
+ yy_flex_debug to 1, then the debug messages will be printed on stderr. */
+%option debug
+
+/* I have no idea what this option does, except it was specified in the bison
+ examples and Postgres folks added it to remove gcc 4.3 warnings. Let's
+ be on the safe side and keep it. */
+%option noinput
+
+%x COMMENT
+%x DIR_ENTER DIR_INCLUDE DIR_EXIT
+
+/* These are not token expressions yet, just convenience expressions that
+ can be used during actual token definitions. Note some can match
+ incorrect inputs (e.g., IP addresses) which must be checked. */
+int \-?[0-9]+
+blank [ \t\r]
+
+UnicodeEscapeSequence u[0-9A-Fa-f]{4}
+JSONEscapeCharacter ["\\/bfnrt]
+JSONEscapeSequence {JSONEscapeCharacter}|{UnicodeEscapeSequence}
+JSONStandardCharacter [^\x00-\x1f"\\]
+JSONStringCharacter {JSONStandardCharacter}|\\{JSONEscapeSequence}
+JSONString \"{JSONStringCharacter}*\"
+
+/* for errors */
+
+BadUnicodeEscapeSequence u[0-9A-Fa-f]{0,3}[^0-9A-Fa-f"]
+BadJSONEscapeSequence [^"\\/bfnrtu]|{BadUnicodeEscapeSequence}
+ControlCharacter [\x00-\x1f]
+ControlCharacterFill [^"\\]|\\["\\/bfnrtu]
+
+%{
+/* This code run each time a pattern is matched. It updates the location
+ by moving it ahead by yyleng bytes. yyleng specifies the length of the
+ currently matched token. */
+#define YY_USER_ACTION driver.loc_.columns(yyleng);
+%}
+
+%%
+
+%{
+ /* This part of the code is copied over to the verbatim to the top
+ of the generated yylex function. Explanation:
+ http://www.gnu.org/software/bison/manual/html_node/Multiple-start_002dsymbols.html */
+
+ /* Code run each time yylex is called. */
+ driver.loc_.step();
+
+ /* We currently have 3 points of entries defined:
+ START_JSON - which expects any valid JSON
+ START_NETCONF - which expects full configuration (with outer map and Netconf
+ object in it.
+ START_SUB_NETCONF - which expects only content of the Netconf, this is
+ primarily useful for testing. */
+ if (start_token_flag) {
+ start_token_flag = false;
+ switch (start_token_value) {
+ case ParserContext::PARSER_JSON:
+ default:
+ return NetconfParser::make_START_JSON(driver.loc_);
+ case ParserContext::PARSER_NETCONF:
+ return NetconfParser::make_START_NETCONF(driver.loc_);
+ case ParserContext::PARSER_SUB_NETCONF:
+ return NetconfParser::make_START_SUB_NETCONF(driver.loc_);
+ }
+ }
+%}
+
+#.* ;
+
+"//"(.*) ;
+
+"/*" {
+ BEGIN(COMMENT);
+ comment_start_line = driver.loc_.end.line;;
+}
+
+<COMMENT>"*/" BEGIN(INITIAL);
+<COMMENT>. ;
+<COMMENT><<EOF>> {
+ isc_throw(ParseError, "Comment not closed. (/* in line " << comment_start_line);
+}
+
+"<?" BEGIN(DIR_ENTER);
+<DIR_ENTER>"include" BEGIN(DIR_INCLUDE);
+<DIR_INCLUDE>\"([^\"\n])+\" {
+ /* Include directive. */
+
+ /* Extract the filename. */
+ std::string tmp(yytext+1);
+ tmp.resize(tmp.size() - 1);
+
+ driver.includeFile(tmp);
+}
+<DIR_ENTER,DIR_INCLUDE,DIR_EXIT><<EOF>> {
+ isc_throw(ParseError, "Directive not closed.");
+}
+<DIR_EXIT>"?>" BEGIN(INITIAL);
+
+
+<*>{blank}+ {
+ /* Ok, we found a with space. Let's ignore it and update loc variable. */
+ driver.loc_.step();
+}
+
+<*>[\n]+ {
+ /* Newline found. Let's update the location and continue. */
+ driver.loc_.lines(yyleng);
+ driver.loc_.step();
+}
+
+
+\"Netconf\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONFIG:
+ return NetconfParser::make_NETCONF(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("Netconf", driver.loc_);
+ }
+}
+
+\"user-context\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ case ParserContext::CONTROL_SOCKET:
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_USER_CONTEXT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("user-context", driver.loc_);
+ }
+}
+
+\"comment\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ case ParserContext::CONTROL_SOCKET:
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_COMMENT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("comment", driver.loc_);
+ }
+}
+
+\"boot-update\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ return NetconfParser::make_BOOT_UPDATE(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("boot-update", driver.loc_);
+ }
+}
+
+\"subscribe-changes\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ return NetconfParser::make_SUBSCRIBE_CHANGES(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("subscribe-changes", driver.loc_);
+ }
+}
+
+\"validate-changes\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ case ParserContext::SERVER:
+ return NetconfParser::make_VALIDATE_CHANGES(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("validate-changes", driver.loc_);
+ }
+}
+
+\"managed-servers\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ return NetconfParser::make_MANAGED_SERVERS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("managed-servers", driver.loc_);
+ }
+}
+
+\"dhcp4\" {
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_DHCP4_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("dhcp4", driver.loc_);
+ }
+}
+
+\"dhcp6\" {
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_DHCP6_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("dhcp6", driver.loc_);
+ }
+}
+
+\"d2\" {
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_D2_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("d2", driver.loc_);
+ }
+}
+
+\"ca\" {
+ switch(driver.ctx_) {
+ case ParserContext::MANAGED_SERVERS:
+ return NetconfParser::make_CA_SERVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("ca", driver.loc_);
+ }
+}
+
+\"model\" {
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return NetconfParser::make_MODEL(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("model", driver.loc_);
+ }
+}
+
+\"control-socket\" {
+ switch(driver.ctx_) {
+ case ParserContext::SERVER:
+ return NetconfParser::make_CONTROL_SOCKET(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("control-socket", driver.loc_);
+ }
+}
+
+\"unix\" {
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return NetconfParser::make_UNIX(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("unix", driver.loc_);
+ }
+}
+
+\"http\" {
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return NetconfParser::make_HTTP(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("http", driver.loc_);
+ }
+}
+
+\"stdout\" {
+ switch(driver.ctx_) {
+ case ParserContext::SOCKET_TYPE:
+ return NetconfParser::make_STDOUT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("stdout", driver.loc_);
+ }
+}
+
+\"socket-type\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKET:
+ return NetconfParser::make_SOCKET_TYPE(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("socket-type", driver.loc_);
+ }
+}
+
+\"socket-name\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKET:
+ return NetconfParser::make_SOCKET_NAME(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("socket-name", driver.loc_);
+ }
+}
+
+\"socket-url\" {
+ switch(driver.ctx_) {
+ case ParserContext::CONTROL_SOCKET:
+ return NetconfParser::make_SOCKET_URL(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("socket-url", driver.loc_);
+ }
+}
+
+\"hooks-libraries\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ return NetconfParser::make_HOOKS_LIBRARIES(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("hooks-libraries", driver.loc_);
+ }
+}
+
+\"library\" {
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return NetconfParser::make_LIBRARY(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("library", driver.loc_);
+ }
+}
+
+\"parameters\" {
+ switch(driver.ctx_) {
+ case ParserContext::HOOKS_LIBRARIES:
+ return NetconfParser::make_PARAMETERS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("parameters", driver.loc_);
+ }
+}
+
+\"loggers\" {
+ switch(driver.ctx_) {
+ case ParserContext::NETCONF:
+ return NetconfParser::make_LOGGERS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("loggers", driver.loc_);
+ }
+}
+
+\"name\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_NAME(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("name", driver.loc_);
+ }
+}
+
+\"output_options\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_OUTPUT_OPTIONS(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("output_options", driver.loc_);
+ }
+}
+
+\"output\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_OUTPUT(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("output", driver.loc_);
+ }
+}
+
+\"flush\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_FLUSH(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("flush", driver.loc_);
+ }
+}
+
+\"maxsize\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_MAXSIZE(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("maxsize", driver.loc_);
+ }
+}
+
+\"maxver\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_MAXVER(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("maxver", driver.loc_);
+ }
+}
+
+\"pattern\" {
+ switch(driver.ctx_) {
+ case ParserContext::OUTPUT_OPTIONS:
+ return NetconfParser::make_PATTERN(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("pattern", driver.loc_);
+ }
+}
+
+\"debuglevel\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_DEBUGLEVEL(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("debuglevel", driver.loc_);
+ }
+}
+
+\"severity\" {
+ switch(driver.ctx_) {
+ case ParserContext::LOGGERS:
+ return NetconfParser::make_SEVERITY(driver.loc_);
+ default:
+ return NetconfParser::make_STRING("severity", driver.loc_);
+ }
+}
+
+{JSONString} {
+ /* A string has been matched. It contains the actual string and single quotes.
+ We need to get those quotes out of the way and just use its content, e.g.
+ for 'foo' we should get foo */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ raw.resize(len);
+ std::string decoded;
+ decoded.reserve(len);
+ for (size_t pos = 0; pos < len; ++pos) {
+ int b = 0;
+ char c = raw[pos];
+ switch (c) {
+ case '"':
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad quote in \"" + raw + "\"");
+ break;
+ case '\\':
+ ++pos;
+ if (pos >= len) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Overflow escape in \"" + raw + "\"");
+ }
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ decoded.push_back(c);
+ break;
+ case 'b':
+ decoded.push_back('\b');
+ break;
+ case 'f':
+ decoded.push_back('\f');
+ break;
+ case 'n':
+ decoded.push_back('\n');
+ break;
+ case 'r':
+ decoded.push_back('\r');
+ break;
+ case 't':
+ decoded.push_back('\t');
+ break;
+ case 'u':
+ /* support only \u0000 to \u00ff */
+ ++pos;
+ if (pos + 4 > len) {
+ /* impossible condition */
+ driver.error(driver.loc_,
+ "Overflow unicode escape in \"" + raw + "\"");
+ }
+ if ((raw[pos] != '0') || (raw[pos + 1] != '0')) {
+ driver.error(driver.loc_,
+ "Unsupported unicode escape in \"" + raw + "\"",
+ pos + 1);
+ }
+ pos += 2;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b = (c - '0') << 4;
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b = (c - 'A' + 10) << 4;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b = (c - 'a' + 10) << 4;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ pos++;
+ c = raw[pos];
+ if ((c >= '0') && (c <= '9')) {
+ b |= c - '0';
+ } else if ((c >= 'A') && (c <= 'F')) {
+ b |= c - 'A' + 10;
+ } else if ((c >= 'a') && (c <= 'f')) {
+ b |= c - 'a' + 10;
+ } else {
+ /* impossible condition */
+ driver.error(driver.loc_, "Not hexadecimal in unicode escape in \"" + raw + "\"");
+ }
+ decoded.push_back(static_cast<char>(b & 0xff));
+ break;
+ default:
+ /* impossible condition */
+ driver.error(driver.loc_, "Bad escape in \"" + raw + "\"");
+ }
+ break;
+ default:
+ if ((c >= 0) && (c < 0x20)) {
+ /* impossible condition */
+ driver.error(driver.loc_, "Invalid control in \"" + raw + "\"");
+ }
+ decoded.push_back(c);
+ }
+ }
+
+ return NetconfParser::make_STRING(decoded, driver.loc_);
+}
+
+\"{JSONStringCharacter}*{ControlCharacter}{ControlCharacterFill}*\" {
+ /* Bad string with a forbidden control character inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ for (; pos < len; ++pos) {
+ char c = raw[pos];
+ if ((c >= 0) && (c < 0x20)) {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Invalid control in " + std::string(yytext),
+ pos + 1);
+}
+
+\"{JSONStringCharacter}*\\{BadJSONEscapeSequence}[^"]*\" {
+ /* Bad string with a bad escape inside */
+ std::string raw(yytext+1);
+ size_t len = raw.size() - 1;
+ size_t pos = 0;
+ bool found = false;
+ for (; pos < len; ++pos) {
+ if (found) {
+ break;
+ }
+ char c = raw[pos];
+ if (c == '\\') {
+ ++pos;
+ c = raw[pos];
+ switch (c) {
+ case '"':
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ break;
+ case 'u':
+ if ((pos + 4 > len) ||
+ !std::isxdigit(raw[pos + 1]) ||
+ !std::isxdigit(raw[pos + 2]) ||
+ !std::isxdigit(raw[pos + 3]) ||
+ !std::isxdigit(raw[pos + 4])) {
+ found = true;
+ }
+ break;
+ default:
+ found = true;
+ break;
+ }
+ }
+ }
+ /* The rule stops on the first " including on \" so add ... in this case */
+ std::string trailer = "";
+ if (raw[len - 1] == '\\') {
+ trailer = "...";
+ }
+ driver.error(driver.loc_,
+ "Bad escape in " + std::string(yytext) + trailer,
+ pos);
+}
+
+\"{JSONStringCharacter}*\\\" {
+ /* Bad string with an open escape at the end */
+ std::string raw(yytext+1);
+ driver.error(driver.loc_,
+ "Overflow escape in " + std::string(yytext),
+ raw.size() + 1);
+}
+
+\"{JSONStringCharacter}*\\u[0-9A-Fa-f]{0,3}\" {
+ /* Bad string with an open unicode escape at the end */
+ std::string raw(yytext+1);
+ size_t pos = raw.size() - 1;
+ for (; pos > 0; --pos) {
+ char c = raw[pos];
+ if (c == 'u') {
+ break;
+ }
+ }
+ driver.error(driver.loc_,
+ "Overflow unicode escape in " + std::string(yytext),
+ pos + 1);
+}
+
+"[" { return NetconfParser::make_LSQUARE_BRACKET(driver.loc_); }
+"]" { return NetconfParser::make_RSQUARE_BRACKET(driver.loc_); }
+"{" { return NetconfParser::make_LCURLY_BRACKET(driver.loc_); }
+"}" { return NetconfParser::make_RCURLY_BRACKET(driver.loc_); }
+"," { return NetconfParser::make_COMMA(driver.loc_); }
+":" { return NetconfParser::make_COLON(driver.loc_); }
+
+{int} {
+ /* An integer was found. */
+ std::string tmp(yytext);
+ int64_t integer = 0;
+ try {
+ /* In substring we want to use negative values (e.g. -1).
+ In enterprise-id we need to use values up to 0xffffffff.
+ To cover both of those use cases, we need at least
+ int64_t. */
+ integer = boost::lexical_cast<int64_t>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to an integer.");
+ }
+
+ /* The parser needs the string form as double conversion is no lossless */
+ return NetconfParser::make_INTEGER(integer, driver.loc_);
+}
+
+[-+]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)? {
+ /* A floating point was found. */
+ std::string tmp(yytext);
+ double fp = 0.0;
+ try {
+ fp = boost::lexical_cast<double>(tmp);
+ } catch (const boost::bad_lexical_cast &) {
+ driver.error(driver.loc_, "Failed to convert " + tmp + " to a floating point.");
+ }
+
+ return NetconfParser::make_FLOAT(fp, driver.loc_);
+}
+
+true|false {
+ string tmp(yytext);
+ return NetconfParser::make_BOOLEAN(tmp == "true", driver.loc_);
+}
+
+null {
+ return NetconfParser::make_NULL_TYPE(driver.loc_);
+}
+
+(?i:true) driver.error (driver.loc_, "JSON true reserved keyword is lower case only");
+
+(?i:false) driver.error (driver.loc_, "JSON false reserved keyword is lower case only");
+
+(?i:null) driver.error (driver.loc_, "JSON null reserved keyword is lower case only");
+
+<*>. driver.error (driver.loc_, "Invalid character: " + std::string(yytext));
+
+<<EOF>> {
+ if (driver.states_.empty()) {
+ return NetconfParser::make_END(driver.loc_);
+ }
+ driver.loc_ = driver.locs_.back();
+ driver.locs_.pop_back();
+ driver.file_ = driver.files_.back();
+ driver.files_.pop_back();
+ if (driver.sfile_) {
+ fclose(driver.sfile_);
+ driver.sfile_ = 0;
+ }
+ if (!driver.sfiles_.empty()) {
+ driver.sfile_ = driver.sfiles_.back();
+ driver.sfiles_.pop_back();
+ }
+ netconf__delete_buffer(YY_CURRENT_BUFFER);
+ netconf__switch_to_buffer(driver.states_.back());
+ driver.states_.pop_back();
+
+ BEGIN(DIR_EXIT);
+}
+
+%%
+
+using namespace isc::dhcp;
+
+void
+ParserContext::scanStringBegin(const std::string& str, ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = "<string>";
+ sfile_ = 0;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+ buffer = netconf__scan_bytes(str.c_str(), str.size());
+ if (!buffer) {
+ fatal("cannot scan string");
+ /* fatal() throws an exception so this can't be reached */
+ }
+}
+
+void
+ParserContext::scanFileBegin(FILE * f,
+ const std::string& filename,
+ ParserType parser_type)
+{
+ start_token_flag = true;
+ start_token_value = parser_type;
+
+ file_ = filename;
+ sfile_ = f;
+ loc_.initialize(&file_);
+ yy_flex_debug = trace_scanning_;
+ YY_BUFFER_STATE buffer;
+
+ /* See netconf_lexer.cc header for available definitions */
+ buffer = netconf__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal("cannot scan file " + filename);
+ }
+ netconf__switch_to_buffer(buffer);
+}
+
+void
+ParserContext::scanEnd() {
+ if (sfile_)
+ fclose(sfile_);
+ sfile_ = 0;
+ static_cast<void>(netconf_lex_destroy());
+ /* Close files */
+ while (!sfiles_.empty()) {
+ FILE* f = sfiles_.back();
+ if (f) {
+ fclose(f);
+ }
+ sfiles_.pop_back();
+ }
+ /* Delete states */
+ while (!states_.empty()) {
+ netconf__delete_buffer(states_.back());
+ states_.pop_back();
+ }
+}
+
+void
+ParserContext::includeFile(const std::string& filename) {
+ if (states_.size() > 10) {
+ fatal("Too many nested include.");
+ }
+
+ FILE* f = fopen(filename.c_str(), "r");
+ if (!f) {
+ fatal("Can't open include file " + filename);
+ }
+ if (sfile_) {
+ sfiles_.push_back(sfile_);
+ }
+ sfile_ = f;
+ states_.push_back(YY_CURRENT_BUFFER);
+ YY_BUFFER_STATE buffer;
+ buffer = netconf__create_buffer(f, 65536 /*buffer size*/);
+ if (!buffer) {
+ fatal( "Can't scan include file " + filename);
+ }
+ netconf__switch_to_buffer(buffer);
+ files_.push_back(file_);
+ file_ = filename;
+ locs_.push_back(loc_);
+ loc_.initialize(&file_);
+
+ BEGIN(INITIAL);
+}
+
+namespace {
+/** To avoid unused function error */
+class Dummy {
+ /* cppcheck-suppress unusedPrivateFunction */
+ void dummy() { yy_fatal_error("Fix me: how to disable its definition?"); }
+};
+}
+#endif /* !__clang_analyzer__ */
diff --git a/src/bin/netconf/netconf_log.cc b/src/bin/netconf/netconf_log.cc
new file mode 100644
index 0000000..d44463e
--- /dev/null
+++ b/src/bin/netconf/netconf_log.cc
@@ -0,0 +1,28 @@
+// 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 netconf_log.cc
+/// Contains the loggers used by the netconf agent.
+
+#include <config.h>
+
+#include <netconf/netconf_log.h>
+
+namespace isc {
+namespace netconf {
+
+extern const int NETCONF_DBG_TRACE = isc::log::DBGLVL_TRACE_BASIC;
+extern const int NETCONF_DBG_RESULTS = isc::log::DBGLVL_TRACE_BASIC_DATA;
+extern const int NETCONF_DBG_TRACE_DETAIL_DATA =
+ isc::log::DBGLVL_TRACE_DETAIL_DATA;
+
+const char* NETCONF_LOGGER_NAME = "netconf";
+
+isc::log::Logger netconf_logger(NETCONF_LOGGER_NAME);
+
+} // namespace netconf
+} // namespace isc
+
diff --git a/src/bin/netconf/netconf_log.h b/src/bin/netconf/netconf_log.h
new file mode 100644
index 0000000..72a51d0
--- /dev/null
+++ b/src/bin/netconf/netconf_log.h
@@ -0,0 +1,54 @@
+// 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 netconf_log.h
+/// Contains declarations for loggers used by the Kea netconf agent.
+
+#ifndef NETCONF_LOG_H
+#define NETCONF_LOG_H
+
+#include <log/logger_support.h>
+#include <log/macros.h>
+#include <netconf/netconf_messages.h>
+
+namespace isc {
+namespace netconf {
+
+///@{
+/// \brief Netconf agent logging levels.
+///
+/// Defines the levels used to output debug messages in the Netconf agent.
+/// Note that higher numbers equate to more verbose (and detailed) output.
+
+/// @brief Traces normal operations.
+///
+/// E.g. sending a command to a server etc.
+extern const int NETCONF_DBG_TRACE;
+
+/// @brief Records the results of the commands.
+///
+/// Using the example of tracing commands to a server, this will just record
+/// the summary results.
+extern const int NETCONF_DBG_RESULTS;
+
+/// @brief Additional information.
+///
+/// Record detailed tracing. This is generally reserved for tracing
+/// configurations from or to a server.
+extern const int NETCONF_DBG_TRACE_DETAIL_DATA;
+
+///@}
+
+/// @brief Defines the name of the root level (default) logger.
+extern const char* NETCONF_LOGGER_NAME;
+
+/// @brief Base logger for the netconf agent
+extern isc::log::Logger netconf_logger;
+
+} // namespace netconf
+} // namespace isc
+
+#endif // NETCONF_LOG_H
diff --git a/src/bin/netconf/netconf_messages.cc b/src/bin/netconf/netconf_messages.cc
new file mode 100644
index 0000000..883e2ee
--- /dev/null
+++ b/src/bin/netconf/netconf_messages.cc
@@ -0,0 +1,91 @@
+// File created from ../../../src/bin/netconf/netconf_messages.mes
+
+#include <cstddef>
+#include <log/message_types.h>
+#include <log/message_initializer.h>
+
+namespace isc {
+namespace netconf {
+
+extern const isc::log::MessageID NETCONF_BOOT_UPDATE_COMPLETED = "NETCONF_BOOT_UPDATE_COMPLETED";
+extern const isc::log::MessageID NETCONF_CONFIG_CHANGED_DETAIL = "NETCONF_CONFIG_CHANGED_DETAIL";
+extern const isc::log::MessageID NETCONF_CONFIG_CHANGE_EVENT = "NETCONF_CONFIG_CHANGE_EVENT";
+extern const isc::log::MessageID NETCONF_CONFIG_CHECK_FAIL = "NETCONF_CONFIG_CHECK_FAIL";
+extern const isc::log::MessageID NETCONF_CONFIG_FAIL = "NETCONF_CONFIG_FAIL";
+extern const isc::log::MessageID NETCONF_CONFIG_SYNTAX_WARNING = "NETCONF_CONFIG_SYNTAX_WARNING";
+extern const isc::log::MessageID NETCONF_FAILED = "NETCONF_FAILED";
+extern const isc::log::MessageID NETCONF_GET_CONFIG = "NETCONF_GET_CONFIG";
+extern const isc::log::MessageID NETCONF_GET_CONFIG_FAILED = "NETCONF_GET_CONFIG_FAILED";
+extern const isc::log::MessageID NETCONF_GET_CONFIG_STARTED = "NETCONF_GET_CONFIG_STARTED";
+extern const isc::log::MessageID NETCONF_LOG_CHANGE_FAIL = "NETCONF_LOG_CHANGE_FAIL";
+extern const isc::log::MessageID NETCONF_MODULE_MISSING_ERR = "NETCONF_MODULE_MISSING_ERR";
+extern const isc::log::MessageID NETCONF_MODULE_MISSING_WARN = "NETCONF_MODULE_MISSING_WARN";
+extern const isc::log::MessageID NETCONF_MODULE_REVISION_ERR = "NETCONF_MODULE_REVISION_ERR";
+extern const isc::log::MessageID NETCONF_MODULE_REVISION_WARN = "NETCONF_MODULE_REVISION_WARN";
+extern const isc::log::MessageID NETCONF_NOTIFICATION_RECEIVED = "NETCONF_NOTIFICATION_RECEIVED";
+extern const isc::log::MessageID NETCONF_RUN_EXIT = "NETCONF_RUN_EXIT";
+extern const isc::log::MessageID NETCONF_SET_CONFIG = "NETCONF_SET_CONFIG";
+extern const isc::log::MessageID NETCONF_SET_CONFIG_FAILED = "NETCONF_SET_CONFIG_FAILED";
+extern const isc::log::MessageID NETCONF_SET_CONFIG_STARTED = "NETCONF_SET_CONFIG_STARTED";
+extern const isc::log::MessageID NETCONF_STARTED = "NETCONF_STARTED";
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG = "NETCONF_SUBSCRIBE_CONFIG";
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG_FAILED = "NETCONF_SUBSCRIBE_CONFIG_FAILED";
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_NOTIFICATIONS = "NETCONF_SUBSCRIBE_NOTIFICATIONS";
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED = "NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED";
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG = "NETCONF_UPDATE_CONFIG";
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG_COMPLETED = "NETCONF_UPDATE_CONFIG_COMPLETED";
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG_FAILED = "NETCONF_UPDATE_CONFIG_FAILED";
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG_STARTED = "NETCONF_UPDATE_CONFIG_STARTED";
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG = "NETCONF_VALIDATE_CONFIG";
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_COMPLETED = "NETCONF_VALIDATE_CONFIG_COMPLETED";
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_FAILED = "NETCONF_VALIDATE_CONFIG_FAILED";
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_REJECTED = "NETCONF_VALIDATE_CONFIG_REJECTED";
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_STARTED = "NETCONF_VALIDATE_CONFIG_STARTED";
+
+} // namespace netconf
+} // namespace isc
+
+namespace {
+
+const char* values[] = {
+ "NETCONF_BOOT_UPDATE_COMPLETED", "Boot-update configuration completed for server %1",
+ "NETCONF_CONFIG_CHANGED_DETAIL", "YANG configuration changed: %1",
+ "NETCONF_CONFIG_CHANGE_EVENT", "Received YANG configuration change %1 event",
+ "NETCONF_CONFIG_CHECK_FAIL", "Netconf configuration check failed: %1",
+ "NETCONF_CONFIG_FAIL", "Netconf configuration failed: %1",
+ "NETCONF_CONFIG_SYNTAX_WARNING", "Netconf configuration syntax warning: %1",
+ "NETCONF_FAILED", "application experienced a fatal error: %1",
+ "NETCONF_GET_CONFIG", "got configuration from %1 server: %2",
+ "NETCONF_GET_CONFIG_FAILED", "getting configuration from %1 server failed: %2",
+ "NETCONF_GET_CONFIG_STARTED", "getting configuration from %1 server",
+ "NETCONF_LOG_CHANGE_FAIL", "Netconf configuration change logging failed: %1",
+ "NETCONF_MODULE_MISSING_ERR", "Missing essential module %1 in sysrepo",
+ "NETCONF_MODULE_MISSING_WARN", "Missing module %1 in sysrepo",
+ "NETCONF_MODULE_REVISION_ERR", "Essential module %1 does NOT have the right revision: expected %2, got %3",
+ "NETCONF_MODULE_REVISION_WARN", "Module %1 does NOT have the right revision: expected %2, got %3",
+ "NETCONF_NOTIFICATION_RECEIVED", "Received notification of type %1 for module %1: %2",
+ "NETCONF_RUN_EXIT", "application is exiting the event loop",
+ "NETCONF_SET_CONFIG", "set configuration to %1 server: %2",
+ "NETCONF_SET_CONFIG_FAILED", "setting configuration to %1 server failed: %2",
+ "NETCONF_SET_CONFIG_STARTED", "setting configuration to %1 server",
+ "NETCONF_STARTED", "Netconf (version %1) started",
+ "NETCONF_SUBSCRIBE_CONFIG", "subscribing configuration changes for %1 server with %2 module",
+ "NETCONF_SUBSCRIBE_CONFIG_FAILED", "subscribe configuration changes for %1 server with %2 module failed: %3",
+ "NETCONF_SUBSCRIBE_NOTIFICATIONS", "subscribing to notifications for %1 server with %2 module",
+ "NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED", "subscribing to notifications for %1 server with %2 module failed: %3",
+ "NETCONF_UPDATE_CONFIG", "updating configuration with %1 server: %2",
+ "NETCONF_UPDATE_CONFIG_COMPLETED", "completed updating configuration for %1 server",
+ "NETCONF_UPDATE_CONFIG_FAILED", "updating configuration with %1 server: %2",
+ "NETCONF_UPDATE_CONFIG_STARTED", "started updating configuration for %1 server",
+ "NETCONF_VALIDATE_CONFIG", "validating configuration with %1 server: %2",
+ "NETCONF_VALIDATE_CONFIG_COMPLETED", "completed validating configuration for %1 server",
+ "NETCONF_VALIDATE_CONFIG_FAILED", "validating configuration with %1 server got an error: %2",
+ "NETCONF_VALIDATE_CONFIG_REJECTED", "validating configuration with %1 server was rejected: %2",
+ "NETCONF_VALIDATE_CONFIG_STARTED", "started validating configuration for %1 server",
+ NULL
+};
+
+const isc::log::MessageInitializer initializer(values);
+
+} // Anonymous namespace
+
diff --git a/src/bin/netconf/netconf_messages.h b/src/bin/netconf/netconf_messages.h
new file mode 100644
index 0000000..df290bb
--- /dev/null
+++ b/src/bin/netconf/netconf_messages.h
@@ -0,0 +1,49 @@
+// File created from ../../../src/bin/netconf/netconf_messages.mes
+
+#ifndef NETCONF_MESSAGES_H
+#define NETCONF_MESSAGES_H
+
+#include <log/message_types.h>
+
+namespace isc {
+namespace netconf {
+
+extern const isc::log::MessageID NETCONF_BOOT_UPDATE_COMPLETED;
+extern const isc::log::MessageID NETCONF_CONFIG_CHANGED_DETAIL;
+extern const isc::log::MessageID NETCONF_CONFIG_CHANGE_EVENT;
+extern const isc::log::MessageID NETCONF_CONFIG_CHECK_FAIL;
+extern const isc::log::MessageID NETCONF_CONFIG_FAIL;
+extern const isc::log::MessageID NETCONF_CONFIG_SYNTAX_WARNING;
+extern const isc::log::MessageID NETCONF_FAILED;
+extern const isc::log::MessageID NETCONF_GET_CONFIG;
+extern const isc::log::MessageID NETCONF_GET_CONFIG_FAILED;
+extern const isc::log::MessageID NETCONF_GET_CONFIG_STARTED;
+extern const isc::log::MessageID NETCONF_LOG_CHANGE_FAIL;
+extern const isc::log::MessageID NETCONF_MODULE_MISSING_ERR;
+extern const isc::log::MessageID NETCONF_MODULE_MISSING_WARN;
+extern const isc::log::MessageID NETCONF_MODULE_REVISION_ERR;
+extern const isc::log::MessageID NETCONF_MODULE_REVISION_WARN;
+extern const isc::log::MessageID NETCONF_NOTIFICATION_RECEIVED;
+extern const isc::log::MessageID NETCONF_RUN_EXIT;
+extern const isc::log::MessageID NETCONF_SET_CONFIG;
+extern const isc::log::MessageID NETCONF_SET_CONFIG_FAILED;
+extern const isc::log::MessageID NETCONF_SET_CONFIG_STARTED;
+extern const isc::log::MessageID NETCONF_STARTED;
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG;
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_CONFIG_FAILED;
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_NOTIFICATIONS;
+extern const isc::log::MessageID NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED;
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG;
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG_COMPLETED;
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG_FAILED;
+extern const isc::log::MessageID NETCONF_UPDATE_CONFIG_STARTED;
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG;
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_COMPLETED;
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_FAILED;
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_REJECTED;
+extern const isc::log::MessageID NETCONF_VALIDATE_CONFIG_STARTED;
+
+} // namespace netconf
+} // namespace isc
+
+#endif // NETCONF_MESSAGES_H
diff --git a/src/bin/netconf/netconf_messages.mes b/src/bin/netconf/netconf_messages.mes
new file mode 100644
index 0000000..7883c1d
--- /dev/null
+++ b/src/bin/netconf/netconf_messages.mes
@@ -0,0 +1,173 @@
+# Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+$NAMESPACE isc::netconf
+
+% NETCONF_BOOT_UPDATE_COMPLETED Boot-update configuration completed for server %1
+This informational message is issued when the initial configuration
+was retrieved from Netconf and successfully applied to Kea server.
+
+% NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: %1
+This debug message indicates a YANG configuration change. The format
+is the change operation (created, modified, deleted or moved) followed
+by xpaths and values of old and new nodes.
+
+% NETCONF_CONFIG_CHANGE_EVENT Received YANG configuration change %1 event
+This informational message is issued when Netconf receives a YANG
+configuration change event. The type of event is printed.
+
+% NETCONF_CONFIG_CHECK_FAIL Netconf configuration check failed: %1
+This error message indicates that Netconf had failed configuration
+check. Details are provided. Additional details may be available
+in earlier log entries, possibly on lower levels.
+
+% NETCONF_CONFIG_FAIL Netconf configuration failed: %1
+This error message indicates that Netconf had failed configuration
+attempt. Details are provided. Additional details may be available
+in earlier log entries, possibly on lower levels.
+
+% NETCONF_CONFIG_SYNTAX_WARNING Netconf configuration syntax warning: %1
+This warning message indicates that the Netconf configuration had a minor
+syntax error. The error was displayed and the configuration parsing resumed.
+
+% NETCONF_FAILED application experienced a fatal error: %1
+This is a fatal error message issued when the Netconf application
+got an unrecoverable error from within the event loop.
+
+% NETCONF_GET_CONFIG got configuration from %1 server: %2
+This debug message indicates that Netconf got the configuration from a
+Kea server. The server name and the retrieved configuration are printed.
+
+% NETCONF_GET_CONFIG_FAILED getting configuration from %1 server failed: %2
+The error message indicates that Netconf got an error getting the
+configuration from a Kea server. Make sure that the server is up and
+running, has appropriate control socket defined and that the controls
+socket configuration on the server matches that of kea-netconf. The
+name of the server and the error are printed.
+
+% NETCONF_GET_CONFIG_STARTED getting configuration from %1 server
+This informational message indicates that Netconf is trying to get the
+configuration from a Kea server.
+
+% NETCONF_LOG_CHANGE_FAIL Netconf configuration change logging failed: %1
+The warning message indicates that the configuration change logging
+encountered an unexpected condition. Details of it will be logged.
+
+% NETCONF_MODULE_MISSING_ERR Missing essential module %1 in sysrepo
+This fatal error message indicates that a module required by Netconf
+configuration is not available in the sysrepo repository. The name of
+the module is printed.
+
+% NETCONF_MODULE_MISSING_WARN Missing module %1 in sysrepo
+This warning message indicates that a module used by Kea is not
+available in the sysrepo repository. The name of the module is printed.
+
+% NETCONF_MODULE_REVISION_ERR Essential module %1 does NOT have the right revision: expected %2, got %3
+This fatal error message indicates that a module required by Netconf
+configuration is not at the right revision in the sysrepo repository.
+The name, expected and available revisions of the module are printed.
+
+% NETCONF_MODULE_REVISION_WARN Module %1 does NOT have the right revision: expected %2, got %3
+This warning message indicates that a module used by Kea is not at the
+right revision in the sysrepo repository. The name, expected and
+available revisions of the module are printed.
+
+% NETCONF_NOTIFICATION_RECEIVED Received notification of type %1 for module %1: %2
+This informational message logs any YANG notification that has been signaled
+by the server, sent to kea-netconf which then was forwarded to subscribed
+clients. To achieve this, kea-netconf subscribes itself as a client to all
+notifications for the configured module.
+
+% NETCONF_RUN_EXIT application is exiting the event loop
+This is a debug message issued when the Netconf application exits its
+event loop. This is a normal step during kea-netconf shutdown.
+
+% NETCONF_SET_CONFIG set configuration to %1 server: %2
+This debug message indicates that Netconf set the configuration to a
+Kea server. The server name and the applied configuration are printed.
+
+% NETCONF_SET_CONFIG_FAILED setting configuration to %1 server failed: %2
+The error message indicates that Netconf got an error setting the
+configuration to a Kea server. Make sure that the server is up and
+running, has appropriate control socket defined and that the controls
+socket configuration on the server matches that of kea-netconf. The
+name of the server and the error are printed.
+
+% NETCONF_SET_CONFIG_STARTED setting configuration to %1 server
+This informational message indicates that Netconf is trying to set the
+configuration to a Kea server.
+
+% NETCONF_STARTED Netconf (version %1) started
+This informational message indicates that Netconf has processed
+all configuration information and is ready to begin processing.
+The version is also printed.
+
+% NETCONF_SUBSCRIBE_CONFIG subscribing configuration changes for %1 server with %2 module
+This information message indicates that Netconf is trying to subscribe
+configuration changes for a Kea server. The names of the server and
+the module are printed.
+
+% NETCONF_SUBSCRIBE_CONFIG_FAILED subscribe configuration changes for %1 server with %2 module failed: %3
+The error message indicates that Netconf got an error subscribing
+configuration changes for a Kea server. The names of the server and
+the module, and the error are printed.
+
+% NETCONF_SUBSCRIBE_NOTIFICATIONS subscribing to notifications for %1 server with %2 module
+This information message indicates that Netconf is trying to subscribe to
+notifications for a Kea server. The server name and module name are printed.
+
+% NETCONF_SUBSCRIBE_NOTIFICATIONS_FAILED subscribing to notifications for %1 server with %2 module failed: %3
+The error message indicates that Netconf got an error subscribing to
+notifications for a Kea server. The server name, module name and the error are
+printed.
+
+% NETCONF_UPDATE_CONFIG updating configuration with %1 server: %2
+This debug message indicates that Netconf update the configuration
+of a Kea server. The server name and the updated configuration are
+printed.
+
+% NETCONF_UPDATE_CONFIG_COMPLETED completed updating configuration for %1 server
+This informational message indicates that Netconf updated with success the
+configuration of a Kea server.
+
+% NETCONF_UPDATE_CONFIG_FAILED updating configuration with %1 server: %2
+The error message indicates that Netconf got an error updating the
+configuration of a Kea server. This includes a configuration rejected
+by a Kea server when it tried to apply it. The name of the server and
+the error are printed.
+
+% NETCONF_UPDATE_CONFIG_STARTED started updating configuration for %1 server
+This informational message indicates that Netconf is trying to update the
+configuration of a Kea server.
+
+% NETCONF_VALIDATE_CONFIG validating configuration with %1 server: %2
+This debug message indicates that Netconf is validating the configuration
+with a Kea server. The server name and the validated configuration are
+printed.
+
+% NETCONF_VALIDATE_CONFIG_COMPLETED completed validating configuration for %1 server
+This informational message indicates that Netconf validated with success the
+configuration with a Kea server.
+
+% NETCONF_VALIDATE_CONFIG_FAILED validating configuration with %1 server got an error: %2
+The error message indicates that Netconf got an error validating the
+configuration with a Kea server. This message is produced when
+exception is thrown during an attempt to validate received
+configuration. Additional explanation may be provided as a
+parameter. You may also take a look at earlier log messages. The name
+of the server and the error are printed.
+
+% NETCONF_VALIDATE_CONFIG_REJECTED validating configuration with %1 server was rejected: %2
+The warning message indicates that Netconf got an error validating the
+configuration with a Kea server. This message is printed when the
+configuration was rejected during normal processing. Additional
+explanation may be provided as a parameter. You may also take a look
+at earlier log messages. The name of the server and the error are
+printed.
+
+% NETCONF_VALIDATE_CONFIG_STARTED started validating configuration for %1 server
+This informational message indicates that Netconf is trying to validate the
+configuration with a Kea server.
diff --git a/src/bin/netconf/netconf_parser.cc b/src/bin/netconf/netconf_parser.cc
new file mode 100644
index 0000000..8fe964d
--- /dev/null
+++ b/src/bin/netconf/netconf_parser.cc
@@ -0,0 +1,2312 @@
+// A Bison parser, made by GNU Bison 3.8.2.
+
+// Skeleton implementation for Bison LALR(1) parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton. Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+// especially those whose name start with YY_ or yy_. They are
+// private implementation details that can be changed or removed.
+
+
+// Take the name prefix into account.
+#define yylex netconf_lex
+
+
+
+#include "netconf_parser.h"
+
+
+// Unqualified %code blocks.
+#line 33 "netconf_parser.yy"
+
+#include <netconf/parser_context.h>
+
+#line 52 "netconf_parser.cc"
+
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+# if ENABLE_NLS
+# include <libintl.h> // FIXME: INFRINGES ON USER NAME SPACE.
+# define YY_(msgid) dgettext ("bison-runtime", msgid)
+# endif
+# endif
+# ifndef YY_
+# define YY_(msgid) msgid
+# endif
+#endif
+
+
+// Whether we are compiled with exception support.
+#ifndef YY_EXCEPTIONS
+# if defined __GNUC__ && !defined __EXCEPTIONS
+# define YY_EXCEPTIONS 0
+# else
+# define YY_EXCEPTIONS 1
+# endif
+#endif
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K].location)
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+# ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).begin = YYRHSLOC (Rhs, 1).begin; \
+ (Current).end = YYRHSLOC (Rhs, N).end; \
+ } \
+ else \
+ { \
+ (Current).begin = (Current).end = YYRHSLOC (Rhs, 0).end; \
+ } \
+ while (false)
+# endif
+
+
+// Enable debugging if requested.
+#if NETCONF_DEBUG
+
+// A pseudo ostream that takes yydebug_ into account.
+# define YYCDEBUG if (yydebug_) (*yycdebug_)
+
+# define YY_SYMBOL_PRINT(Title, Symbol) \
+ do { \
+ if (yydebug_) \
+ { \
+ *yycdebug_ << Title << ' '; \
+ yy_print_ (*yycdebug_, Symbol); \
+ *yycdebug_ << '\n'; \
+ } \
+ } while (false)
+
+# define YY_REDUCE_PRINT(Rule) \
+ do { \
+ if (yydebug_) \
+ yy_reduce_print_ (Rule); \
+ } while (false)
+
+# define YY_STACK_PRINT() \
+ do { \
+ if (yydebug_) \
+ yy_stack_print_ (); \
+ } while (false)
+
+#else // !NETCONF_DEBUG
+
+# define YYCDEBUG if (false) std::cerr
+# define YY_SYMBOL_PRINT(Title, Symbol) YY_USE (Symbol)
+# define YY_REDUCE_PRINT(Rule) static_cast<void> (0)
+# define YY_STACK_PRINT() static_cast<void> (0)
+
+#endif // !NETCONF_DEBUG
+
+#define yyerrok (yyerrstatus_ = 0)
+#define yyclearin (yyla.clear ())
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+#define YYRECOVERING() (!!yyerrstatus_)
+
+#line 14 "netconf_parser.yy"
+namespace isc { namespace netconf {
+#line 145 "netconf_parser.cc"
+
+ /// Build a parser object.
+ NetconfParser::NetconfParser (isc::netconf::ParserContext& ctx_yyarg)
+#if NETCONF_DEBUG
+ : yydebug_ (false),
+ yycdebug_ (&std::cerr),
+#else
+ :
+#endif
+ ctx (ctx_yyarg)
+ {}
+
+ NetconfParser::~NetconfParser ()
+ {}
+
+ NetconfParser::syntax_error::~syntax_error () YY_NOEXCEPT YY_NOTHROW
+ {}
+
+ /*---------.
+ | symbol. |
+ `---------*/
+
+
+
+ // by_state.
+ NetconfParser::by_state::by_state () YY_NOEXCEPT
+ : state (empty_state)
+ {}
+
+ NetconfParser::by_state::by_state (const by_state& that) YY_NOEXCEPT
+ : state (that.state)
+ {}
+
+ void
+ NetconfParser::by_state::clear () YY_NOEXCEPT
+ {
+ state = empty_state;
+ }
+
+ void
+ NetconfParser::by_state::move (by_state& that)
+ {
+ state = that.state;
+ that.clear ();
+ }
+
+ NetconfParser::by_state::by_state (state_type s) YY_NOEXCEPT
+ : state (s)
+ {}
+
+ NetconfParser::symbol_kind_type
+ NetconfParser::by_state::kind () const YY_NOEXCEPT
+ {
+ if (state == empty_state)
+ return symbol_kind::S_YYEMPTY;
+ else
+ return YY_CAST (symbol_kind_type, yystos_[+state]);
+ }
+
+ NetconfParser::stack_symbol_type::stack_symbol_type ()
+ {}
+
+ NetconfParser::stack_symbol_type::stack_symbol_type (YY_RVREF (stack_symbol_type) that)
+ : super_type (YY_MOVE (that.state), YY_MOVE (that.location))
+ {
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.YY_MOVE_OR_COPY< ElementPtr > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.YY_MOVE_OR_COPY< bool > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.YY_MOVE_OR_COPY< double > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.YY_MOVE_OR_COPY< int64_t > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.YY_MOVE_OR_COPY< std::string > (YY_MOVE (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+#if 201103L <= YY_CPLUSPLUS
+ // that is emptied.
+ that.state = empty_state;
+#endif
+ }
+
+ NetconfParser::stack_symbol_type::stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) that)
+ : super_type (s, YY_MOVE (that.location))
+ {
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.move< ElementPtr > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (YY_MOVE (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+ // that is emptied.
+ that.kind_ = symbol_kind::S_YYEMPTY;
+ }
+
+#if YY_CPLUSPLUS < 201103L
+ NetconfParser::stack_symbol_type&
+ NetconfParser::stack_symbol_type::operator= (const stack_symbol_type& that)
+ {
+ state = that.state;
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.copy< ElementPtr > (that.value);
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.copy< bool > (that.value);
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.copy< double > (that.value);
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.copy< int64_t > (that.value);
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.copy< std::string > (that.value);
+ break;
+
+ default:
+ break;
+ }
+
+ location = that.location;
+ return *this;
+ }
+
+ NetconfParser::stack_symbol_type&
+ NetconfParser::stack_symbol_type::operator= (stack_symbol_type& that)
+ {
+ state = that.state;
+ switch (that.kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.move< ElementPtr > (that.value);
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (that.value);
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (that.value);
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (that.value);
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (that.value);
+ break;
+
+ default:
+ break;
+ }
+
+ location = that.location;
+ // that is emptied.
+ that.state = empty_state;
+ return *this;
+ }
+#endif
+
+ template <typename Base>
+ void
+ NetconfParser::yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const
+ {
+ if (yymsg)
+ YY_SYMBOL_PRINT (yymsg, yysym);
+ }
+
+#if NETCONF_DEBUG
+ template <typename Base>
+ void
+ NetconfParser::yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const
+ {
+ std::ostream& yyoutput = yyo;
+ YY_USE (yyoutput);
+ if (yysym.empty ())
+ yyo << "empty symbol";
+ else
+ {
+ symbol_kind_type yykind = yysym.kind ();
+ yyo << (yykind < YYNTOKENS ? "token" : "nterm")
+ << ' ' << yysym.name () << " ("
+ << yysym.location << ": ";
+ switch (yykind)
+ {
+ case symbol_kind::S_STRING: // "constant string"
+#line 107 "netconf_parser.yy"
+ { yyoutput << yysym.value.template as < std::string > (); }
+#line 384 "netconf_parser.cc"
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+#line 107 "netconf_parser.yy"
+ { yyoutput << yysym.value.template as < int64_t > (); }
+#line 390 "netconf_parser.cc"
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+#line 107 "netconf_parser.yy"
+ { yyoutput << yysym.value.template as < double > (); }
+#line 396 "netconf_parser.cc"
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+#line 107 "netconf_parser.yy"
+ { yyoutput << yysym.value.template as < bool > (); }
+#line 402 "netconf_parser.cc"
+ break;
+
+ case symbol_kind::S_value: // value
+#line 107 "netconf_parser.yy"
+ { yyoutput << yysym.value.template as < ElementPtr > (); }
+#line 408 "netconf_parser.cc"
+ break;
+
+ case symbol_kind::S_map_value: // map_value
+#line 107 "netconf_parser.yy"
+ { yyoutput << yysym.value.template as < ElementPtr > (); }
+#line 414 "netconf_parser.cc"
+ break;
+
+ case symbol_kind::S_socket_type_value: // socket_type_value
+#line 107 "netconf_parser.yy"
+ { yyoutput << yysym.value.template as < ElementPtr > (); }
+#line 420 "netconf_parser.cc"
+ break;
+
+ default:
+ break;
+ }
+ yyo << ')';
+ }
+ }
+#endif
+
+ void
+ NetconfParser::yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym)
+ {
+ if (m)
+ YY_SYMBOL_PRINT (m, sym);
+ yystack_.push (YY_MOVE (sym));
+ }
+
+ void
+ NetconfParser::yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym)
+ {
+#if 201103L <= YY_CPLUSPLUS
+ yypush_ (m, stack_symbol_type (s, std::move (sym)));
+#else
+ stack_symbol_type ss (s, sym);
+ yypush_ (m, ss);
+#endif
+ }
+
+ void
+ NetconfParser::yypop_ (int n) YY_NOEXCEPT
+ {
+ yystack_.pop (n);
+ }
+
+#if NETCONF_DEBUG
+ std::ostream&
+ NetconfParser::debug_stream () const
+ {
+ return *yycdebug_;
+ }
+
+ void
+ NetconfParser::set_debug_stream (std::ostream& o)
+ {
+ yycdebug_ = &o;
+ }
+
+
+ NetconfParser::debug_level_type
+ NetconfParser::debug_level () const
+ {
+ return yydebug_;
+ }
+
+ void
+ NetconfParser::set_debug_level (debug_level_type l)
+ {
+ yydebug_ = l;
+ }
+#endif // NETCONF_DEBUG
+
+ NetconfParser::state_type
+ NetconfParser::yy_lr_goto_state_ (state_type yystate, int yysym)
+ {
+ int yyr = yypgoto_[yysym - YYNTOKENS] + yystate;
+ if (0 <= yyr && yyr <= yylast_ && yycheck_[yyr] == yystate)
+ return yytable_[yyr];
+ else
+ return yydefgoto_[yysym - YYNTOKENS];
+ }
+
+ bool
+ NetconfParser::yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT
+ {
+ return yyvalue == yypact_ninf_;
+ }
+
+ bool
+ NetconfParser::yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT
+ {
+ return yyvalue == yytable_ninf_;
+ }
+
+ int
+ NetconfParser::operator() ()
+ {
+ return parse ();
+ }
+
+ int
+ NetconfParser::parse ()
+ {
+ int yyn;
+ /// Length of the RHS of the rule being reduced.
+ int yylen = 0;
+
+ // Error handling.
+ int yynerrs_ = 0;
+ int yyerrstatus_ = 0;
+
+ /// The lookahead symbol.
+ symbol_type yyla;
+
+ /// The locations where the error started and ended.
+ stack_symbol_type yyerror_range[3];
+
+ /// The return value of parse ().
+ int yyresult;
+
+#if YY_EXCEPTIONS
+ try
+#endif // YY_EXCEPTIONS
+ {
+ YYCDEBUG << "Starting parse\n";
+
+
+ /* Initialize the stack. The initial state will be set in
+ yynewstate, since the latter expects the semantical and the
+ location values to have been already stored, initialize these
+ stacks with a primary value. */
+ yystack_.clear ();
+ yypush_ (YY_NULLPTR, 0, YY_MOVE (yyla));
+
+ /*-----------------------------------------------.
+ | yynewstate -- push a new symbol on the stack. |
+ `-----------------------------------------------*/
+ yynewstate:
+ YYCDEBUG << "Entering state " << int (yystack_[0].state) << '\n';
+ YY_STACK_PRINT ();
+
+ // Accept?
+ if (yystack_[0].state == yyfinal_)
+ YYACCEPT;
+
+ goto yybackup;
+
+
+ /*-----------.
+ | yybackup. |
+ `-----------*/
+ yybackup:
+ // Try to take a decision without lookahead.
+ yyn = yypact_[+yystack_[0].state];
+ if (yy_pact_value_is_default_ (yyn))
+ goto yydefault;
+
+ // Read a lookahead token.
+ if (yyla.empty ())
+ {
+ YYCDEBUG << "Reading a token\n";
+#if YY_EXCEPTIONS
+ try
+#endif // YY_EXCEPTIONS
+ {
+ symbol_type yylookahead (yylex (ctx));
+ yyla.move (yylookahead);
+ }
+#if YY_EXCEPTIONS
+ catch (const syntax_error& yyexc)
+ {
+ YYCDEBUG << "Caught exception: " << yyexc.what() << '\n';
+ error (yyexc);
+ goto yyerrlab1;
+ }
+#endif // YY_EXCEPTIONS
+ }
+ YY_SYMBOL_PRINT ("Next token is", yyla);
+
+ if (yyla.kind () == symbol_kind::S_YYerror)
+ {
+ // The scanner already issued an error message, process directly
+ // to error recovery. But do not keep the error token as
+ // lookahead, it is too special and may lead us to an endless
+ // loop in error recovery. */
+ yyla.kind_ = symbol_kind::S_YYUNDEF;
+ goto yyerrlab1;
+ }
+
+ /* If the proper action on seeing token YYLA.TYPE is to reduce or
+ to detect an error, take that action. */
+ yyn += yyla.kind ();
+ if (yyn < 0 || yylast_ < yyn || yycheck_[yyn] != yyla.kind ())
+ {
+ goto yydefault;
+ }
+
+ // Reduce or error.
+ yyn = yytable_[yyn];
+ if (yyn <= 0)
+ {
+ if (yy_table_value_is_error_ (yyn))
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ // Count tokens shifted since error; after three, turn off error status.
+ if (yyerrstatus_)
+ --yyerrstatus_;
+
+ // Shift the lookahead token.
+ yypush_ ("Shifting", state_type (yyn), YY_MOVE (yyla));
+ goto yynewstate;
+
+
+ /*-----------------------------------------------------------.
+ | yydefault -- do the default action for the current state. |
+ `-----------------------------------------------------------*/
+ yydefault:
+ yyn = yydefact_[+yystack_[0].state];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+ /*-----------------------------.
+ | yyreduce -- do a reduction. |
+ `-----------------------------*/
+ yyreduce:
+ yylen = yyr2_[yyn];
+ {
+ stack_symbol_type yylhs;
+ yylhs.state = yy_lr_goto_state_ (yystack_[yylen].state, yyr1_[yyn]);
+ /* Variants are always initialized to an empty instance of the
+ correct type. The default '$$ = $1' action is NOT applied
+ when using variants. */
+ switch (yyr1_[yyn])
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ yylhs.value.emplace< ElementPtr > ();
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ yylhs.value.emplace< bool > ();
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ yylhs.value.emplace< double > ();
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ yylhs.value.emplace< int64_t > ();
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ yylhs.value.emplace< std::string > ();
+ break;
+
+ default:
+ break;
+ }
+
+
+ // Default location.
+ {
+ stack_type::slice range (yystack_, yylen);
+ YYLLOC_DEFAULT (yylhs.location, range, yylen);
+ yyerror_range[1].location = yylhs.location;
+ }
+
+ // Perform the reduction.
+ YY_REDUCE_PRINT (yyn);
+#if YY_EXCEPTIONS
+ try
+#endif // YY_EXCEPTIONS
+ {
+ switch (yyn)
+ {
+ case 2: // $@1: %empty
+#line 118 "netconf_parser.yy"
+ { ctx.ctx_ = ctx.NO_KEYWORDS; }
+#line 695 "netconf_parser.cc"
+ break;
+
+ case 4: // $@2: %empty
+#line 119 "netconf_parser.yy"
+ { ctx.ctx_ = ctx.CONFIG; }
+#line 701 "netconf_parser.cc"
+ break;
+
+ case 6: // $@3: %empty
+#line 120 "netconf_parser.yy"
+ { ctx.ctx_ = ctx.NETCONF; }
+#line 707 "netconf_parser.cc"
+ break;
+
+ case 8: // $@4: %empty
+#line 128 "netconf_parser.yy"
+ {
+ // Parse the Netconf map
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(m);
+}
+#line 717 "netconf_parser.cc"
+ break;
+
+ case 9: // sub_netconf: "{" $@4 global_params "}"
+#line 132 "netconf_parser.yy"
+ {
+ // parsing completed
+}
+#line 725 "netconf_parser.cc"
+ break;
+
+ case 10: // json: value
+#line 139 "netconf_parser.yy"
+ {
+ // Push back the JSON value on the stack
+ ctx.stack_.push_back(yystack_[0].value.as < ElementPtr > ());
+}
+#line 734 "netconf_parser.cc"
+ break;
+
+ case 11: // value: "integer"
+#line 145 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); }
+#line 740 "netconf_parser.cc"
+ break;
+
+ case 12: // value: "floating point"
+#line 146 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new DoubleElement(yystack_[0].value.as < double > (), ctx.loc2pos(yystack_[0].location))); }
+#line 746 "netconf_parser.cc"
+ break;
+
+ case 13: // value: "boolean"
+#line 147 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location))); }
+#line 752 "netconf_parser.cc"
+ break;
+
+ case 14: // value: "constant string"
+#line 148 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location))); }
+#line 758 "netconf_parser.cc"
+ break;
+
+ case 15: // value: "null"
+#line 149 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new NullElement(ctx.loc2pos(yystack_[0].location))); }
+#line 764 "netconf_parser.cc"
+ break;
+
+ case 16: // value: map
+#line 150 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
+#line 770 "netconf_parser.cc"
+ break;
+
+ case 17: // value: list_generic
+#line 151 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
+#line 776 "netconf_parser.cc"
+ break;
+
+ case 18: // $@5: %empty
+#line 155 "netconf_parser.yy"
+ {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(m);
+}
+#line 787 "netconf_parser.cc"
+ break;
+
+ case 19: // map: "{" $@5 map_content "}"
+#line 160 "netconf_parser.yy"
+ {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+}
+#line 797 "netconf_parser.cc"
+ break;
+
+ case 20: // map_value: map
+#line 166 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); }
+#line 803 "netconf_parser.cc"
+ break;
+
+ case 23: // not_empty_map: "constant string" ":" value
+#line 180 "netconf_parser.yy"
+ {
+ // map containing a single entry
+ ctx.unique(yystack_[2].value.as < std::string > (), ctx.loc2pos(yystack_[2].location));
+ ctx.stack_.back()->set(yystack_[2].value.as < std::string > (), yystack_[0].value.as < ElementPtr > ());
+ }
+#line 813 "netconf_parser.cc"
+ break;
+
+ case 24: // not_empty_map: not_empty_map "," "constant string" ":" value
+#line 185 "netconf_parser.yy"
+ {
+ // map consisting of a shorter map followed by
+ // comma and string:value
+ ctx.unique(yystack_[2].value.as < std::string > (), ctx.loc2pos(yystack_[2].location));
+ ctx.stack_.back()->set(yystack_[2].value.as < std::string > (), yystack_[0].value.as < ElementPtr > ());
+ }
+#line 824 "netconf_parser.cc"
+ break;
+
+ case 25: // not_empty_map: not_empty_map ","
+#line 191 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 832 "netconf_parser.cc"
+ break;
+
+ case 26: // $@6: %empty
+#line 196 "netconf_parser.yy"
+ {
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(l);
+}
+#line 841 "netconf_parser.cc"
+ break;
+
+ case 27: // list_generic: "[" $@6 list_content "]"
+#line 199 "netconf_parser.yy"
+ {
+}
+#line 848 "netconf_parser.cc"
+ break;
+
+ case 30: // not_empty_list: value
+#line 206 "netconf_parser.yy"
+ {
+ // List consisting of a single element.
+ ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ());
+ }
+#line 857 "netconf_parser.cc"
+ break;
+
+ case 31: // not_empty_list: not_empty_list "," value
+#line 210 "netconf_parser.yy"
+ {
+ // List ending with , and a value.
+ ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ());
+ }
+#line 866 "netconf_parser.cc"
+ break;
+
+ case 32: // not_empty_list: not_empty_list ","
+#line 214 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 874 "netconf_parser.cc"
+ break;
+
+ case 33: // unknown_map_entry: "constant string" ":"
+#line 226 "netconf_parser.yy"
+ {
+ const std::string& where = ctx.contextName();
+ const std::string& keyword = yystack_[1].value.as < std::string > ();
+ error(yystack_[1].location,
+ "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
+}
+#line 885 "netconf_parser.cc"
+ break;
+
+ case 34: // $@7: %empty
+#line 234 "netconf_parser.yy"
+ {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.push_back(m);
+}
+#line 896 "netconf_parser.cc"
+ break;
+
+ case 35: // netconf_syntax_map: "{" $@7 global_object "}"
+#line 239 "netconf_parser.yy"
+ {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+}
+#line 906 "netconf_parser.cc"
+ break;
+
+ case 36: // $@8: %empty
+#line 246 "netconf_parser.yy"
+ {
+ // Let's create a MapElement that will represent it, add it to the
+ // top level map (that's already on the stack) and put the new map
+ // on the stack as well, so child elements will be able to add
+ // themselves to it.
+
+ // Prevent against duplicate.
+ ctx.unique("Netconf", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("Netconf", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.NETCONF);
+}
+#line 924 "netconf_parser.cc"
+ break;
+
+ case 37: // global_object: "Netconf" $@8 ":" "{" global_params "}"
+#line 258 "netconf_parser.yy"
+ {
+ // Ok, we're done with parsing Netconf. Let's take the map
+ // off the stack.
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 935 "netconf_parser.cc"
+ break;
+
+ case 39: // global_object_comma: global_object ","
+#line 267 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+}
+#line 943 "netconf_parser.cc"
+ break;
+
+ case 44: // not_empty_global_params: not_empty_global_params ","
+#line 278 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 951 "netconf_parser.cc"
+ break;
+
+ case 54: // boot_update: "boot-update" ":" "boolean"
+#line 296 "netconf_parser.yy"
+ {
+ ctx.unique("boot-update", ctx.loc2pos(yystack_[2].location));
+ ElementPtr flag(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("boot-update", flag);
+}
+#line 961 "netconf_parser.cc"
+ break;
+
+ case 55: // subscribe_changes: "subscribe-changes" ":" "boolean"
+#line 302 "netconf_parser.yy"
+ {
+ ctx.unique("subscribe-changes", ctx.loc2pos(yystack_[2].location));
+ ElementPtr flag(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("subscribe-changes", flag);
+}
+#line 971 "netconf_parser.cc"
+ break;
+
+ case 56: // validate_changes: "validate-changes" ":" "boolean"
+#line 308 "netconf_parser.yy"
+ {
+ ctx.unique("validate-changes", ctx.loc2pos(yystack_[2].location));
+ ElementPtr flag(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("validate-changes", flag);
+}
+#line 981 "netconf_parser.cc"
+ break;
+
+ case 57: // $@9: %empty
+#line 314 "netconf_parser.yy"
+ {
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 989 "netconf_parser.cc"
+ break;
+
+ case 58: // user_context: "user-context" $@9 ":" map_value
+#line 316 "netconf_parser.yy"
+ {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context = yystack_[0].value.as < ElementPtr > ();
+ ConstElementPtr old = parent->get("user-context");
+
+ // Handle already existing user context
+ if (old) {
+ // Check if it was a comment or a duplicate
+ if ((old->size() != 1) || !old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context entries (previous at "
+ << old->getPosition().str() << ")";
+ error(yystack_[3].location, msg.str());
+ }
+ // Merge the comment
+ user_context->set("comment", old->get("comment"));
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+}
+#line 1016 "netconf_parser.cc"
+ break;
+
+ case 59: // $@10: %empty
+#line 339 "netconf_parser.yy"
+ {
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1024 "netconf_parser.cc"
+ break;
+
+ case 60: // comment: "comment" $@10 ":" "constant string"
+#line 341 "netconf_parser.yy"
+ {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context(new MapElement(ctx.loc2pos(yystack_[3].location)));
+ ElementPtr comment(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ user_context->set("comment", comment);
+
+ // Handle already existing user context
+ ConstElementPtr old = parent->get("user-context");
+ if (old) {
+ // Check for duplicate comment
+ if (old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context/comment entries (previous at "
+ << old->getPosition().str() << ")";
+ error(yystack_[3].location, msg.str());
+ }
+ // Merge the user context in the comment
+ merge(user_context, old);
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+}
+#line 1053 "netconf_parser.cc"
+ break;
+
+ case 61: // $@11: %empty
+#line 367 "netconf_parser.yy"
+ {
+ ctx.unique("hooks-libraries", ctx.loc2pos(yystack_[0].location));
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("hooks-libraries", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.HOOKS_LIBRARIES);
+}
+#line 1065 "netconf_parser.cc"
+ break;
+
+ case 62: // hooks_libraries: "hooks-libraries" $@11 ":" "[" hooks_libraries_list "]"
+#line 373 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1074 "netconf_parser.cc"
+ break;
+
+ case 67: // not_empty_hooks_libraries_list: not_empty_hooks_libraries_list ","
+#line 384 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1082 "netconf_parser.cc"
+ break;
+
+ case 68: // $@12: %empty
+#line 389 "netconf_parser.yy"
+ {
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+}
+#line 1092 "netconf_parser.cc"
+ break;
+
+ case 69: // hooks_library: "{" $@12 hooks_params "}"
+#line 393 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+}
+#line 1100 "netconf_parser.cc"
+ break;
+
+ case 72: // hooks_params: hooks_params ","
+#line 399 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1108 "netconf_parser.cc"
+ break;
+
+ case 76: // $@13: %empty
+#line 409 "netconf_parser.yy"
+ {
+ ctx.unique("library", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1117 "netconf_parser.cc"
+ break;
+
+ case 77: // library: "library" $@13 ":" "constant string"
+#line 412 "netconf_parser.yy"
+ {
+ ElementPtr lib(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("library", lib);
+ ctx.leave();
+}
+#line 1127 "netconf_parser.cc"
+ break;
+
+ case 78: // $@14: %empty
+#line 418 "netconf_parser.yy"
+ {
+ ctx.unique("parameters", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1136 "netconf_parser.cc"
+ break;
+
+ case 79: // parameters: "parameters" $@14 ":" map_value
+#line 421 "netconf_parser.yy"
+ {
+ ctx.stack_.back()->set("parameters", yystack_[0].value.as < ElementPtr > ());
+ ctx.leave();
+}
+#line 1145 "netconf_parser.cc"
+ break;
+
+ case 80: // $@15: %empty
+#line 429 "netconf_parser.yy"
+ {
+ ctx.unique("managed-servers", ctx.loc2pos(yystack_[2].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[2].location)));
+ ctx.stack_.back()->set("managed-servers", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.MANAGED_SERVERS);
+}
+#line 1157 "netconf_parser.cc"
+ break;
+
+ case 81: // managed_servers: "managed-servers" ":" "{" $@15 servers_entries "}"
+#line 435 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1166 "netconf_parser.cc"
+ break;
+
+ case 86: // not_empty_servers_entries: not_empty_servers_entries ","
+#line 446 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1174 "netconf_parser.cc"
+ break;
+
+ case 92: // $@16: %empty
+#line 462 "netconf_parser.yy"
+ {
+ ctx.unique("dhcp4", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("dhcp4", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+}
+#line 1186 "netconf_parser.cc"
+ break;
+
+ case 93: // dhcp4_server: "dhcp4" $@16 ":" "{" managed_server_params "}"
+#line 468 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1195 "netconf_parser.cc"
+ break;
+
+ case 94: // $@17: %empty
+#line 474 "netconf_parser.yy"
+ {
+ ctx.unique("dhcp6", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("dhcp6", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+}
+#line 1207 "netconf_parser.cc"
+ break;
+
+ case 95: // dhcp6_server: "dhcp6" $@17 ":" "{" managed_server_params "}"
+#line 480 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1216 "netconf_parser.cc"
+ break;
+
+ case 96: // $@18: %empty
+#line 486 "netconf_parser.yy"
+ {
+ ctx.unique("d2", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("d2", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+}
+#line 1228 "netconf_parser.cc"
+ break;
+
+ case 97: // d2_server: "d2" $@18 ":" "{" managed_server_params "}"
+#line 492 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1237 "netconf_parser.cc"
+ break;
+
+ case 98: // $@19: %empty
+#line 498 "netconf_parser.yy"
+ {
+ ctx.unique("ca", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("ca", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+}
+#line 1249 "netconf_parser.cc"
+ break;
+
+ case 99: // ca_server: "ca" $@19 ":" "{" managed_server_params "}"
+#line 504 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1258 "netconf_parser.cc"
+ break;
+
+ case 102: // managed_server_params: managed_server_params ","
+#line 512 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1266 "netconf_parser.cc"
+ break;
+
+ case 111: // $@20: %empty
+#line 529 "netconf_parser.yy"
+ {
+ ctx.unique("model", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1275 "netconf_parser.cc"
+ break;
+
+ case 112: // model: "model" $@20 ":" "constant string"
+#line 532 "netconf_parser.yy"
+ {
+ ElementPtr model(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("model", model);
+ ctx.leave();
+}
+#line 1285 "netconf_parser.cc"
+ break;
+
+ case 113: // $@21: %empty
+#line 539 "netconf_parser.yy"
+ {
+ ctx.unique("control-socket", ctx.loc2pos(yystack_[0].location));
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("control-socket", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.CONTROL_SOCKET);
+}
+#line 1297 "netconf_parser.cc"
+ break;
+
+ case 114: // control_socket: "control-socket" $@21 ":" "{" control_socket_params "}"
+#line 545 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1306 "netconf_parser.cc"
+ break;
+
+ case 117: // control_socket_params: control_socket_params ","
+#line 553 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1314 "netconf_parser.cc"
+ break;
+
+ case 124: // $@22: %empty
+#line 566 "netconf_parser.yy"
+ {
+ ctx.unique("socket-type", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.SOCKET_TYPE);
+}
+#line 1323 "netconf_parser.cc"
+ break;
+
+ case 125: // socket_type: "socket-type" $@22 ":" socket_type_value
+#line 569 "netconf_parser.yy"
+ {
+ ctx.stack_.back()->set("socket-type", yystack_[0].value.as < ElementPtr > ());
+ ctx.leave();
+}
+#line 1332 "netconf_parser.cc"
+ break;
+
+ case 126: // socket_type_value: "unix"
+#line 575 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("unix", ctx.loc2pos(yystack_[0].location))); }
+#line 1338 "netconf_parser.cc"
+ break;
+
+ case 127: // socket_type_value: "http"
+#line 576 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("http", ctx.loc2pos(yystack_[0].location))); }
+#line 1344 "netconf_parser.cc"
+ break;
+
+ case 128: // socket_type_value: "stdout"
+#line 577 "netconf_parser.yy"
+ { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("stdout", ctx.loc2pos(yystack_[0].location))); }
+#line 1350 "netconf_parser.cc"
+ break;
+
+ case 129: // $@23: %empty
+#line 580 "netconf_parser.yy"
+ {
+ ctx.unique("socket-name", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1359 "netconf_parser.cc"
+ break;
+
+ case 130: // socket_name: "socket-name" $@23 ":" "constant string"
+#line 583 "netconf_parser.yy"
+ {
+ ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("socket-name", name);
+ ctx.leave();
+}
+#line 1369 "netconf_parser.cc"
+ break;
+
+ case 131: // $@24: %empty
+#line 590 "netconf_parser.yy"
+ {
+ ctx.unique("socket-url", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1378 "netconf_parser.cc"
+ break;
+
+ case 132: // socket_url: "socket-url" $@24 ":" "constant string"
+#line 593 "netconf_parser.yy"
+ {
+ ElementPtr url(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("socket-url", url);
+ ctx.leave();
+}
+#line 1388 "netconf_parser.cc"
+ break;
+
+ case 133: // $@25: %empty
+#line 603 "netconf_parser.yy"
+ {
+ ctx.unique("loggers", ctx.loc2pos(yystack_[0].location));
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("loggers", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.LOGGERS);
+}
+#line 1400 "netconf_parser.cc"
+ break;
+
+ case 134: // loggers: "loggers" $@25 ":" "[" loggers_entries "]"
+#line 609 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1409 "netconf_parser.cc"
+ break;
+
+ case 137: // loggers_entries: loggers_entries ","
+#line 618 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1417 "netconf_parser.cc"
+ break;
+
+ case 138: // $@26: %empty
+#line 624 "netconf_parser.yy"
+ {
+ ElementPtr l(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->add(l);
+ ctx.stack_.push_back(l);
+}
+#line 1427 "netconf_parser.cc"
+ break;
+
+ case 139: // logger_entry: "{" $@26 logger_params "}"
+#line 628 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+}
+#line 1435 "netconf_parser.cc"
+ break;
+
+ case 142: // logger_params: logger_params ","
+#line 634 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1443 "netconf_parser.cc"
+ break;
+
+ case 150: // $@27: %empty
+#line 648 "netconf_parser.yy"
+ {
+ ctx.unique("name", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1452 "netconf_parser.cc"
+ break;
+
+ case 151: // name: "name" $@27 ":" "constant string"
+#line 651 "netconf_parser.yy"
+ {
+ ElementPtr name(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("name", name);
+ ctx.leave();
+}
+#line 1462 "netconf_parser.cc"
+ break;
+
+ case 152: // debuglevel: "debuglevel" ":" "integer"
+#line 657 "netconf_parser.yy"
+ {
+ ctx.unique("debuglevel", ctx.loc2pos(yystack_[2].location));
+ ElementPtr dl(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("debuglevel", dl);
+}
+#line 1472 "netconf_parser.cc"
+ break;
+
+ case 153: // $@28: %empty
+#line 663 "netconf_parser.yy"
+ {
+ ctx.unique("severity", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1481 "netconf_parser.cc"
+ break;
+
+ case 154: // severity: "severity" $@28 ":" "constant string"
+#line 666 "netconf_parser.yy"
+ {
+ ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("severity", sev);
+ ctx.leave();
+}
+#line 1491 "netconf_parser.cc"
+ break;
+
+ case 155: // $@29: %empty
+#line 672 "netconf_parser.yy"
+ {
+ ctx.unique("output_options", ctx.loc2pos(yystack_[0].location));
+ ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("output_options", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.OUTPUT_OPTIONS);
+}
+#line 1503 "netconf_parser.cc"
+ break;
+
+ case 156: // output_options_list: "output_options" $@29 ":" "[" output_options_list_content "]"
+#line 678 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+#line 1512 "netconf_parser.cc"
+ break;
+
+ case 159: // output_options_list_content: output_options_list_content ","
+#line 685 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1520 "netconf_parser.cc"
+ break;
+
+ case 160: // $@30: %empty
+#line 690 "netconf_parser.yy"
+ {
+ ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+}
+#line 1530 "netconf_parser.cc"
+ break;
+
+ case 161: // output_entry: "{" $@30 output_params_list "}"
+#line 694 "netconf_parser.yy"
+ {
+ ctx.stack_.pop_back();
+}
+#line 1538 "netconf_parser.cc"
+ break;
+
+ case 164: // output_params_list: output_params_list ","
+#line 700 "netconf_parser.yy"
+ {
+ ctx.warnAboutExtraCommas(yystack_[0].location);
+ }
+#line 1546 "netconf_parser.cc"
+ break;
+
+ case 170: // $@31: %empty
+#line 712 "netconf_parser.yy"
+ {
+ ctx.unique("output", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1555 "netconf_parser.cc"
+ break;
+
+ case 171: // output: "output" $@31 ":" "constant string"
+#line 715 "netconf_parser.yy"
+ {
+ ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("output", sev);
+ ctx.leave();
+}
+#line 1565 "netconf_parser.cc"
+ break;
+
+ case 172: // flush: "flush" ":" "boolean"
+#line 721 "netconf_parser.yy"
+ {
+ ctx.unique("flush", ctx.loc2pos(yystack_[2].location));
+ ElementPtr flush(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("flush", flush);
+}
+#line 1575 "netconf_parser.cc"
+ break;
+
+ case 173: // maxsize: "maxsize" ":" "integer"
+#line 727 "netconf_parser.yy"
+ {
+ ctx.unique("maxsize", ctx.loc2pos(yystack_[2].location));
+ ElementPtr maxsize(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("maxsize", maxsize);
+}
+#line 1585 "netconf_parser.cc"
+ break;
+
+ case 174: // maxver: "maxver" ":" "integer"
+#line 733 "netconf_parser.yy"
+ {
+ ctx.unique("maxver", ctx.loc2pos(yystack_[2].location));
+ ElementPtr maxver(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("maxver", maxver);
+}
+#line 1595 "netconf_parser.cc"
+ break;
+
+ case 175: // $@32: %empty
+#line 739 "netconf_parser.yy"
+ {
+ ctx.unique("pattern", ctx.loc2pos(yystack_[0].location));
+ ctx.enter(ctx.NO_KEYWORDS);
+}
+#line 1604 "netconf_parser.cc"
+ break;
+
+ case 176: // pattern: "pattern" $@32 ":" "constant string"
+#line 742 "netconf_parser.yy"
+ {
+ ElementPtr sev(new StringElement(yystack_[0].value.as < std::string > (), ctx.loc2pos(yystack_[0].location)));
+ ctx.stack_.back()->set("pattern", sev);
+ ctx.leave();
+}
+#line 1614 "netconf_parser.cc"
+ break;
+
+
+#line 1618 "netconf_parser.cc"
+
+ default:
+ break;
+ }
+ }
+#if YY_EXCEPTIONS
+ catch (const syntax_error& yyexc)
+ {
+ YYCDEBUG << "Caught exception: " << yyexc.what() << '\n';
+ error (yyexc);
+ YYERROR;
+ }
+#endif // YY_EXCEPTIONS
+ YY_SYMBOL_PRINT ("-> $$ =", yylhs);
+ yypop_ (yylen);
+ yylen = 0;
+
+ // Shift the result of the reduction.
+ yypush_ (YY_NULLPTR, YY_MOVE (yylhs));
+ }
+ goto yynewstate;
+
+
+ /*--------------------------------------.
+ | yyerrlab -- here on detecting error. |
+ `--------------------------------------*/
+ yyerrlab:
+ // If not already recovering from an error, report this error.
+ if (!yyerrstatus_)
+ {
+ ++yynerrs_;
+ context yyctx (*this, yyla);
+ std::string msg = yysyntax_error_ (yyctx);
+ error (yyla.location, YY_MOVE (msg));
+ }
+
+
+ yyerror_range[1].location = yyla.location;
+ if (yyerrstatus_ == 3)
+ {
+ /* If just tried and failed to reuse lookahead token after an
+ error, discard it. */
+
+ // Return failure if at end of input.
+ if (yyla.kind () == symbol_kind::S_YYEOF)
+ YYABORT;
+ else if (!yyla.empty ())
+ {
+ yy_destroy_ ("Error: discarding", yyla);
+ yyla.clear ();
+ }
+ }
+
+ // Else will try to reuse lookahead token after shifting the error token.
+ goto yyerrlab1;
+
+
+ /*---------------------------------------------------.
+ | yyerrorlab -- error raised explicitly by YYERROR. |
+ `---------------------------------------------------*/
+ yyerrorlab:
+ /* Pacify compilers when the user code never invokes YYERROR and
+ the label yyerrorlab therefore never appears in user code. */
+ if (false)
+ YYERROR;
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYERROR. */
+ yypop_ (yylen);
+ yylen = 0;
+ YY_STACK_PRINT ();
+ goto yyerrlab1;
+
+
+ /*-------------------------------------------------------------.
+ | yyerrlab1 -- common code for both syntax error and YYERROR. |
+ `-------------------------------------------------------------*/
+ yyerrlab1:
+ yyerrstatus_ = 3; // Each real token shifted decrements this.
+ // Pop stack until we find a state that shifts the error token.
+ for (;;)
+ {
+ yyn = yypact_[+yystack_[0].state];
+ if (!yy_pact_value_is_default_ (yyn))
+ {
+ yyn += symbol_kind::S_YYerror;
+ if (0 <= yyn && yyn <= yylast_
+ && yycheck_[yyn] == symbol_kind::S_YYerror)
+ {
+ yyn = yytable_[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ // Pop the current state because it cannot handle the error token.
+ if (yystack_.size () == 1)
+ YYABORT;
+
+ yyerror_range[1].location = yystack_[0].location;
+ yy_destroy_ ("Error: popping", yystack_[0]);
+ yypop_ ();
+ YY_STACK_PRINT ();
+ }
+ {
+ stack_symbol_type error_token;
+
+ yyerror_range[2].location = yyla.location;
+ YYLLOC_DEFAULT (error_token.location, yyerror_range, 2);
+
+ // Shift the error token.
+ error_token.state = state_type (yyn);
+ yypush_ ("Shifting", YY_MOVE (error_token));
+ }
+ goto yynewstate;
+
+
+ /*-------------------------------------.
+ | yyacceptlab -- YYACCEPT comes here. |
+ `-------------------------------------*/
+ yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+
+ /*-----------------------------------.
+ | yyabortlab -- YYABORT comes here. |
+ `-----------------------------------*/
+ yyabortlab:
+ yyresult = 1;
+ goto yyreturn;
+
+
+ /*-----------------------------------------------------.
+ | yyreturn -- parsing is finished, return the result. |
+ `-----------------------------------------------------*/
+ yyreturn:
+ if (!yyla.empty ())
+ yy_destroy_ ("Cleanup: discarding lookahead", yyla);
+
+ /* Do not reclaim the symbols of the rule whose action triggered
+ this YYABORT or YYACCEPT. */
+ yypop_ (yylen);
+ YY_STACK_PRINT ();
+ while (1 < yystack_.size ())
+ {
+ yy_destroy_ ("Cleanup: popping", yystack_[0]);
+ yypop_ ();
+ }
+
+ return yyresult;
+ }
+#if YY_EXCEPTIONS
+ catch (...)
+ {
+ YYCDEBUG << "Exception caught: cleaning lookahead and stack\n";
+ // Do not try to display the values of the reclaimed symbols,
+ // as their printers might throw an exception.
+ if (!yyla.empty ())
+ yy_destroy_ (YY_NULLPTR, yyla);
+
+ while (1 < yystack_.size ())
+ {
+ yy_destroy_ (YY_NULLPTR, yystack_[0]);
+ yypop_ ();
+ }
+ throw;
+ }
+#endif // YY_EXCEPTIONS
+ }
+
+ void
+ NetconfParser::error (const syntax_error& yyexc)
+ {
+ error (yyexc.location, yyexc.what ());
+ }
+
+ /* Return YYSTR after stripping away unnecessary quotes and
+ backslashes, so that it's suitable for yyerror. The heuristic is
+ that double-quoting is unnecessary unless the string contains an
+ apostrophe, a comma, or backslash (other than backslash-backslash).
+ YYSTR is taken from yytname. */
+ std::string
+ NetconfParser::yytnamerr_ (const char *yystr)
+ {
+ if (*yystr == '"')
+ {
+ std::string yyr;
+ char const *yyp = yystr;
+
+ for (;;)
+ switch (*++yyp)
+ {
+ case '\'':
+ case ',':
+ goto do_not_strip_quotes;
+
+ case '\\':
+ if (*++yyp != '\\')
+ goto do_not_strip_quotes;
+ else
+ goto append;
+
+ append:
+ default:
+ yyr += *yyp;
+ break;
+
+ case '"':
+ return yyr;
+ }
+ do_not_strip_quotes: ;
+ }
+
+ return yystr;
+ }
+
+ std::string
+ NetconfParser::symbol_name (symbol_kind_type yysymbol)
+ {
+ return yytnamerr_ (yytname_[yysymbol]);
+ }
+
+
+
+ // NetconfParser::context.
+ NetconfParser::context::context (const NetconfParser& yyparser, const symbol_type& yyla)
+ : yyparser_ (yyparser)
+ , yyla_ (yyla)
+ {}
+
+ int
+ NetconfParser::context::expected_tokens (symbol_kind_type yyarg[], int yyargn) const
+ {
+ // Actual number of expected tokens
+ int yycount = 0;
+
+ const int yyn = yypact_[+yyparser_.yystack_[0].state];
+ if (!yy_pact_value_is_default_ (yyn))
+ {
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. In other words, skip the first -YYN actions for
+ this state because they are default actions. */
+ const int yyxbegin = yyn < 0 ? -yyn : 0;
+ // Stay within bounds of both yycheck and yytname.
+ const int yychecklim = yylast_ - yyn + 1;
+ const int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ for (int yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck_[yyx + yyn] == yyx && yyx != symbol_kind::S_YYerror
+ && !yy_table_value_is_error_ (yytable_[yyx + yyn]))
+ {
+ if (!yyarg)
+ ++yycount;
+ else if (yycount == yyargn)
+ return 0;
+ else
+ yyarg[yycount++] = YY_CAST (symbol_kind_type, yyx);
+ }
+ }
+
+ if (yyarg && yycount == 0 && 0 < yyargn)
+ yyarg[0] = symbol_kind::S_YYEMPTY;
+ return yycount;
+ }
+
+
+
+
+
+
+ int
+ NetconfParser::yy_syntax_error_arguments_ (const context& yyctx,
+ symbol_kind_type yyarg[], int yyargn) const
+ {
+ /* There are many possibilities here to consider:
+ - If this state is a consistent state with a default action, then
+ the only way this function was invoked is if the default action
+ is an error action. In that case, don't check for expected
+ tokens because there are none.
+ - The only way there can be no lookahead present (in yyla) is
+ if this state is a consistent state with a default action.
+ Thus, detecting the absence of a lookahead is sufficient to
+ determine that there is no unexpected or expected token to
+ report. In that case, just report a simple "syntax error".
+ - Don't assume there isn't a lookahead just because this state is
+ a consistent state with a default action. There might have
+ been a previous inconsistent state, consistent state with a
+ non-default action, or user semantic action that manipulated
+ yyla. (However, yyla is currently not documented for users.)
+ - Of course, the expected token list depends on states to have
+ correct lookahead information, and it depends on the parser not
+ to perform extra reductions after fetching a lookahead from the
+ scanner and before detecting a syntax error. Thus, state merging
+ (from LALR or IELR) and default reductions corrupt the expected
+ token list. However, the list is correct for canonical LR with
+ one exception: it will still contain any token that will not be
+ accepted due to an error action in a later state.
+ */
+
+ if (!yyctx.lookahead ().empty ())
+ {
+ if (yyarg)
+ yyarg[0] = yyctx.token ();
+ int yyn = yyctx.expected_tokens (yyarg ? yyarg + 1 : yyarg, yyargn - 1);
+ return yyn + 1;
+ }
+ return 0;
+ }
+
+ // Generate an error message.
+ std::string
+ NetconfParser::yysyntax_error_ (const context& yyctx) const
+ {
+ // Its maximum.
+ enum { YYARGS_MAX = 5 };
+ // Arguments of yyformat.
+ symbol_kind_type yyarg[YYARGS_MAX];
+ int yycount = yy_syntax_error_arguments_ (yyctx, yyarg, YYARGS_MAX);
+
+ char const* yyformat = YY_NULLPTR;
+ switch (yycount)
+ {
+#define YYCASE_(N, S) \
+ case N: \
+ yyformat = S; \
+ break
+ default: // Avoid compiler warnings.
+ YYCASE_ (0, YY_("syntax error"));
+ YYCASE_ (1, YY_("syntax error, unexpected %s"));
+ YYCASE_ (2, YY_("syntax error, unexpected %s, expecting %s"));
+ YYCASE_ (3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+ YYCASE_ (4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+ YYCASE_ (5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+#undef YYCASE_
+ }
+
+ std::string yyres;
+ // Argument number.
+ std::ptrdiff_t yyi = 0;
+ for (char const* yyp = yyformat; *yyp; ++yyp)
+ if (yyp[0] == '%' && yyp[1] == 's' && yyi < yycount)
+ {
+ yyres += symbol_name (yyarg[yyi++]);
+ ++yyp;
+ }
+ else
+ yyres += *yyp;
+ return yyres;
+ }
+
+
+ const signed char NetconfParser::yypact_ninf_ = -79;
+
+ const signed char NetconfParser::yytable_ninf_ = -1;
+
+ const short
+ NetconfParser::yypact_[] =
+ {
+ -8, -79, -79, -79, 16, 14, 13, 15, -79, -79,
+ -79, -79, -79, -79, -79, -79, -79, -79, -79, -79,
+ -79, -79, -79, -79, 14, 2, 45, -1, -79, 61,
+ 68, 75, 73, 79, -79, 30, -79, -79, -79, 89,
+ 90, 123, 124, -79, -79, 125, -79, 108, 94, -79,
+ -79, -79, -79, -79, -79, -79, -79, -79, -79, 14,
+ 14, -79, 85, 127, -79, -79, 128, 129, 86, 87,
+ 88, 130, 134, 135, -79, -79, -1, -79, -79, 136,
+ 140, 141, 96, -79, -79, -79, -79, 137, 138, -79,
+ 14, -1, -79, -79, -79, 7, 142, 143, -79, 144,
+ -79, -79, -79, -79, -79, 145, 148, -79, -79, -79,
+ -79, -79, -79, 149, 151, -79, -79, 50, -79, -79,
+ 152, 153, 154, 155, -79, 7, -13, -79, 142, 76,
+ 143, -79, 156, 157, 158, 159, -79, -79, -79, -79,
+ 34, -79, -79, -79, -79, -79, -79, 163, -79, -79,
+ -79, -79, 40, -79, -79, -79, -79, -79, -79, 63,
+ 63, 63, 63, 164, 165, -24, -79, 166, 167, 116,
+ 168, 76, -79, -79, -79, -79, -79, -79, -79, -79,
+ -79, 92, -79, -79, -79, 93, 111, 112, 131, 141,
+ -79, 132, 169, -79, 133, -79, 171, 175, 63, -79,
+ -79, -79, -79, -79, -79, -79, 173, -79, 139, 174,
+ -79, -79, 77, -79, -79, 18, 51, 173, -79, -79,
+ -79, -79, -79, -79, -79, 114, -79, -79, -79, -79,
+ -79, 178, 179, 181, -79, 115, -79, -79, -79, -79,
+ -79, -79, -79, 182, 183, 187, 18, -79, 188, 113,
+ 147, 150, 190, 51, -79, 81, 160, 161, -79, 162,
+ -79, -79, -79, 170, -79, -79, -79, -79, -79, -79,
+ -79, -79, -79
+ };
+
+ const unsigned char
+ NetconfParser::yydefact_[] =
+ {
+ 0, 2, 4, 6, 0, 0, 0, 0, 1, 26,
+ 18, 15, 14, 11, 12, 13, 3, 10, 16, 17,
+ 34, 5, 8, 7, 28, 21, 0, 40, 30, 0,
+ 29, 0, 0, 22, 36, 0, 38, 57, 59, 0,
+ 0, 0, 0, 61, 133, 0, 53, 0, 41, 42,
+ 45, 46, 47, 51, 52, 49, 48, 50, 27, 32,
+ 0, 19, 25, 0, 39, 35, 0, 0, 0, 0,
+ 0, 0, 0, 0, 33, 9, 44, 31, 23, 0,
+ 0, 0, 0, 54, 55, 56, 80, 0, 0, 43,
+ 0, 40, 20, 58, 60, 82, 63, 0, 24, 0,
+ 92, 94, 96, 98, 91, 0, 83, 84, 87, 88,
+ 89, 90, 68, 0, 64, 65, 138, 0, 135, 37,
+ 0, 0, 0, 0, 81, 86, 0, 62, 67, 0,
+ 137, 134, 0, 0, 0, 0, 85, 76, 78, 73,
+ 0, 70, 74, 75, 66, 150, 155, 0, 153, 149,
+ 147, 148, 0, 140, 143, 145, 146, 144, 136, 0,
+ 0, 0, 0, 0, 0, 72, 69, 0, 0, 0,
+ 0, 142, 139, 111, 113, 110, 104, 105, 106, 108,
+ 109, 0, 100, 103, 107, 0, 0, 0, 0, 0,
+ 71, 0, 0, 152, 0, 141, 0, 0, 102, 93,
+ 95, 97, 99, 77, 79, 151, 0, 154, 0, 0,
+ 101, 160, 0, 157, 112, 0, 0, 159, 156, 124,
+ 129, 131, 123, 121, 122, 0, 115, 118, 119, 120,
+ 170, 0, 0, 0, 175, 0, 162, 165, 166, 167,
+ 168, 169, 158, 0, 0, 0, 117, 114, 0, 0,
+ 0, 0, 0, 164, 161, 0, 0, 0, 116, 0,
+ 172, 173, 174, 0, 163, 126, 127, 128, 125, 130,
+ 132, 171, 176
+ };
+
+ const signed char
+ NetconfParser::yypgoto_[] =
+ {
+ -79, -79, -79, -79, -79, -79, -79, -79, -20, -78,
+ -79, -29, -79, -79, -79, -79, -79, -79, -27, -79,
+ -79, -79, -79, -79, 82, -79, 119, -22, -19, -18,
+ -26, -79, -25, -79, -79, -79, -79, -79, 69, -79,
+ -79, 33, -79, -79, -79, -79, -79, -79, -79, -79,
+ 74, -79, -79, -79, -79, -79, -79, -79, -79, -36,
+ 3, -79, -79, -79, -79, -79, -46, -79, -79, -79,
+ -79, -79, -79, -79, -79, -79, -79, 72, -79, -79,
+ 32, -79, -79, -79, -79, -79, -79, -79, -79, -9,
+ -79, -79, -49, -79, -79, -79, -79, -79, -79, -79
+ };
+
+ const short
+ NetconfParser::yydefgoto_[] =
+ {
+ 0, 4, 5, 6, 7, 23, 27, 16, 17, 18,
+ 25, 93, 32, 33, 19, 24, 29, 30, 175, 21,
+ 26, 35, 63, 36, 47, 48, 49, 176, 177, 178,
+ 179, 66, 180, 67, 55, 72, 113, 114, 115, 126,
+ 140, 141, 142, 163, 143, 164, 56, 95, 105, 106,
+ 107, 108, 120, 109, 121, 110, 122, 111, 123, 181,
+ 182, 183, 196, 184, 197, 225, 226, 227, 243, 268,
+ 228, 244, 229, 245, 57, 73, 117, 118, 129, 152,
+ 153, 154, 167, 155, 156, 170, 157, 168, 212, 213,
+ 216, 235, 236, 237, 248, 238, 239, 240, 241, 252
+ };
+
+ const short
+ NetconfParser::yytable_[] =
+ {
+ 46, 53, 54, 92, 28, 50, 137, 138, 51, 52,
+ 37, 38, 39, 40, 41, 42, 8, 137, 138, 9,
+ 20, 10, 22, 11, 100, 101, 102, 103, 43, 37,
+ 38, 44, 45, 64, 1, 2, 3, 165, 65, 77,
+ 78, 219, 166, 171, 45, 220, 221, 31, 172, 46,
+ 53, 54, 45, 130, 50, 34, 131, 51, 52, 12,
+ 13, 14, 15, 45, 46, 53, 54, 58, 104, 50,
+ 98, 59, 51, 52, 37, 38, 39, 40, 41, 60,
+ 217, 61, 62, 218, 173, 174, 230, 37, 38, 231,
+ 232, 233, 234, 68, 69, 198, 198, 76, 104, 139,
+ 199, 200, 149, 150, 151, 265, 266, 267, 45, 145,
+ 146, 92, 147, 148, 198, 198, 75, 246, 253, 201,
+ 202, 45, 247, 254, 185, 186, 187, 70, 71, 74,
+ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
+ 90, 94, 96, 97, 149, 150, 151, 91, 10, 112,
+ 116, 125, 119, 124, 128, 127, 132, 133, 134, 135,
+ 204, 260, 193, 159, 160, 161, 162, 169, 188, 189,
+ 191, 192, 194, 99, 206, 208, 203, 205, 207, 209,
+ 211, 215, 249, 250, 214, 251, 255, 256, 222, 223,
+ 224, 257, 259, 261, 263, 89, 262, 144, 190, 136,
+ 258, 210, 158, 195, 264, 269, 270, 271, 242, 0,
+ 0, 0, 0, 0, 0, 272, 0, 0, 0, 222,
+ 223, 224
+ };
+
+ const short
+ NetconfParser::yycheck_[] =
+ {
+ 27, 27, 27, 81, 24, 27, 30, 31, 27, 27,
+ 11, 12, 13, 14, 15, 16, 0, 30, 31, 5,
+ 7, 7, 7, 9, 17, 18, 19, 20, 29, 11,
+ 12, 32, 45, 3, 42, 43, 44, 3, 8, 59,
+ 60, 23, 8, 3, 45, 27, 28, 45, 8, 76,
+ 76, 76, 45, 3, 76, 10, 6, 76, 76, 45,
+ 46, 47, 48, 45, 91, 91, 91, 6, 95, 91,
+ 90, 3, 91, 91, 11, 12, 13, 14, 15, 4,
+ 3, 8, 3, 6, 21, 22, 35, 11, 12, 38,
+ 39, 40, 41, 4, 4, 3, 3, 3, 125, 126,
+ 8, 8, 129, 129, 129, 24, 25, 26, 45, 33,
+ 34, 189, 36, 37, 3, 3, 8, 3, 3, 8,
+ 8, 45, 8, 8, 160, 161, 162, 4, 4, 4,
+ 45, 4, 4, 4, 48, 48, 48, 7, 4, 4,
+ 4, 45, 5, 5, 171, 171, 171, 7, 7, 7,
+ 7, 3, 8, 8, 3, 6, 4, 4, 4, 4,
+ 189, 48, 46, 7, 7, 7, 7, 4, 4, 4,
+ 4, 4, 4, 91, 5, 4, 45, 45, 45, 4,
+ 7, 7, 4, 4, 45, 4, 4, 4, 215, 215,
+ 215, 4, 4, 46, 4, 76, 46, 128, 165, 125,
+ 246, 198, 130, 171, 253, 45, 45, 45, 217, -1,
+ -1, -1, -1, -1, -1, 45, -1, -1, -1, 246,
+ 246, 246
+ };
+
+ const unsigned char
+ NetconfParser::yystos_[] =
+ {
+ 0, 42, 43, 44, 50, 51, 52, 53, 0, 5,
+ 7, 9, 45, 46, 47, 48, 56, 57, 58, 63,
+ 7, 68, 7, 54, 64, 59, 69, 55, 57, 65,
+ 66, 45, 61, 62, 10, 70, 72, 11, 12, 13,
+ 14, 15, 16, 29, 32, 45, 67, 73, 74, 75,
+ 76, 77, 78, 79, 81, 83, 95, 123, 6, 3,
+ 4, 8, 3, 71, 3, 8, 80, 82, 4, 4,
+ 4, 4, 84, 124, 4, 8, 3, 57, 57, 45,
+ 4, 4, 4, 48, 48, 48, 7, 4, 4, 75,
+ 4, 7, 58, 60, 45, 96, 5, 5, 57, 73,
+ 17, 18, 19, 20, 67, 97, 98, 99, 100, 102,
+ 104, 106, 7, 85, 86, 87, 7, 125, 126, 8,
+ 101, 103, 105, 107, 8, 3, 88, 6, 3, 127,
+ 3, 6, 4, 4, 4, 4, 99, 30, 31, 67,
+ 89, 90, 91, 93, 87, 33, 34, 36, 37, 67,
+ 79, 81, 128, 129, 130, 132, 133, 135, 126, 7,
+ 7, 7, 7, 92, 94, 3, 8, 131, 136, 4,
+ 134, 3, 8, 21, 22, 67, 76, 77, 78, 79,
+ 81, 108, 109, 110, 112, 108, 108, 108, 4, 4,
+ 90, 4, 4, 46, 4, 129, 111, 113, 3, 8,
+ 8, 8, 8, 45, 60, 45, 5, 45, 4, 4,
+ 109, 7, 137, 138, 45, 7, 139, 3, 6, 23,
+ 27, 28, 67, 79, 81, 114, 115, 116, 119, 121,
+ 35, 38, 39, 40, 41, 140, 141, 142, 144, 145,
+ 146, 147, 138, 117, 120, 122, 3, 8, 143, 4,
+ 4, 4, 148, 3, 8, 4, 4, 4, 115, 4,
+ 48, 46, 46, 4, 141, 24, 25, 26, 118, 45,
+ 45, 45, 45
+ };
+
+ const unsigned char
+ NetconfParser::yyr1_[] =
+ {
+ 0, 49, 51, 50, 52, 50, 53, 50, 55, 54,
+ 56, 57, 57, 57, 57, 57, 57, 57, 59, 58,
+ 60, 61, 61, 62, 62, 62, 64, 63, 65, 65,
+ 66, 66, 66, 67, 69, 68, 71, 70, 70, 72,
+ 73, 73, 74, 74, 74, 75, 75, 75, 75, 75,
+ 75, 75, 75, 75, 76, 77, 78, 80, 79, 82,
+ 81, 84, 83, 85, 85, 86, 86, 86, 88, 87,
+ 89, 89, 89, 89, 90, 90, 92, 91, 94, 93,
+ 96, 95, 97, 97, 98, 98, 98, 99, 99, 99,
+ 99, 99, 101, 100, 103, 102, 105, 104, 107, 106,
+ 108, 108, 108, 109, 109, 109, 109, 109, 109, 109,
+ 109, 111, 110, 113, 112, 114, 114, 114, 115, 115,
+ 115, 115, 115, 115, 117, 116, 118, 118, 118, 120,
+ 119, 122, 121, 124, 123, 125, 125, 125, 127, 126,
+ 128, 128, 128, 129, 129, 129, 129, 129, 129, 129,
+ 131, 130, 132, 134, 133, 136, 135, 137, 137, 137,
+ 139, 138, 140, 140, 140, 141, 141, 141, 141, 141,
+ 143, 142, 144, 145, 146, 148, 147
+ };
+
+ const signed char
+ NetconfParser::yyr2_[] =
+ {
+ 0, 2, 0, 3, 0, 3, 0, 3, 0, 4,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 4,
+ 1, 0, 1, 3, 5, 2, 0, 4, 0, 1,
+ 1, 3, 2, 2, 0, 4, 0, 6, 1, 2,
+ 0, 1, 1, 3, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 3, 3, 3, 0, 4, 0,
+ 4, 0, 6, 0, 1, 1, 3, 2, 0, 4,
+ 1, 3, 2, 1, 1, 1, 0, 4, 0, 4,
+ 0, 6, 0, 1, 1, 3, 2, 1, 1, 1,
+ 1, 1, 0, 6, 0, 6, 0, 6, 0, 6,
+ 1, 3, 2, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 4, 0, 6, 1, 3, 2, 1, 1,
+ 1, 1, 1, 1, 0, 4, 1, 1, 1, 0,
+ 4, 0, 4, 0, 6, 1, 3, 2, 0, 4,
+ 1, 3, 2, 1, 1, 1, 1, 1, 1, 1,
+ 0, 4, 3, 0, 4, 0, 6, 1, 3, 2,
+ 0, 4, 1, 3, 2, 1, 1, 1, 1, 1,
+ 0, 4, 3, 3, 3, 0, 4
+ };
+
+
+#if NETCONF_DEBUG || 1
+ // YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ // First, the terminals, then, starting at \a YYNTOKENS, nonterminals.
+ const char*
+ const NetconfParser::yytname_[] =
+ {
+ "\"end of file\"", "error", "\"invalid token\"", "\",\"", "\":\"",
+ "\"[\"", "\"]\"", "\"{\"", "\"}\"", "\"null\"", "\"Netconf\"",
+ "\"user-context\"", "\"comment\"", "\"boot-update\"",
+ "\"subscribe-changes\"", "\"validate-changes\"", "\"managed-servers\"",
+ "\"dhcp4\"", "\"dhcp6\"", "\"d2\"", "\"ca\"", "\"model\"",
+ "\"control-socket\"", "\"socket-type\"", "\"unix\"", "\"http\"",
+ "\"stdout\"", "\"socket-name\"", "\"socket-url\"", "\"hooks-libraries\"",
+ "\"library\"", "\"parameters\"", "\"loggers\"", "\"name\"",
+ "\"output_options\"", "\"output\"", "\"debuglevel\"", "\"severity\"",
+ "\"flush\"", "\"maxsize\"", "\"maxver\"", "\"pattern\"", "START_JSON",
+ "START_NETCONF", "START_SUB_NETCONF", "\"constant string\"",
+ "\"integer\"", "\"floating point\"", "\"boolean\"", "$accept", "start",
+ "$@1", "$@2", "$@3", "sub_netconf", "$@4", "json", "value", "map", "$@5",
+ "map_value", "map_content", "not_empty_map", "list_generic", "$@6",
+ "list_content", "not_empty_list", "unknown_map_entry",
+ "netconf_syntax_map", "$@7", "global_object", "$@8",
+ "global_object_comma", "global_params", "not_empty_global_params",
+ "global_param", "boot_update", "subscribe_changes", "validate_changes",
+ "user_context", "$@9", "comment", "$@10", "hooks_libraries", "$@11",
+ "hooks_libraries_list", "not_empty_hooks_libraries_list",
+ "hooks_library", "$@12", "hooks_params", "hooks_param", "library",
+ "$@13", "parameters", "$@14", "managed_servers", "$@15",
+ "servers_entries", "not_empty_servers_entries", "server_entry",
+ "dhcp4_server", "$@16", "dhcp6_server", "$@17", "d2_server", "$@18",
+ "ca_server", "$@19", "managed_server_params", "managed_server_param",
+ "model", "$@20", "control_socket", "$@21", "control_socket_params",
+ "control_socket_param", "socket_type", "$@22", "socket_type_value",
+ "socket_name", "$@23", "socket_url", "$@24", "loggers", "$@25",
+ "loggers_entries", "logger_entry", "$@26", "logger_params",
+ "logger_param", "name", "$@27", "debuglevel", "severity", "$@28",
+ "output_options_list", "$@29", "output_options_list_content",
+ "output_entry", "$@30", "output_params_list", "output_params", "output",
+ "$@31", "flush", "maxsize", "maxver", "pattern", "$@32", YY_NULLPTR
+ };
+#endif
+
+
+#if NETCONF_DEBUG
+ const short
+ NetconfParser::yyrline_[] =
+ {
+ 0, 118, 118, 118, 119, 119, 120, 120, 128, 128,
+ 139, 145, 146, 147, 148, 149, 150, 151, 155, 155,
+ 166, 171, 172, 180, 185, 191, 196, 196, 202, 203,
+ 206, 210, 214, 226, 234, 234, 246, 246, 264, 267,
+ 272, 273, 276, 277, 278, 285, 286, 287, 288, 289,
+ 290, 291, 292, 293, 296, 302, 308, 314, 314, 339,
+ 339, 367, 367, 378, 379, 382, 383, 384, 389, 389,
+ 397, 398, 399, 402, 405, 406, 409, 409, 418, 418,
+ 429, 429, 440, 441, 444, 445, 446, 454, 455, 456,
+ 457, 458, 462, 462, 474, 474, 486, 486, 498, 498,
+ 510, 511, 512, 518, 519, 520, 521, 522, 523, 524,
+ 525, 529, 529, 539, 539, 551, 552, 553, 558, 559,
+ 560, 561, 562, 563, 566, 566, 575, 576, 577, 580,
+ 580, 590, 590, 603, 603, 616, 617, 618, 624, 624,
+ 632, 633, 634, 639, 640, 641, 642, 643, 644, 645,
+ 648, 648, 657, 663, 663, 672, 672, 683, 684, 685,
+ 690, 690, 698, 699, 700, 705, 706, 707, 708, 709,
+ 712, 712, 721, 727, 733, 739, 739
+ };
+
+ void
+ NetconfParser::yy_stack_print_ () const
+ {
+ *yycdebug_ << "Stack now";
+ for (stack_type::const_iterator
+ i = yystack_.begin (),
+ i_end = yystack_.end ();
+ i != i_end; ++i)
+ *yycdebug_ << ' ' << int (i->state);
+ *yycdebug_ << '\n';
+ }
+
+ void
+ NetconfParser::yy_reduce_print_ (int yyrule) const
+ {
+ int yylno = yyrline_[yyrule];
+ int yynrhs = yyr2_[yyrule];
+ // Print the symbols being reduced, and their result.
+ *yycdebug_ << "Reducing stack by rule " << yyrule - 1
+ << " (line " << yylno << "):\n";
+ // The symbols being reduced.
+ for (int yyi = 0; yyi < yynrhs; yyi++)
+ YY_SYMBOL_PRINT (" $" << yyi + 1 << " =",
+ yystack_[(yynrhs) - (yyi + 1)]);
+ }
+#endif // NETCONF_DEBUG
+
+
+#line 14 "netconf_parser.yy"
+} } // isc::netconf
+#line 2303 "netconf_parser.cc"
+
+#line 748 "netconf_parser.yy"
+
+
+void
+isc::netconf::NetconfParser::error(const location_type& loc,
+ const std::string& what)
+{
+ ctx.error(loc, what);
+}
diff --git a/src/bin/netconf/netconf_parser.h b/src/bin/netconf/netconf_parser.h
new file mode 100644
index 0000000..3b8e258
--- /dev/null
+++ b/src/bin/netconf/netconf_parser.h
@@ -0,0 +1,2387 @@
+// A Bison parser, made by GNU Bison 3.8.2.
+
+// Skeleton interface for Bison LALR(1) parsers in C++
+
+// Copyright (C) 2002-2015, 2018-2021 Free Software Foundation, Inc.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+// As a special exception, you may create a larger work that contains
+// part or all of the Bison parser skeleton and distribute that work
+// under terms of your choice, so long as that work isn't itself a
+// parser generator using the skeleton or a modified version thereof
+// as a parser skeleton. Alternatively, if you modify or redistribute
+// the parser skeleton itself, you may (at your option) remove this
+// special exception, which will cause the skeleton and the resulting
+// Bison output files to be licensed under the GNU General Public
+// License without this special exception.
+
+// This special exception was added by the Free Software Foundation in
+// version 2.2 of Bison.
+
+
+/**
+ ** \file netconf_parser.h
+ ** Define the isc::netconf::parser class.
+ */
+
+// C++ LALR(1) parser skeleton written by Akim Demaille.
+
+// DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+// especially those whose name start with YY_ or yy_. They are
+// private implementation details that can be changed or removed.
+
+#ifndef YY_NETCONF_NETCONF_PARSER_H_INCLUDED
+# define YY_NETCONF_NETCONF_PARSER_H_INCLUDED
+// "%code requires" blocks.
+#line 17 "netconf_parser.yy"
+
+#include <string>
+#include <cc/data.h>
+#include <boost/lexical_cast.hpp>
+#include <netconf/parser_context_decl.h>
+
+using namespace isc::netconf;
+using namespace isc::data;
+using namespace std;
+
+#line 60 "netconf_parser.h"
+
+# include <cassert>
+# include <cstdlib> // std::abort
+# include <iostream>
+# include <stdexcept>
+# include <string>
+# include <vector>
+
+#if defined __cplusplus
+# define YY_CPLUSPLUS __cplusplus
+#else
+# define YY_CPLUSPLUS 199711L
+#endif
+
+// Support move semantics when possible.
+#if 201103L <= YY_CPLUSPLUS
+# define YY_MOVE std::move
+# define YY_MOVE_OR_COPY move
+# define YY_MOVE_REF(Type) Type&&
+# define YY_RVREF(Type) Type&&
+# define YY_COPY(Type) Type
+#else
+# define YY_MOVE
+# define YY_MOVE_OR_COPY copy
+# define YY_MOVE_REF(Type) Type&
+# define YY_RVREF(Type) const Type&
+# define YY_COPY(Type) const Type&
+#endif
+
+// Support noexcept when possible.
+#if 201103L <= YY_CPLUSPLUS
+# define YY_NOEXCEPT noexcept
+# define YY_NOTHROW
+#else
+# define YY_NOEXCEPT
+# define YY_NOTHROW throw ()
+#endif
+
+// Support constexpr when possible.
+#if 201703 <= YY_CPLUSPLUS
+# define YY_CONSTEXPR constexpr
+#else
+# define YY_CONSTEXPR
+#endif
+# include "location.hh"
+#include <typeinfo>
+#ifndef NETCONF__ASSERT
+# include <cassert>
+# define NETCONF__ASSERT assert
+#endif
+
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+# define YY_ATTRIBUTE_PURE
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+# define YY_ATTRIBUTE_UNUSED
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E. */
+#if ! defined lint || defined __GNUC__
+# define YY_USE(E) ((void) (E))
+#else
+# define YY_USE(E) /* empty */
+#endif
+
+/* Suppress an incorrect diagnostic about yylval being uninitialized. */
+#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
+# if __GNUC__ * 100 + __GNUC_MINOR__ < 407
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")
+# else
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \
+ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# endif
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+ _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN \
+ _Pragma ("GCC diagnostic push") \
+ _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END \
+ _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
+# ifndef YY_CAST
+# ifdef __cplusplus
+# define YY_CAST(Type, Val) static_cast<Type> (Val)
+# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+# else
+# define YY_CAST(Type, Val) ((Type) (Val))
+# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+# endif
+# endif
+# ifndef YY_NULLPTR
+# if defined __cplusplus
+# if 201103L <= __cplusplus
+# define YY_NULLPTR nullptr
+# else
+# define YY_NULLPTR 0
+# endif
+# else
+# define YY_NULLPTR ((void*)0)
+# endif
+# endif
+
+/* Debug traces. */
+#ifndef NETCONF_DEBUG
+# if defined YYDEBUG
+#if YYDEBUG
+# define NETCONF_DEBUG 1
+# else
+# define NETCONF_DEBUG 0
+# endif
+# else /* ! defined YYDEBUG */
+# define NETCONF_DEBUG 1
+# endif /* ! defined YYDEBUG */
+#endif /* ! defined NETCONF_DEBUG */
+
+#line 14 "netconf_parser.yy"
+namespace isc { namespace netconf {
+#line 209 "netconf_parser.h"
+
+
+
+
+ /// A Bison parser.
+ class NetconfParser
+ {
+ public:
+#ifdef NETCONF_STYPE
+# ifdef __GNUC__
+# pragma GCC message "bison: do not #define NETCONF_STYPE in C++, use %define api.value.type"
+# endif
+ typedef NETCONF_STYPE value_type;
+#else
+ /// A buffer to store and retrieve objects.
+ ///
+ /// Sort of a variant, but does not keep track of the nature
+ /// of the stored data, since that knowledge is available
+ /// via the current parser state.
+ class value_type
+ {
+ public:
+ /// Type of *this.
+ typedef value_type self_type;
+
+ /// Empty construction.
+ value_type () YY_NOEXCEPT
+ : yyraw_ ()
+ , yytypeid_ (YY_NULLPTR)
+ {}
+
+ /// Construct and fill.
+ template <typename T>
+ value_type (YY_RVREF (T) t)
+ : yytypeid_ (&typeid (T))
+ {
+ NETCONF__ASSERT (sizeof (T) <= size);
+ new (yyas_<T> ()) T (YY_MOVE (t));
+ }
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Non copyable.
+ value_type (const self_type&) = delete;
+ /// Non copyable.
+ self_type& operator= (const self_type&) = delete;
+#endif
+
+ /// Destruction, allowed only if empty.
+ ~value_type () YY_NOEXCEPT
+ {
+ NETCONF__ASSERT (!yytypeid_);
+ }
+
+# if 201103L <= YY_CPLUSPLUS
+ /// Instantiate a \a T in here from \a t.
+ template <typename T, typename... U>
+ T&
+ emplace (U&&... u)
+ {
+ NETCONF__ASSERT (!yytypeid_);
+ NETCONF__ASSERT (sizeof (T) <= size);
+ yytypeid_ = & typeid (T);
+ return *new (yyas_<T> ()) T (std::forward <U>(u)...);
+ }
+# else
+ /// Instantiate an empty \a T in here.
+ template <typename T>
+ T&
+ emplace ()
+ {
+ NETCONF__ASSERT (!yytypeid_);
+ NETCONF__ASSERT (sizeof (T) <= size);
+ yytypeid_ = & typeid (T);
+ return *new (yyas_<T> ()) T ();
+ }
+
+ /// Instantiate a \a T in here from \a t.
+ template <typename T>
+ T&
+ emplace (const T& t)
+ {
+ NETCONF__ASSERT (!yytypeid_);
+ NETCONF__ASSERT (sizeof (T) <= size);
+ yytypeid_ = & typeid (T);
+ return *new (yyas_<T> ()) T (t);
+ }
+# endif
+
+ /// Instantiate an empty \a T in here.
+ /// Obsolete, use emplace.
+ template <typename T>
+ T&
+ build ()
+ {
+ return emplace<T> ();
+ }
+
+ /// Instantiate a \a T in here from \a t.
+ /// Obsolete, use emplace.
+ template <typename T>
+ T&
+ build (const T& t)
+ {
+ return emplace<T> (t);
+ }
+
+ /// Accessor to a built \a T.
+ template <typename T>
+ T&
+ as () YY_NOEXCEPT
+ {
+ NETCONF__ASSERT (yytypeid_);
+ NETCONF__ASSERT (*yytypeid_ == typeid (T));
+ NETCONF__ASSERT (sizeof (T) <= size);
+ return *yyas_<T> ();
+ }
+
+ /// Const accessor to a built \a T (for %printer).
+ template <typename T>
+ const T&
+ as () const YY_NOEXCEPT
+ {
+ NETCONF__ASSERT (yytypeid_);
+ NETCONF__ASSERT (*yytypeid_ == typeid (T));
+ NETCONF__ASSERT (sizeof (T) <= size);
+ return *yyas_<T> ();
+ }
+
+ /// Swap the content with \a that, of same type.
+ ///
+ /// Both variants must be built beforehand, because swapping the actual
+ /// data requires reading it (with as()), and this is not possible on
+ /// unconstructed variants: it would require some dynamic testing, which
+ /// should not be the variant's responsibility.
+ /// Swapping between built and (possibly) non-built is done with
+ /// self_type::move ().
+ template <typename T>
+ void
+ swap (self_type& that) YY_NOEXCEPT
+ {
+ NETCONF__ASSERT (yytypeid_);
+ NETCONF__ASSERT (*yytypeid_ == *that.yytypeid_);
+ std::swap (as<T> (), that.as<T> ());
+ }
+
+ /// Move the content of \a that to this.
+ ///
+ /// Destroys \a that.
+ template <typename T>
+ void
+ move (self_type& that)
+ {
+# if 201103L <= YY_CPLUSPLUS
+ emplace<T> (std::move (that.as<T> ()));
+# else
+ emplace<T> ();
+ swap<T> (that);
+# endif
+ that.destroy<T> ();
+ }
+
+# if 201103L <= YY_CPLUSPLUS
+ /// Move the content of \a that to this.
+ template <typename T>
+ void
+ move (self_type&& that)
+ {
+ emplace<T> (std::move (that.as<T> ()));
+ that.destroy<T> ();
+ }
+#endif
+
+ /// Copy the content of \a that to this.
+ template <typename T>
+ void
+ copy (const self_type& that)
+ {
+ emplace<T> (that.as<T> ());
+ }
+
+ /// Destroy the stored \a T.
+ template <typename T>
+ void
+ destroy ()
+ {
+ as<T> ().~T ();
+ yytypeid_ = YY_NULLPTR;
+ }
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Non copyable.
+ value_type (const self_type&);
+ /// Non copyable.
+ self_type& operator= (const self_type&);
+#endif
+
+ /// Accessor to raw memory as \a T.
+ template <typename T>
+ T*
+ yyas_ () YY_NOEXCEPT
+ {
+ void *yyp = yyraw_;
+ return static_cast<T*> (yyp);
+ }
+
+ /// Const accessor to raw memory as \a T.
+ template <typename T>
+ const T*
+ yyas_ () const YY_NOEXCEPT
+ {
+ const void *yyp = yyraw_;
+ return static_cast<const T*> (yyp);
+ }
+
+ /// An auxiliary type to compute the largest semantic type.
+ union union_type
+ {
+ // value
+ // map_value
+ // socket_type_value
+ char dummy1[sizeof (ElementPtr)];
+
+ // "boolean"
+ char dummy2[sizeof (bool)];
+
+ // "floating point"
+ char dummy3[sizeof (double)];
+
+ // "integer"
+ char dummy4[sizeof (int64_t)];
+
+ // "constant string"
+ char dummy5[sizeof (std::string)];
+ };
+
+ /// The size of the largest semantic type.
+ enum { size = sizeof (union_type) };
+
+ /// A buffer to store semantic values.
+ union
+ {
+ /// Strongest alignment constraints.
+ long double yyalign_me_;
+ /// A buffer large enough to store any of the semantic values.
+ char yyraw_[size];
+ };
+
+ /// Whether the content is built: if defined, the name of the stored type.
+ const std::type_info *yytypeid_;
+ };
+
+#endif
+ /// Backward compatibility (Bison 3.8).
+ typedef value_type semantic_type;
+
+ /// Symbol locations.
+ typedef location location_type;
+
+ /// Syntax errors thrown from user actions.
+ struct syntax_error : std::runtime_error
+ {
+ syntax_error (const location_type& l, const std::string& m)
+ : std::runtime_error (m)
+ , location (l)
+ {}
+
+ syntax_error (const syntax_error& s)
+ : std::runtime_error (s.what ())
+ , location (s.location)
+ {}
+
+ ~syntax_error () YY_NOEXCEPT YY_NOTHROW;
+
+ location_type location;
+ };
+
+ /// Token kinds.
+ struct token
+ {
+ enum token_kind_type
+ {
+ TOKEN_NETCONF_EMPTY = -2,
+ TOKEN_END = 0, // "end of file"
+ TOKEN_NETCONF_error = 256, // error
+ TOKEN_NETCONF_UNDEF = 257, // "invalid token"
+ TOKEN_COMMA = 258, // ","
+ TOKEN_COLON = 259, // ":"
+ TOKEN_LSQUARE_BRACKET = 260, // "["
+ TOKEN_RSQUARE_BRACKET = 261, // "]"
+ TOKEN_LCURLY_BRACKET = 262, // "{"
+ TOKEN_RCURLY_BRACKET = 263, // "}"
+ TOKEN_NULL_TYPE = 264, // "null"
+ TOKEN_NETCONF = 265, // "Netconf"
+ TOKEN_USER_CONTEXT = 266, // "user-context"
+ TOKEN_COMMENT = 267, // "comment"
+ TOKEN_BOOT_UPDATE = 268, // "boot-update"
+ TOKEN_SUBSCRIBE_CHANGES = 269, // "subscribe-changes"
+ TOKEN_VALIDATE_CHANGES = 270, // "validate-changes"
+ TOKEN_MANAGED_SERVERS = 271, // "managed-servers"
+ TOKEN_DHCP4_SERVER = 272, // "dhcp4"
+ TOKEN_DHCP6_SERVER = 273, // "dhcp6"
+ TOKEN_D2_SERVER = 274, // "d2"
+ TOKEN_CA_SERVER = 275, // "ca"
+ TOKEN_MODEL = 276, // "model"
+ TOKEN_CONTROL_SOCKET = 277, // "control-socket"
+ TOKEN_SOCKET_TYPE = 278, // "socket-type"
+ TOKEN_UNIX = 279, // "unix"
+ TOKEN_HTTP = 280, // "http"
+ TOKEN_STDOUT = 281, // "stdout"
+ TOKEN_SOCKET_NAME = 282, // "socket-name"
+ TOKEN_SOCKET_URL = 283, // "socket-url"
+ TOKEN_HOOKS_LIBRARIES = 284, // "hooks-libraries"
+ TOKEN_LIBRARY = 285, // "library"
+ TOKEN_PARAMETERS = 286, // "parameters"
+ TOKEN_LOGGERS = 287, // "loggers"
+ TOKEN_NAME = 288, // "name"
+ TOKEN_OUTPUT_OPTIONS = 289, // "output_options"
+ TOKEN_OUTPUT = 290, // "output"
+ TOKEN_DEBUGLEVEL = 291, // "debuglevel"
+ TOKEN_SEVERITY = 292, // "severity"
+ TOKEN_FLUSH = 293, // "flush"
+ TOKEN_MAXSIZE = 294, // "maxsize"
+ TOKEN_MAXVER = 295, // "maxver"
+ TOKEN_PATTERN = 296, // "pattern"
+ TOKEN_START_JSON = 297, // START_JSON
+ TOKEN_START_NETCONF = 298, // START_NETCONF
+ TOKEN_START_SUB_NETCONF = 299, // START_SUB_NETCONF
+ TOKEN_STRING = 300, // "constant string"
+ TOKEN_INTEGER = 301, // "integer"
+ TOKEN_FLOAT = 302, // "floating point"
+ TOKEN_BOOLEAN = 303 // "boolean"
+ };
+ /// Backward compatibility alias (Bison 3.6).
+ typedef token_kind_type yytokentype;
+ };
+
+ /// Token kind, as returned by yylex.
+ typedef token::token_kind_type token_kind_type;
+
+ /// Backward compatibility alias (Bison 3.6).
+ typedef token_kind_type token_type;
+
+ /// Symbol kinds.
+ struct symbol_kind
+ {
+ enum symbol_kind_type
+ {
+ YYNTOKENS = 49, ///< Number of tokens.
+ S_YYEMPTY = -2,
+ S_YYEOF = 0, // "end of file"
+ S_YYerror = 1, // error
+ S_YYUNDEF = 2, // "invalid token"
+ S_COMMA = 3, // ","
+ S_COLON = 4, // ":"
+ S_LSQUARE_BRACKET = 5, // "["
+ S_RSQUARE_BRACKET = 6, // "]"
+ S_LCURLY_BRACKET = 7, // "{"
+ S_RCURLY_BRACKET = 8, // "}"
+ S_NULL_TYPE = 9, // "null"
+ S_NETCONF = 10, // "Netconf"
+ S_USER_CONTEXT = 11, // "user-context"
+ S_COMMENT = 12, // "comment"
+ S_BOOT_UPDATE = 13, // "boot-update"
+ S_SUBSCRIBE_CHANGES = 14, // "subscribe-changes"
+ S_VALIDATE_CHANGES = 15, // "validate-changes"
+ S_MANAGED_SERVERS = 16, // "managed-servers"
+ S_DHCP4_SERVER = 17, // "dhcp4"
+ S_DHCP6_SERVER = 18, // "dhcp6"
+ S_D2_SERVER = 19, // "d2"
+ S_CA_SERVER = 20, // "ca"
+ S_MODEL = 21, // "model"
+ S_CONTROL_SOCKET = 22, // "control-socket"
+ S_SOCKET_TYPE = 23, // "socket-type"
+ S_UNIX = 24, // "unix"
+ S_HTTP = 25, // "http"
+ S_STDOUT = 26, // "stdout"
+ S_SOCKET_NAME = 27, // "socket-name"
+ S_SOCKET_URL = 28, // "socket-url"
+ S_HOOKS_LIBRARIES = 29, // "hooks-libraries"
+ S_LIBRARY = 30, // "library"
+ S_PARAMETERS = 31, // "parameters"
+ S_LOGGERS = 32, // "loggers"
+ S_NAME = 33, // "name"
+ S_OUTPUT_OPTIONS = 34, // "output_options"
+ S_OUTPUT = 35, // "output"
+ S_DEBUGLEVEL = 36, // "debuglevel"
+ S_SEVERITY = 37, // "severity"
+ S_FLUSH = 38, // "flush"
+ S_MAXSIZE = 39, // "maxsize"
+ S_MAXVER = 40, // "maxver"
+ S_PATTERN = 41, // "pattern"
+ S_START_JSON = 42, // START_JSON
+ S_START_NETCONF = 43, // START_NETCONF
+ S_START_SUB_NETCONF = 44, // START_SUB_NETCONF
+ S_STRING = 45, // "constant string"
+ S_INTEGER = 46, // "integer"
+ S_FLOAT = 47, // "floating point"
+ S_BOOLEAN = 48, // "boolean"
+ S_YYACCEPT = 49, // $accept
+ S_start = 50, // start
+ S_51_1 = 51, // $@1
+ S_52_2 = 52, // $@2
+ S_53_3 = 53, // $@3
+ S_sub_netconf = 54, // sub_netconf
+ S_55_4 = 55, // $@4
+ S_json = 56, // json
+ S_value = 57, // value
+ S_map = 58, // map
+ S_59_5 = 59, // $@5
+ S_map_value = 60, // map_value
+ S_map_content = 61, // map_content
+ S_not_empty_map = 62, // not_empty_map
+ S_list_generic = 63, // list_generic
+ S_64_6 = 64, // $@6
+ S_list_content = 65, // list_content
+ S_not_empty_list = 66, // not_empty_list
+ S_unknown_map_entry = 67, // unknown_map_entry
+ S_netconf_syntax_map = 68, // netconf_syntax_map
+ S_69_7 = 69, // $@7
+ S_global_object = 70, // global_object
+ S_71_8 = 71, // $@8
+ S_global_object_comma = 72, // global_object_comma
+ S_global_params = 73, // global_params
+ S_not_empty_global_params = 74, // not_empty_global_params
+ S_global_param = 75, // global_param
+ S_boot_update = 76, // boot_update
+ S_subscribe_changes = 77, // subscribe_changes
+ S_validate_changes = 78, // validate_changes
+ S_user_context = 79, // user_context
+ S_80_9 = 80, // $@9
+ S_comment = 81, // comment
+ S_82_10 = 82, // $@10
+ S_hooks_libraries = 83, // hooks_libraries
+ S_84_11 = 84, // $@11
+ S_hooks_libraries_list = 85, // hooks_libraries_list
+ S_not_empty_hooks_libraries_list = 86, // not_empty_hooks_libraries_list
+ S_hooks_library = 87, // hooks_library
+ S_88_12 = 88, // $@12
+ S_hooks_params = 89, // hooks_params
+ S_hooks_param = 90, // hooks_param
+ S_library = 91, // library
+ S_92_13 = 92, // $@13
+ S_parameters = 93, // parameters
+ S_94_14 = 94, // $@14
+ S_managed_servers = 95, // managed_servers
+ S_96_15 = 96, // $@15
+ S_servers_entries = 97, // servers_entries
+ S_not_empty_servers_entries = 98, // not_empty_servers_entries
+ S_server_entry = 99, // server_entry
+ S_dhcp4_server = 100, // dhcp4_server
+ S_101_16 = 101, // $@16
+ S_dhcp6_server = 102, // dhcp6_server
+ S_103_17 = 103, // $@17
+ S_d2_server = 104, // d2_server
+ S_105_18 = 105, // $@18
+ S_ca_server = 106, // ca_server
+ S_107_19 = 107, // $@19
+ S_managed_server_params = 108, // managed_server_params
+ S_managed_server_param = 109, // managed_server_param
+ S_model = 110, // model
+ S_111_20 = 111, // $@20
+ S_control_socket = 112, // control_socket
+ S_113_21 = 113, // $@21
+ S_control_socket_params = 114, // control_socket_params
+ S_control_socket_param = 115, // control_socket_param
+ S_socket_type = 116, // socket_type
+ S_117_22 = 117, // $@22
+ S_socket_type_value = 118, // socket_type_value
+ S_socket_name = 119, // socket_name
+ S_120_23 = 120, // $@23
+ S_socket_url = 121, // socket_url
+ S_122_24 = 122, // $@24
+ S_loggers = 123, // loggers
+ S_124_25 = 124, // $@25
+ S_loggers_entries = 125, // loggers_entries
+ S_logger_entry = 126, // logger_entry
+ S_127_26 = 127, // $@26
+ S_logger_params = 128, // logger_params
+ S_logger_param = 129, // logger_param
+ S_name = 130, // name
+ S_131_27 = 131, // $@27
+ S_debuglevel = 132, // debuglevel
+ S_severity = 133, // severity
+ S_134_28 = 134, // $@28
+ S_output_options_list = 135, // output_options_list
+ S_136_29 = 136, // $@29
+ S_output_options_list_content = 137, // output_options_list_content
+ S_output_entry = 138, // output_entry
+ S_139_30 = 139, // $@30
+ S_output_params_list = 140, // output_params_list
+ S_output_params = 141, // output_params
+ S_output = 142, // output
+ S_143_31 = 143, // $@31
+ S_flush = 144, // flush
+ S_maxsize = 145, // maxsize
+ S_maxver = 146, // maxver
+ S_pattern = 147, // pattern
+ S_148_32 = 148 // $@32
+ };
+ };
+
+ /// (Internal) symbol kind.
+ typedef symbol_kind::symbol_kind_type symbol_kind_type;
+
+ /// The number of tokens.
+ static const symbol_kind_type YYNTOKENS = symbol_kind::YYNTOKENS;
+
+ /// A complete symbol.
+ ///
+ /// Expects its Base type to provide access to the symbol kind
+ /// via kind ().
+ ///
+ /// Provide access to semantic value and location.
+ template <typename Base>
+ struct basic_symbol : Base
+ {
+ /// Alias to Base.
+ typedef Base super_type;
+
+ /// Default constructor.
+ basic_symbol () YY_NOEXCEPT
+ : value ()
+ , location ()
+ {}
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Move constructor.
+ basic_symbol (basic_symbol&& that)
+ : Base (std::move (that))
+ , value ()
+ , location (std::move (that.location))
+ {
+ switch (this->kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.move< ElementPtr > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (std::move (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (std::move (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+ }
+#endif
+
+ /// Copy constructor.
+ basic_symbol (const basic_symbol& that);
+
+ /// Constructors for typed symbols.
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, location_type&& l)
+ : Base (t)
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const location_type& l)
+ : Base (t)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, ElementPtr&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const ElementPtr& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, bool&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const bool& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, double&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const double& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, int64_t&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const int64_t& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+#if 201103L <= YY_CPLUSPLUS
+ basic_symbol (typename Base::kind_type t, std::string&& v, location_type&& l)
+ : Base (t)
+ , value (std::move (v))
+ , location (std::move (l))
+ {}
+#else
+ basic_symbol (typename Base::kind_type t, const std::string& v, const location_type& l)
+ : Base (t)
+ , value (v)
+ , location (l)
+ {}
+#endif
+
+ /// Destroy the symbol.
+ ~basic_symbol ()
+ {
+ clear ();
+ }
+
+
+
+ /// Destroy contents, and record that is empty.
+ void clear () YY_NOEXCEPT
+ {
+ // User destructor.
+ symbol_kind_type yykind = this->kind ();
+ basic_symbol<Base>& yysym = *this;
+ (void) yysym;
+ switch (yykind)
+ {
+ default:
+ break;
+ }
+
+ // Value type destructor.
+switch (yykind)
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.template destroy< ElementPtr > ();
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.template destroy< bool > ();
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.template destroy< double > ();
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.template destroy< int64_t > ();
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.template destroy< std::string > ();
+ break;
+
+ default:
+ break;
+ }
+
+ Base::clear ();
+ }
+
+ /// The user-facing name of this symbol.
+ std::string name () const YY_NOEXCEPT
+ {
+ return NetconfParser::symbol_name (this->kind ());
+ }
+
+ /// Backward compatibility (Bison 3.6).
+ symbol_kind_type type_get () const YY_NOEXCEPT;
+
+ /// Whether empty.
+ bool empty () const YY_NOEXCEPT;
+
+ /// Destructive move, \a s is emptied into this.
+ void move (basic_symbol& s);
+
+ /// The semantic value.
+ value_type value;
+
+ /// The location.
+ location_type location;
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Assignment operator.
+ basic_symbol& operator= (const basic_symbol& that);
+#endif
+ };
+
+ /// Type access provider for token (enum) based symbols.
+ struct by_kind
+ {
+ /// The symbol kind as needed by the constructor.
+ typedef token_kind_type kind_type;
+
+ /// Default constructor.
+ by_kind () YY_NOEXCEPT;
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Move constructor.
+ by_kind (by_kind&& that) YY_NOEXCEPT;
+#endif
+
+ /// Copy constructor.
+ by_kind (const by_kind& that) YY_NOEXCEPT;
+
+ /// Constructor from (external) token numbers.
+ by_kind (kind_type t) YY_NOEXCEPT;
+
+
+
+ /// Record that this symbol is empty.
+ void clear () YY_NOEXCEPT;
+
+ /// Steal the symbol kind from \a that.
+ void move (by_kind& that);
+
+ /// The (internal) type number (corresponding to \a type).
+ /// \a empty when empty.
+ symbol_kind_type kind () const YY_NOEXCEPT;
+
+ /// Backward compatibility (Bison 3.6).
+ symbol_kind_type type_get () const YY_NOEXCEPT;
+
+ /// The symbol kind.
+ /// \a S_YYEMPTY when empty.
+ symbol_kind_type kind_;
+ };
+
+ /// Backward compatibility for a private implementation detail (Bison 3.6).
+ typedef by_kind by_type;
+
+ /// "External" symbols: returned by the scanner.
+ struct symbol_type : basic_symbol<by_kind>
+ {
+ /// Superclass.
+ typedef basic_symbol<by_kind> super_type;
+
+ /// Empty symbol.
+ symbol_type () YY_NOEXCEPT {}
+
+ /// Constructor for valueless symbols, and symbols from each type.
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, location_type l)
+ : super_type (token_kind_type (tok), std::move (l))
+#else
+ symbol_type (int tok, const location_type& l)
+ : super_type (token_kind_type (tok), l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ NETCONF__ASSERT (tok == token::TOKEN_END
+ || (token::TOKEN_NETCONF_error <= tok && tok <= token::TOKEN_START_SUB_NETCONF));
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, bool v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const bool& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ NETCONF__ASSERT (tok == token::TOKEN_BOOLEAN);
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, double v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const double& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ NETCONF__ASSERT (tok == token::TOKEN_FLOAT);
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, int64_t v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const int64_t& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ NETCONF__ASSERT (tok == token::TOKEN_INTEGER);
+#endif
+ }
+#if 201103L <= YY_CPLUSPLUS
+ symbol_type (int tok, std::string v, location_type l)
+ : super_type (token_kind_type (tok), std::move (v), std::move (l))
+#else
+ symbol_type (int tok, const std::string& v, const location_type& l)
+ : super_type (token_kind_type (tok), v, l)
+#endif
+ {
+#if !defined _MSC_VER || defined __clang__
+ NETCONF__ASSERT (tok == token::TOKEN_STRING);
+#endif
+ }
+ };
+
+ /// Build a parser object.
+ NetconfParser (isc::netconf::ParserContext& ctx_yyarg);
+ virtual ~NetconfParser ();
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Non copyable.
+ NetconfParser (const NetconfParser&) = delete;
+ /// Non copyable.
+ NetconfParser& operator= (const NetconfParser&) = delete;
+#endif
+
+ /// Parse. An alias for parse ().
+ /// \returns 0 iff parsing succeeded.
+ int operator() ();
+
+ /// Parse.
+ /// \returns 0 iff parsing succeeded.
+ virtual int parse ();
+
+#if NETCONF_DEBUG
+ /// The current debugging stream.
+ std::ostream& debug_stream () const YY_ATTRIBUTE_PURE;
+ /// Set the current debugging stream.
+ void set_debug_stream (std::ostream &);
+
+ /// Type for debugging levels.
+ typedef int debug_level_type;
+ /// The current debugging level.
+ debug_level_type debug_level () const YY_ATTRIBUTE_PURE;
+ /// Set the current debugging level.
+ void set_debug_level (debug_level_type l);
+#endif
+
+ /// Report a syntax error.
+ /// \param loc where the syntax error is found.
+ /// \param msg a description of the syntax error.
+ virtual void error (const location_type& loc, const std::string& msg);
+
+ /// Report a syntax error.
+ void error (const syntax_error& err);
+
+ /// The user-facing name of the symbol whose (internal) number is
+ /// YYSYMBOL. No bounds checking.
+ static std::string symbol_name (symbol_kind_type yysymbol);
+
+ // Implementation of make_symbol for each token kind.
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_END (location_type l)
+ {
+ return symbol_type (token::TOKEN_END, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_END (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_END, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_NETCONF_error (location_type l)
+ {
+ return symbol_type (token::TOKEN_NETCONF_error, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_NETCONF_error (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_NETCONF_error, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_NETCONF_UNDEF (location_type l)
+ {
+ return symbol_type (token::TOKEN_NETCONF_UNDEF, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_NETCONF_UNDEF (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_NETCONF_UNDEF, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_COMMA (location_type l)
+ {
+ return symbol_type (token::TOKEN_COMMA, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_COMMA (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_COMMA, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_COLON (location_type l)
+ {
+ return symbol_type (token::TOKEN_COLON, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_COLON (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_COLON, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LSQUARE_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_LSQUARE_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LSQUARE_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LSQUARE_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_RSQUARE_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_RSQUARE_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_RSQUARE_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_RSQUARE_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LCURLY_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_LCURLY_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LCURLY_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LCURLY_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_RCURLY_BRACKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_RCURLY_BRACKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_RCURLY_BRACKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_RCURLY_BRACKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_NULL_TYPE (location_type l)
+ {
+ return symbol_type (token::TOKEN_NULL_TYPE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_NULL_TYPE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_NULL_TYPE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_NETCONF (location_type l)
+ {
+ return symbol_type (token::TOKEN_NETCONF, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_NETCONF (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_NETCONF, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_USER_CONTEXT (location_type l)
+ {
+ return symbol_type (token::TOKEN_USER_CONTEXT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_USER_CONTEXT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_USER_CONTEXT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_COMMENT (location_type l)
+ {
+ return symbol_type (token::TOKEN_COMMENT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_COMMENT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_COMMENT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_BOOT_UPDATE (location_type l)
+ {
+ return symbol_type (token::TOKEN_BOOT_UPDATE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_BOOT_UPDATE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_BOOT_UPDATE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SUBSCRIBE_CHANGES (location_type l)
+ {
+ return symbol_type (token::TOKEN_SUBSCRIBE_CHANGES, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SUBSCRIBE_CHANGES (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SUBSCRIBE_CHANGES, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_VALIDATE_CHANGES (location_type l)
+ {
+ return symbol_type (token::TOKEN_VALIDATE_CHANGES, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_VALIDATE_CHANGES (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_VALIDATE_CHANGES, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_MANAGED_SERVERS (location_type l)
+ {
+ return symbol_type (token::TOKEN_MANAGED_SERVERS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_MANAGED_SERVERS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_MANAGED_SERVERS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_DHCP4_SERVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_DHCP4_SERVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_DHCP4_SERVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_DHCP4_SERVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_DHCP6_SERVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_DHCP6_SERVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_DHCP6_SERVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_DHCP6_SERVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_D2_SERVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_D2_SERVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_D2_SERVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_D2_SERVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_CA_SERVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_CA_SERVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_CA_SERVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_CA_SERVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_MODEL (location_type l)
+ {
+ return symbol_type (token::TOKEN_MODEL, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_MODEL (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_MODEL, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_CONTROL_SOCKET (location_type l)
+ {
+ return symbol_type (token::TOKEN_CONTROL_SOCKET, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_CONTROL_SOCKET (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_CONTROL_SOCKET, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SOCKET_TYPE (location_type l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_TYPE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SOCKET_TYPE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_TYPE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_UNIX (location_type l)
+ {
+ return symbol_type (token::TOKEN_UNIX, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_UNIX (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_UNIX, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_HTTP (location_type l)
+ {
+ return symbol_type (token::TOKEN_HTTP, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_HTTP (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_HTTP, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_STDOUT (location_type l)
+ {
+ return symbol_type (token::TOKEN_STDOUT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_STDOUT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_STDOUT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SOCKET_NAME (location_type l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_NAME, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SOCKET_NAME (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_NAME, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SOCKET_URL (location_type l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_URL, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SOCKET_URL (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SOCKET_URL, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_HOOKS_LIBRARIES (location_type l)
+ {
+ return symbol_type (token::TOKEN_HOOKS_LIBRARIES, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_HOOKS_LIBRARIES (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_HOOKS_LIBRARIES, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LIBRARY (location_type l)
+ {
+ return symbol_type (token::TOKEN_LIBRARY, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LIBRARY (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LIBRARY, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_PARAMETERS (location_type l)
+ {
+ return symbol_type (token::TOKEN_PARAMETERS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_PARAMETERS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_PARAMETERS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_LOGGERS (location_type l)
+ {
+ return symbol_type (token::TOKEN_LOGGERS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_LOGGERS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_LOGGERS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_NAME (location_type l)
+ {
+ return symbol_type (token::TOKEN_NAME, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_NAME (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_NAME, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_OUTPUT_OPTIONS (location_type l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT_OPTIONS, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_OUTPUT_OPTIONS (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT_OPTIONS, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_OUTPUT (location_type l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_OUTPUT (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_OUTPUT, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_DEBUGLEVEL (location_type l)
+ {
+ return symbol_type (token::TOKEN_DEBUGLEVEL, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_DEBUGLEVEL (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_DEBUGLEVEL, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_SEVERITY (location_type l)
+ {
+ return symbol_type (token::TOKEN_SEVERITY, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_SEVERITY (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_SEVERITY, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_FLUSH (location_type l)
+ {
+ return symbol_type (token::TOKEN_FLUSH, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_FLUSH (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_FLUSH, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_MAXSIZE (location_type l)
+ {
+ return symbol_type (token::TOKEN_MAXSIZE, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_MAXSIZE (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_MAXSIZE, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_MAXVER (location_type l)
+ {
+ return symbol_type (token::TOKEN_MAXVER, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_MAXVER (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_MAXVER, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_PATTERN (location_type l)
+ {
+ return symbol_type (token::TOKEN_PATTERN, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_PATTERN (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_PATTERN, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_START_JSON (location_type l)
+ {
+ return symbol_type (token::TOKEN_START_JSON, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_START_JSON (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_START_JSON, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_START_NETCONF (location_type l)
+ {
+ return symbol_type (token::TOKEN_START_NETCONF, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_START_NETCONF (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_START_NETCONF, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_START_SUB_NETCONF (location_type l)
+ {
+ return symbol_type (token::TOKEN_START_SUB_NETCONF, std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_START_SUB_NETCONF (const location_type& l)
+ {
+ return symbol_type (token::TOKEN_START_SUB_NETCONF, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_STRING (std::string v, location_type l)
+ {
+ return symbol_type (token::TOKEN_STRING, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_STRING (const std::string& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_STRING, v, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_INTEGER (int64_t v, location_type l)
+ {
+ return symbol_type (token::TOKEN_INTEGER, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_INTEGER (const int64_t& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_INTEGER, v, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_FLOAT (double v, location_type l)
+ {
+ return symbol_type (token::TOKEN_FLOAT, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_FLOAT (const double& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_FLOAT, v, l);
+ }
+#endif
+#if 201103L <= YY_CPLUSPLUS
+ static
+ symbol_type
+ make_BOOLEAN (bool v, location_type l)
+ {
+ return symbol_type (token::TOKEN_BOOLEAN, std::move (v), std::move (l));
+ }
+#else
+ static
+ symbol_type
+ make_BOOLEAN (const bool& v, const location_type& l)
+ {
+ return symbol_type (token::TOKEN_BOOLEAN, v, l);
+ }
+#endif
+
+
+ class context
+ {
+ public:
+ context (const NetconfParser& yyparser, const symbol_type& yyla);
+ const symbol_type& lookahead () const YY_NOEXCEPT { return yyla_; }
+ symbol_kind_type token () const YY_NOEXCEPT { return yyla_.kind (); }
+ const location_type& location () const YY_NOEXCEPT { return yyla_.location; }
+
+ /// Put in YYARG at most YYARGN of the expected tokens, and return the
+ /// number of tokens stored in YYARG. If YYARG is null, return the
+ /// number of expected tokens (guaranteed to be less than YYNTOKENS).
+ int expected_tokens (symbol_kind_type yyarg[], int yyargn) const;
+
+ private:
+ const NetconfParser& yyparser_;
+ const symbol_type& yyla_;
+ };
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Non copyable.
+ NetconfParser (const NetconfParser&);
+ /// Non copyable.
+ NetconfParser& operator= (const NetconfParser&);
+#endif
+
+
+ /// Stored state numbers (used for stacks).
+ typedef short state_type;
+
+ /// The arguments of the error message.
+ int yy_syntax_error_arguments_ (const context& yyctx,
+ symbol_kind_type yyarg[], int yyargn) const;
+
+ /// Generate an error message.
+ /// \param yyctx the context in which the error occurred.
+ virtual std::string yysyntax_error_ (const context& yyctx) const;
+ /// Compute post-reduction state.
+ /// \param yystate the current state
+ /// \param yysym the nonterminal to push on the stack
+ static state_type yy_lr_goto_state_ (state_type yystate, int yysym);
+
+ /// Whether the given \c yypact_ value indicates a defaulted state.
+ /// \param yyvalue the value to check
+ static bool yy_pact_value_is_default_ (int yyvalue) YY_NOEXCEPT;
+
+ /// Whether the given \c yytable_ value indicates a syntax error.
+ /// \param yyvalue the value to check
+ static bool yy_table_value_is_error_ (int yyvalue) YY_NOEXCEPT;
+
+ static const signed char yypact_ninf_;
+ static const signed char yytable_ninf_;
+
+ /// Convert a scanner token kind \a t to a symbol kind.
+ /// In theory \a t should be a token_kind_type, but character literals
+ /// are valid, yet not members of the token_kind_type enum.
+ static symbol_kind_type yytranslate_ (int t) YY_NOEXCEPT;
+
+ /// Convert the symbol name \a n to a form suitable for a diagnostic.
+ static std::string yytnamerr_ (const char *yystr);
+
+ /// For a symbol, its name in clear.
+ static const char* const yytname_[];
+
+
+ // Tables.
+ // YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ // STATE-NUM.
+ static const short yypact_[];
+
+ // YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+ // Performed when YYTABLE does not specify something else to do. Zero
+ // means the default is an error.
+ static const unsigned char yydefact_[];
+
+ // YYPGOTO[NTERM-NUM].
+ static const signed char yypgoto_[];
+
+ // YYDEFGOTO[NTERM-NUM].
+ static const short yydefgoto_[];
+
+ // YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If
+ // positive, shift that token. If negative, reduce the rule whose
+ // number is the opposite. If YYTABLE_NINF, syntax error.
+ static const short yytable_[];
+
+ static const short yycheck_[];
+
+ // YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
+ // state STATE-NUM.
+ static const unsigned char yystos_[];
+
+ // YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.
+ static const unsigned char yyr1_[];
+
+ // YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.
+ static const signed char yyr2_[];
+
+
+#if NETCONF_DEBUG
+ // YYRLINE[YYN] -- Source line where rule number YYN was defined.
+ static const short yyrline_[];
+ /// Report on the debug stream that the rule \a r is going to be reduced.
+ virtual void yy_reduce_print_ (int r) const;
+ /// Print the state stack on the debug stream.
+ virtual void yy_stack_print_ () const;
+
+ /// Debugging level.
+ int yydebug_;
+ /// Debug stream.
+ std::ostream* yycdebug_;
+
+ /// \brief Display a symbol kind, value and location.
+ /// \param yyo The output stream.
+ /// \param yysym The symbol.
+ template <typename Base>
+ void yy_print_ (std::ostream& yyo, const basic_symbol<Base>& yysym) const;
+#endif
+
+ /// \brief Reclaim the memory associated to a symbol.
+ /// \param yymsg Why this token is reclaimed.
+ /// If null, print nothing.
+ /// \param yysym The symbol.
+ template <typename Base>
+ void yy_destroy_ (const char* yymsg, basic_symbol<Base>& yysym) const;
+
+ private:
+ /// Type access provider for state based symbols.
+ struct by_state
+ {
+ /// Default constructor.
+ by_state () YY_NOEXCEPT;
+
+ /// The symbol kind as needed by the constructor.
+ typedef state_type kind_type;
+
+ /// Constructor.
+ by_state (kind_type s) YY_NOEXCEPT;
+
+ /// Copy constructor.
+ by_state (const by_state& that) YY_NOEXCEPT;
+
+ /// Record that this symbol is empty.
+ void clear () YY_NOEXCEPT;
+
+ /// Steal the symbol kind from \a that.
+ void move (by_state& that);
+
+ /// The symbol kind (corresponding to \a state).
+ /// \a symbol_kind::S_YYEMPTY when empty.
+ symbol_kind_type kind () const YY_NOEXCEPT;
+
+ /// The state number used to denote an empty symbol.
+ /// We use the initial state, as it does not have a value.
+ enum { empty_state = 0 };
+
+ /// The state.
+ /// \a empty when empty.
+ state_type state;
+ };
+
+ /// "Internal" symbol: element of the stack.
+ struct stack_symbol_type : basic_symbol<by_state>
+ {
+ /// Superclass.
+ typedef basic_symbol<by_state> super_type;
+ /// Construct an empty symbol.
+ stack_symbol_type ();
+ /// Move or copy construction.
+ stack_symbol_type (YY_RVREF (stack_symbol_type) that);
+ /// Steal the contents from \a sym to build this.
+ stack_symbol_type (state_type s, YY_MOVE_REF (symbol_type) sym);
+#if YY_CPLUSPLUS < 201103L
+ /// Assignment, needed by push_back by some old implementations.
+ /// Moves the contents of that.
+ stack_symbol_type& operator= (stack_symbol_type& that);
+
+ /// Assignment, needed by push_back by other implementations.
+ /// Needed by some other old implementations.
+ stack_symbol_type& operator= (const stack_symbol_type& that);
+#endif
+ };
+
+ /// A stack with random access from its top.
+ template <typename T, typename S = std::vector<T> >
+ class stack
+ {
+ public:
+ // Hide our reversed order.
+ typedef typename S::iterator iterator;
+ typedef typename S::const_iterator const_iterator;
+ typedef typename S::size_type size_type;
+ typedef typename std::ptrdiff_t index_type;
+
+ stack (size_type n = 200) YY_NOEXCEPT
+ : seq_ (n)
+ {}
+
+#if 201103L <= YY_CPLUSPLUS
+ /// Non copyable.
+ stack (const stack&) = delete;
+ /// Non copyable.
+ stack& operator= (const stack&) = delete;
+#endif
+
+ /// Random access.
+ ///
+ /// Index 0 returns the topmost element.
+ const T&
+ operator[] (index_type i) const
+ {
+ return seq_[size_type (size () - 1 - i)];
+ }
+
+ /// Random access.
+ ///
+ /// Index 0 returns the topmost element.
+ T&
+ operator[] (index_type i)
+ {
+ return seq_[size_type (size () - 1 - i)];
+ }
+
+ /// Steal the contents of \a t.
+ ///
+ /// Close to move-semantics.
+ void
+ push (YY_MOVE_REF (T) t)
+ {
+ seq_.push_back (T ());
+ operator[] (0).move (t);
+ }
+
+ /// Pop elements from the stack.
+ void
+ pop (std::ptrdiff_t n = 1) YY_NOEXCEPT
+ {
+ for (; 0 < n; --n)
+ seq_.pop_back ();
+ }
+
+ /// Pop all elements from the stack.
+ void
+ clear () YY_NOEXCEPT
+ {
+ seq_.clear ();
+ }
+
+ /// Number of elements on the stack.
+ index_type
+ size () const YY_NOEXCEPT
+ {
+ return index_type (seq_.size ());
+ }
+
+ /// Iterator on top of the stack (going downwards).
+ const_iterator
+ begin () const YY_NOEXCEPT
+ {
+ return seq_.begin ();
+ }
+
+ /// Bottom of the stack.
+ const_iterator
+ end () const YY_NOEXCEPT
+ {
+ return seq_.end ();
+ }
+
+ /// Present a slice of the top of a stack.
+ class slice
+ {
+ public:
+ slice (const stack& stack, index_type range) YY_NOEXCEPT
+ : stack_ (stack)
+ , range_ (range)
+ {}
+
+ const T&
+ operator[] (index_type i) const
+ {
+ return stack_[range_ - i];
+ }
+
+ private:
+ const stack& stack_;
+ index_type range_;
+ };
+
+ private:
+#if YY_CPLUSPLUS < 201103L
+ /// Non copyable.
+ stack (const stack&);
+ /// Non copyable.
+ stack& operator= (const stack&);
+#endif
+ /// The wrapped container.
+ S seq_;
+ };
+
+
+ /// Stack type.
+ typedef stack<stack_symbol_type> stack_type;
+
+ /// The stack.
+ stack_type yystack_;
+
+ /// Push a new state on the stack.
+ /// \param m a debug message to display
+ /// if null, no trace is output.
+ /// \param sym the symbol
+ /// \warning the contents of \a s.value is stolen.
+ void yypush_ (const char* m, YY_MOVE_REF (stack_symbol_type) sym);
+
+ /// Push a new look ahead token on the state on the stack.
+ /// \param m a debug message to display
+ /// if null, no trace is output.
+ /// \param s the state
+ /// \param sym the symbol (for its value and location).
+ /// \warning the contents of \a sym.value is stolen.
+ void yypush_ (const char* m, state_type s, YY_MOVE_REF (symbol_type) sym);
+
+ /// Pop \a n symbols from the stack.
+ void yypop_ (int n = 1) YY_NOEXCEPT;
+
+ /// Constants.
+ enum
+ {
+ yylast_ = 221, ///< Last index in yytable_.
+ yynnts_ = 100, ///< Number of nonterminal symbols.
+ yyfinal_ = 8 ///< Termination state number.
+ };
+
+
+ // User arguments.
+ isc::netconf::ParserContext& ctx;
+
+ };
+
+ inline
+ NetconfParser::symbol_kind_type
+ NetconfParser::yytranslate_ (int t) YY_NOEXCEPT
+ {
+ // YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to
+ // TOKEN-NUM as returned by yylex.
+ static
+ const signed char
+ translate_table[] =
+ {
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
+ 45, 46, 47, 48
+ };
+ // Last valid token kind.
+ const int code_max = 303;
+
+ if (t <= 0)
+ return symbol_kind::S_YYEOF;
+ else if (t <= code_max)
+ return static_cast <symbol_kind_type> (translate_table[t]);
+ else
+ return symbol_kind::S_YYUNDEF;
+ }
+
+ // basic_symbol.
+ template <typename Base>
+ NetconfParser::basic_symbol<Base>::basic_symbol (const basic_symbol& that)
+ : Base (that)
+ , value ()
+ , location (that.location)
+ {
+ switch (this->kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.copy< ElementPtr > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.copy< bool > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.copy< double > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.copy< int64_t > (YY_MOVE (that.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.copy< std::string > (YY_MOVE (that.value));
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+
+
+
+ template <typename Base>
+ NetconfParser::symbol_kind_type
+ NetconfParser::basic_symbol<Base>::type_get () const YY_NOEXCEPT
+ {
+ return this->kind ();
+ }
+
+
+ template <typename Base>
+ bool
+ NetconfParser::basic_symbol<Base>::empty () const YY_NOEXCEPT
+ {
+ return this->kind () == symbol_kind::S_YYEMPTY;
+ }
+
+ template <typename Base>
+ void
+ NetconfParser::basic_symbol<Base>::move (basic_symbol& s)
+ {
+ super_type::move (s);
+ switch (this->kind ())
+ {
+ case symbol_kind::S_value: // value
+ case symbol_kind::S_map_value: // map_value
+ case symbol_kind::S_socket_type_value: // socket_type_value
+ value.move< ElementPtr > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_BOOLEAN: // "boolean"
+ value.move< bool > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_FLOAT: // "floating point"
+ value.move< double > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_INTEGER: // "integer"
+ value.move< int64_t > (YY_MOVE (s.value));
+ break;
+
+ case symbol_kind::S_STRING: // "constant string"
+ value.move< std::string > (YY_MOVE (s.value));
+ break;
+
+ default:
+ break;
+ }
+
+ location = YY_MOVE (s.location);
+ }
+
+ // by_kind.
+ inline
+ NetconfParser::by_kind::by_kind () YY_NOEXCEPT
+ : kind_ (symbol_kind::S_YYEMPTY)
+ {}
+
+#if 201103L <= YY_CPLUSPLUS
+ inline
+ NetconfParser::by_kind::by_kind (by_kind&& that) YY_NOEXCEPT
+ : kind_ (that.kind_)
+ {
+ that.clear ();
+ }
+#endif
+
+ inline
+ NetconfParser::by_kind::by_kind (const by_kind& that) YY_NOEXCEPT
+ : kind_ (that.kind_)
+ {}
+
+ inline
+ NetconfParser::by_kind::by_kind (token_kind_type t) YY_NOEXCEPT
+ : kind_ (yytranslate_ (t))
+ {}
+
+
+
+ inline
+ void
+ NetconfParser::by_kind::clear () YY_NOEXCEPT
+ {
+ kind_ = symbol_kind::S_YYEMPTY;
+ }
+
+ inline
+ void
+ NetconfParser::by_kind::move (by_kind& that)
+ {
+ kind_ = that.kind_;
+ that.clear ();
+ }
+
+ inline
+ NetconfParser::symbol_kind_type
+ NetconfParser::by_kind::kind () const YY_NOEXCEPT
+ {
+ return kind_;
+ }
+
+
+ inline
+ NetconfParser::symbol_kind_type
+ NetconfParser::by_kind::type_get () const YY_NOEXCEPT
+ {
+ return this->kind ();
+ }
+
+
+#line 14 "netconf_parser.yy"
+} } // isc::netconf
+#line 2383 "netconf_parser.h"
+
+
+
+
+#endif // !YY_NETCONF_NETCONF_PARSER_H_INCLUDED
diff --git a/src/bin/netconf/netconf_parser.yy b/src/bin/netconf/netconf_parser.yy
new file mode 100644
index 0000000..10048de
--- /dev/null
+++ b/src/bin/netconf/netconf_parser.yy
@@ -0,0 +1,755 @@
+/* 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/. */
+
+%skeleton "lalr1.cc" /* -*- C++ -*- */
+%require "3.3.0"
+%defines
+%define api.parser.class {NetconfParser}
+%define api.prefix {netconf_}
+%define api.token.constructor
+%define api.value.type variant
+%define api.namespace {isc::netconf}
+%define parse.assert
+%code requires
+{
+#include <string>
+#include <cc/data.h>
+#include <boost/lexical_cast.hpp>
+#include <netconf/parser_context_decl.h>
+
+using namespace isc::netconf;
+using namespace isc::data;
+using namespace std;
+}
+// The parsing context.
+%param { isc::netconf::ParserContext& ctx }
+%locations
+%define parse.trace
+%define parse.error verbose
+%code
+{
+#include <netconf/parser_context.h>
+}
+
+
+%define api.token.prefix {TOKEN_}
+// Tokens in an order which makes sense and related to the intended use.
+// Actual regexps for tokens are defined in netconf_lexer.ll.
+%token
+ END 0 "end of file"
+ COMMA ","
+ COLON ":"
+ LSQUARE_BRACKET "["
+ RSQUARE_BRACKET "]"
+ LCURLY_BRACKET "{"
+ RCURLY_BRACKET "}"
+ NULL_TYPE "null"
+
+ NETCONF "Netconf"
+
+ USER_CONTEXT "user-context"
+ COMMENT "comment"
+
+ BOOT_UPDATE "boot-update"
+ SUBSCRIBE_CHANGES "subscribe-changes"
+ VALIDATE_CHANGES "validate-changes"
+
+ MANAGED_SERVERS "managed-servers"
+ DHCP4_SERVER "dhcp4"
+ DHCP6_SERVER "dhcp6"
+ D2_SERVER "d2"
+ CA_SERVER "ca"
+ MODEL "model"
+ CONTROL_SOCKET "control-socket"
+ SOCKET_TYPE "socket-type"
+ UNIX "unix"
+ HTTP "http"
+ STDOUT "stdout"
+ SOCKET_NAME "socket-name"
+ SOCKET_URL "socket-url"
+
+ HOOKS_LIBRARIES "hooks-libraries"
+ LIBRARY "library"
+ PARAMETERS "parameters"
+
+ LOGGERS "loggers"
+ NAME "name"
+ OUTPUT_OPTIONS "output_options"
+ OUTPUT "output"
+ DEBUGLEVEL "debuglevel"
+ SEVERITY "severity"
+ FLUSH "flush"
+ MAXSIZE "maxsize"
+ MAXVER "maxver"
+ PATTERN "pattern"
+
+ // Not real tokens, just a way to signal what the parser is expected to
+ // parse. This define the starting point. It either can be full grammar
+ // (START_NETCONF), part of the grammar related to Netconf (START_SUB_NETCONF)
+ // or can be any valid JSON (START_JSON)
+ START_JSON
+ START_NETCONF
+ START_SUB_NETCONF
+;
+
+%token <std::string> STRING "constant string"
+%token <int64_t> INTEGER "integer"
+%token <double> FLOAT "floating point"
+%token <bool> BOOLEAN "boolean"
+
+%type <ElementPtr> value
+%type <ElementPtr> map_value
+%type <ElementPtr> socket_type_value
+
+%printer { yyoutput << $$; } <*>;
+
+%%
+
+// The whole grammar starts with a map, because the config file
+// consists of Netconf, DhcpX, Logger and DhcpDdns entries in one big { }.
+%start start;
+
+// The starting token can be one of those listed below. Note these are
+// "fake" tokens. They're produced by the lexer before any input text
+// is parsed.
+start: START_JSON { ctx.ctx_ = ctx.NO_KEYWORDS; } json
+ | START_NETCONF { ctx.ctx_ = ctx.CONFIG; } netconf_syntax_map
+ | START_SUB_NETCONF { ctx.ctx_ = ctx.NETCONF; } sub_netconf
+ ;
+
+// This rule defines a "shortcut". Instead of specifying the whole structure
+// expected by full grammar, we can tell the parser to start from content of
+// the Netconf. This is very useful for unit-testing, so we don't need
+// to repeat the outer map and "Netconf" map. We can simply provide
+// the contents of that map.
+sub_netconf: LCURLY_BRACKET {
+ // Parse the Netconf map
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(m);
+} global_params RCURLY_BRACKET {
+ // parsing completed
+};
+
+// --- generic JSON parser -----------------------------------------------------
+
+// json expression can be a value. What value means is defined below.
+json: value {
+ // Push back the JSON value on the stack
+ ctx.stack_.push_back($1);
+};
+
+// Rules for value. This can be one of the primary types allowed in JSON.
+value: INTEGER { $$ = ElementPtr(new IntElement($1, ctx.loc2pos(@1))); }
+ | FLOAT { $$ = ElementPtr(new DoubleElement($1, ctx.loc2pos(@1))); }
+ | BOOLEAN { $$ = ElementPtr(new BoolElement($1, ctx.loc2pos(@1))); }
+ | STRING { $$ = ElementPtr(new StringElement($1, ctx.loc2pos(@1))); }
+ | NULL_TYPE { $$ = ElementPtr(new NullElement(ctx.loc2pos(@1))); }
+ | map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
+ | list_generic { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); }
+ ;
+
+// Rule for map. It will start with {, have some content and will end with }.
+map: LCURLY_BRACKET {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(m);
+} map_content RCURLY_BRACKET {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+};
+
+map_value: map { $$ = ctx.stack_.back(); ctx.stack_.pop_back(); };
+
+// Rule for map content. In some cases it is allowed to have an empty map,
+// so we should say that explicitly. In most cases, though, there will
+// be some actual content inside. That's defined by not_empty_map
+map_content: %empty // empty map
+ | not_empty_map
+ ;
+
+// Rule for content of the map. It can have one of two formats:
+// 1) string: value
+// 2) non_empty_map , string: value
+// The first case covers a single entry, while the second case
+// covers all longer lists recursively.
+not_empty_map: STRING COLON value {
+ // map containing a single entry
+ ctx.unique($1, ctx.loc2pos(@1));
+ ctx.stack_.back()->set($1, $3);
+ }
+ | not_empty_map COMMA STRING COLON value {
+ // map consisting of a shorter map followed by
+ // comma and string:value
+ ctx.unique($3, ctx.loc2pos(@3));
+ ctx.stack_.back()->set($3, $5);
+ }
+ | not_empty_map COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+list_generic: LSQUARE_BRACKET {
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(l);
+} list_content RSQUARE_BRACKET {
+};
+
+list_content: %empty // Empty list
+ | not_empty_list
+ ;
+
+not_empty_list: value {
+ // List consisting of a single element.
+ ctx.stack_.back()->add($1);
+ }
+ | not_empty_list COMMA value {
+ // List ending with , and a value.
+ ctx.stack_.back()->add($3);
+ }
+ | not_empty_list COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// --- generic JSON parser ends here -------------------------------------------
+
+// --- syntax checking parser starts here --------------------------------------
+
+// Unknown keyword in a map. This clever rule can be added to any map
+// if you want to have a nice expression printed when unknown (mistyped?)
+// parameter is found.
+unknown_map_entry: STRING COLON {
+ const std::string& where = ctx.contextName();
+ const std::string& keyword = $1;
+ error(@1,
+ "got unexpected keyword \"" + keyword + "\" in " + where + " map.");
+};
+
+// This defines the top-level { } that holds Netconf object.
+netconf_syntax_map: LCURLY_BRACKET {
+ // This code is executed when we're about to start parsing
+ // the content of the map
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.push_back(m);
+} global_object RCURLY_BRACKET {
+ // map parsing completed. If we ever want to do any wrap up
+ // (maybe some sanity checking), this would be the best place
+ // for it.
+};
+
+// This represents the single top level entry, e.g. Netconf.
+global_object: NETCONF {
+ // Let's create a MapElement that will represent it, add it to the
+ // top level map (that's already on the stack) and put the new map
+ // on the stack as well, so child elements will be able to add
+ // themselves to it.
+
+ // Prevent against duplicate.
+ ctx.unique("Netconf", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("Netconf", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.NETCONF);
+} COLON LCURLY_BRACKET global_params RCURLY_BRACKET {
+ // Ok, we're done with parsing Netconf. Let's take the map
+ // off the stack.
+ ctx.stack_.pop_back();
+ ctx.leave();
+}
+ | global_object_comma
+ ;
+
+global_object_comma: global_object COMMA {
+ ctx.warnAboutExtraCommas(@2);
+};
+
+
+global_params: %empty
+ | not_empty_global_params
+ ;
+
+not_empty_global_params: global_param
+ | not_empty_global_params COMMA global_param
+ | not_empty_global_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// These are the parameters that are allowed in the top-level for
+// Netconf.
+global_param: boot_update
+ | subscribe_changes
+ | validate_changes
+ | managed_servers
+ | hooks_libraries
+ | loggers
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+boot_update: BOOT_UPDATE COLON BOOLEAN {
+ ctx.unique("boot-update", ctx.loc2pos(@1));
+ ElementPtr flag(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("boot-update", flag);
+};
+
+subscribe_changes: SUBSCRIBE_CHANGES COLON BOOLEAN {
+ ctx.unique("subscribe-changes", ctx.loc2pos(@1));
+ ElementPtr flag(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("subscribe-changes", flag);
+};
+
+validate_changes: VALIDATE_CHANGES COLON BOOLEAN {
+ ctx.unique("validate-changes", ctx.loc2pos(@1));
+ ElementPtr flag(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("validate-changes", flag);
+};
+
+user_context: USER_CONTEXT {
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON map_value {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context = $4;
+ ConstElementPtr old = parent->get("user-context");
+
+ // Handle already existing user context
+ if (old) {
+ // Check if it was a comment or a duplicate
+ if ((old->size() != 1) || !old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context entries (previous at "
+ << old->getPosition().str() << ")";
+ error(@1, msg.str());
+ }
+ // Merge the comment
+ user_context->set("comment", old->get("comment"));
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+};
+
+comment: COMMENT {
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr parent = ctx.stack_.back();
+ ElementPtr user_context(new MapElement(ctx.loc2pos(@1)));
+ ElementPtr comment(new StringElement($4, ctx.loc2pos(@4)));
+ user_context->set("comment", comment);
+
+ // Handle already existing user context
+ ConstElementPtr old = parent->get("user-context");
+ if (old) {
+ // Check for duplicate comment
+ if (old->contains("comment")) {
+ std::stringstream msg;
+ msg << "duplicate user-context/comment entries (previous at "
+ << old->getPosition().str() << ")";
+ error(@1, msg.str());
+ }
+ // Merge the user context in the comment
+ merge(user_context, old);
+ }
+
+ // Set the user context
+ parent->set("user-context", user_context);
+ ctx.leave();
+};
+
+// --- hooks-libraries ---------------------------------------------------------
+hooks_libraries: HOOKS_LIBRARIES {
+ ctx.unique("hooks-libraries", ctx.loc2pos(@1));
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("hooks-libraries", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.HOOKS_LIBRARIES);
+} COLON LSQUARE_BRACKET hooks_libraries_list RSQUARE_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+hooks_libraries_list: %empty
+ | not_empty_hooks_libraries_list
+ ;
+
+not_empty_hooks_libraries_list: hooks_library
+ | not_empty_hooks_libraries_list COMMA hooks_library
+ | not_empty_hooks_libraries_list COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+hooks_library: LCURLY_BRACKET {
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+} hooks_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+};
+
+hooks_params: hooks_param
+ | hooks_params COMMA hooks_param
+ | hooks_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ | unknown_map_entry
+ ;
+
+hooks_param: library
+ | parameters
+ ;
+
+library: LIBRARY {
+ ctx.unique("library", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr lib(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("library", lib);
+ ctx.leave();
+};
+
+parameters: PARAMETERS {
+ ctx.unique("parameters", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON map_value {
+ ctx.stack_.back()->set("parameters", $4);
+ ctx.leave();
+};
+
+// --- hooks-libraries end here ------------------------------------------------
+
+// --- managed-servers starts here ---------------------------------------------
+managed_servers: MANAGED_SERVERS COLON LCURLY_BRACKET {
+ ctx.unique("managed-servers", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("managed-servers", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.MANAGED_SERVERS);
+} servers_entries RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+servers_entries: %empty
+ | not_empty_servers_entries
+ ;
+
+not_empty_servers_entries: server_entry
+ | not_empty_servers_entries COMMA server_entry
+ | not_empty_servers_entries COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+
+// We currently support four types of servers: DHCPv4, DHCPv6, D2 and CA
+// (even though D2 socket support is not yet merged).
+server_entry: dhcp4_server
+ | dhcp6_server
+ | d2_server
+ | ca_server
+ | unknown_map_entry
+ ;
+
+// That's an entry for dhcp4 server.
+dhcp4_server: DHCP4_SERVER {
+ ctx.unique("dhcp4", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("dhcp4", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+} COLON LCURLY_BRACKET managed_server_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// That's an entry for dhcp6 server.
+dhcp6_server: DHCP6_SERVER {
+ ctx.unique("dhcp6", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("dhcp6", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+} COLON LCURLY_BRACKET managed_server_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// That's an entry for d2 server.
+d2_server: D2_SERVER {
+ ctx.unique("d2", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("d2", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+} COLON LCURLY_BRACKET managed_server_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// That's an entry for ca server.
+ca_server: CA_SERVER {
+ ctx.unique("ca", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("ca", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.SERVER);
+} COLON LCURLY_BRACKET managed_server_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// Server parameters consist of one or more parameters.
+managed_server_params: managed_server_param
+ | managed_server_params COMMA managed_server_param
+ | managed_server_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// We currently support two server parameters: model and control-socket.
+managed_server_param: model
+ | boot_update
+ | subscribe_changes
+ | validate_changes
+ | control_socket
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+// YANG model
+model: MODEL {
+ ctx.unique("model", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr model(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("model", model);
+ ctx.leave();
+};
+
+// Control socket.
+control_socket: CONTROL_SOCKET {
+ ctx.unique("control-socket", ctx.loc2pos(@1));
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("control-socket", m);
+ ctx.stack_.push_back(m);
+ ctx.enter(ctx.CONTROL_SOCKET);
+} COLON LCURLY_BRACKET control_socket_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// control-socket parameters
+control_socket_params: control_socket_param
+ | control_socket_params COMMA control_socket_param
+ | control_socket_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+control_socket_param: socket_type
+ | socket_name
+ | socket_url
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+socket_type: SOCKET_TYPE {
+ ctx.unique("socket-type", ctx.loc2pos(@1));
+ ctx.enter(ctx.SOCKET_TYPE);
+} COLON socket_type_value {
+ ctx.stack_.back()->set("socket-type", $4);
+ ctx.leave();
+};
+
+// We currently allow unix, http and stdout control socket types.
+socket_type_value : UNIX { $$ = ElementPtr(new StringElement("unix", ctx.loc2pos(@1))); }
+ | HTTP { $$ = ElementPtr(new StringElement("http", ctx.loc2pos(@1))); }
+ | STDOUT { $$ = ElementPtr(new StringElement("stdout", ctx.loc2pos(@1))); }
+ ;
+// Unix name.
+socket_name: SOCKET_NAME {
+ ctx.unique("socket-name", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("socket-name", name);
+ ctx.leave();
+};
+
+// HTTP url.
+socket_url: SOCKET_URL {
+ ctx.unique("socket-url", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr url(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("socket-url", url);
+ ctx.leave();
+};
+
+// --- managed-servers end here ------------------------------------------------
+
+// --- Loggers starts here -----------------------------------------------------
+
+loggers: LOGGERS {
+ ctx.unique("loggers", ctx.loc2pos(@1));
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("loggers", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.LOGGERS);
+} COLON LSQUARE_BRACKET loggers_entries RSQUARE_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+// These are the parameters allowed in loggers: either one logger
+// entry or multiple entries separate by commas.
+loggers_entries: logger_entry
+ | loggers_entries COMMA logger_entry
+ | loggers_entries COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+// This defines a single entry defined in loggers.
+logger_entry: LCURLY_BRACKET {
+ ElementPtr l(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->add(l);
+ ctx.stack_.push_back(l);
+} logger_params RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+};
+
+logger_params: logger_param
+ | logger_params COMMA logger_param
+ | logger_params COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+logger_param: name
+ | output_options_list
+ | debuglevel
+ | severity
+ | user_context
+ | comment
+ | unknown_map_entry
+ ;
+
+name: NAME {
+ ctx.unique("name", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr name(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("name", name);
+ ctx.leave();
+};
+
+debuglevel: DEBUGLEVEL COLON INTEGER {
+ ctx.unique("debuglevel", ctx.loc2pos(@1));
+ ElementPtr dl(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("debuglevel", dl);
+};
+
+severity: SEVERITY {
+ ctx.unique("severity", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("severity", sev);
+ ctx.leave();
+};
+
+output_options_list: OUTPUT_OPTIONS {
+ ctx.unique("output_options", ctx.loc2pos(@1));
+ ElementPtr l(new ListElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->set("output_options", l);
+ ctx.stack_.push_back(l);
+ ctx.enter(ctx.OUTPUT_OPTIONS);
+} COLON LSQUARE_BRACKET output_options_list_content RSQUARE_BRACKET {
+ ctx.stack_.pop_back();
+ ctx.leave();
+};
+
+output_options_list_content: output_entry
+ | output_options_list_content COMMA output_entry
+ | output_options_list_content COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+output_entry: LCURLY_BRACKET {
+ ElementPtr m(new MapElement(ctx.loc2pos(@1)));
+ ctx.stack_.back()->add(m);
+ ctx.stack_.push_back(m);
+} output_params_list RCURLY_BRACKET {
+ ctx.stack_.pop_back();
+};
+
+output_params_list: output_params
+ | output_params_list COMMA output_params
+ | output_params_list COMMA {
+ ctx.warnAboutExtraCommas(@2);
+ }
+ ;
+
+output_params: output
+ | flush
+ | maxsize
+ | maxver
+ | pattern
+ ;
+
+output: OUTPUT {
+ ctx.unique("output", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("output", sev);
+ ctx.leave();
+};
+
+flush: FLUSH COLON BOOLEAN {
+ ctx.unique("flush", ctx.loc2pos(@1));
+ ElementPtr flush(new BoolElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("flush", flush);
+};
+
+maxsize: MAXSIZE COLON INTEGER {
+ ctx.unique("maxsize", ctx.loc2pos(@1));
+ ElementPtr maxsize(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("maxsize", maxsize);
+};
+
+maxver: MAXVER COLON INTEGER {
+ ctx.unique("maxver", ctx.loc2pos(@1));
+ ElementPtr maxver(new IntElement($3, ctx.loc2pos(@3)));
+ ctx.stack_.back()->set("maxver", maxver);
+};
+
+pattern: PATTERN {
+ ctx.unique("pattern", ctx.loc2pos(@1));
+ ctx.enter(ctx.NO_KEYWORDS);
+} COLON STRING {
+ ElementPtr sev(new StringElement($4, ctx.loc2pos(@4)));
+ ctx.stack_.back()->set("pattern", sev);
+ ctx.leave();
+};
+
+%%
+
+void
+isc::netconf::NetconfParser::error(const location_type& loc,
+ const std::string& what)
+{
+ ctx.error(loc, what);
+}
diff --git a/src/bin/netconf/netconf_process.cc b/src/bin/netconf/netconf_process.cc
new file mode 100644
index 0000000..f8430dd
--- /dev/null
+++ b/src/bin/netconf/netconf_process.cc
@@ -0,0 +1,99 @@
+// 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/asio_wrapper.h>
+#include <netconf/netconf.h>
+#include <netconf/netconf_process.h>
+#include <netconf/netconf_controller.h>
+#include <netconf/netconf_log.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+#include <cc/command_interpreter.h>
+#include <config/timeouts.h>
+
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+using namespace isc::process;
+
+namespace isc {
+namespace netconf {
+
+NetconfProcess::NetconfProcess(const char* name,
+ const asiolink::IOServicePtr& io_service)
+ : DProcessBase(name, io_service, DCfgMgrBasePtr(new NetconfCfgMgr())) {
+}
+
+void
+NetconfProcess::init() {
+}
+
+void
+NetconfProcess::run() {
+ LOG_INFO(netconf_logger, NETCONF_STARTED).arg(VERSION);
+
+ try {
+ // Call init.
+ agent_.init(getNetconfCfgMgr());
+
+ // Let's process incoming data or expiring timers in a loop until
+ // shutdown condition is detected.
+ while (!shouldShutdown()) {
+ runIO();
+ }
+ stopIOService();
+
+ } catch (const std::exception& ex) {
+ LOG_FATAL(netconf_logger, NETCONF_FAILED).arg(ex.what());
+ try {
+ stopIOService();
+ } catch (...) {
+ // Ignore double errors
+ }
+ isc_throw(DProcessBaseError,
+ "Process run method failed: " << ex.what());
+ }
+
+ LOG_DEBUG(netconf_logger, isc::log::DBGLVL_START_SHUT, NETCONF_RUN_EXIT);
+}
+
+size_t
+NetconfProcess::runIO() {
+ size_t cnt = getIoService()->get_io_service().poll();
+ if (!cnt) {
+ cnt = getIoService()->get_io_service().run_one();
+ }
+ return (cnt);
+}
+
+isc::data::ConstElementPtr
+NetconfProcess::shutdown(isc::data::ConstElementPtr /*args*/) {
+ setShutdownFlag(true);
+ return (isc::config::createAnswer(0, "Netconf is shutting down"));
+}
+
+isc::data::ConstElementPtr
+NetconfProcess::configure(isc::data::ConstElementPtr config_set,
+ bool check_only) {
+ ConstElementPtr answer =
+ getCfgMgr()->simpleParseConfig(config_set, check_only);
+ int rcode = 0;
+ config::parseAnswer(rcode, answer);
+ return (answer);
+}
+
+NetconfCfgMgrPtr
+NetconfProcess::getNetconfCfgMgr() {
+ return (boost::dynamic_pointer_cast<NetconfCfgMgr>(getCfgMgr()));
+}
+
+} // namespace isc::netconf
+} // namespace isc
diff --git a/src/bin/netconf/netconf_process.h b/src/bin/netconf/netconf_process.h
new file mode 100644
index 0000000..e8b24b1
--- /dev/null
+++ b/src/bin/netconf/netconf_process.h
@@ -0,0 +1,106 @@
+// 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/.
+
+#ifndef NETCONF_PROCESS_H
+#define NETCONF_PROCESS_H
+
+#include <netconf/netconf.h>
+#include <process/d_process.h>
+
+#include <vector>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Kea Netconf Application Process
+///
+/// NetconfProcess provides top level application logic for the Netconf,
+/// a process managing Kea servers using YANG / NETCONF.
+///
+/// The Netconf receives YANG configuration change events, converts them
+/// to JSON commands sent to the respective Kea servers.
+class NetconfProcess : public process::DProcessBase {
+public:
+ /// @brief Constructor
+ ///
+ /// @param name name is a text label for the process. Generally used
+ /// in log statements, but otherwise arbitrary.
+ /// @param io_service is the io_service used by the caller for
+ /// asynchronous event handling.
+ NetconfProcess(const char* name, const asiolink::IOServicePtr& io_service);
+
+ /// @brief Destructor
+ virtual ~NetconfProcess() = default;
+
+ /// @brief Initialize the Netconf process.
+ ///
+ /// This is invoked by the controller after command line arguments but
+ /// prior to configuration reception. The base class provides this method
+ /// as a place to perform any derivation-specific initialization steps
+ /// that are inappropriate for the constructor but necessary prior to
+ /// launch.
+ virtual void init();
+
+ /// @brief Implements the process's event loop.
+ ///
+ /// @throw DProcessBaseError if an operational error is encountered.
+ virtual void run();
+
+ /// @brief Initiates the process's shutdown process.
+ ///
+ /// This is last step in the shutdown event callback chain, that is
+ /// intended to notify the process it is to begin its shutdown process.
+ ///
+ /// @param args an Element set of shutdown arguments (if any) that are
+ /// supported by the process derivation.
+ ///
+ /// @return an Element that contains the results of argument processing,
+ /// consisting of an integer status value (0 means successful,
+ /// non-zero means failure), and a string explanation of the outcome.
+ ///
+ /// @throw DProcessBaseError if an operational error is encountered.
+ virtual isc::data::ConstElementPtr
+ shutdown(isc::data::ConstElementPtr args);
+
+ /// @brief Processes the given configuration.
+ ///
+ /// This method may be called multiple times during the process lifetime.
+ /// Certainly once during process startup, and possibly later if the user
+ /// alters configuration. This method must not throw, it should catch any
+ /// processing errors and return a success or failure answer as described
+ /// below.
+ ///
+ /// @param config_set a new configuration (JSON) for the process
+ /// @param check_only true if configuration is to be verified only, not applied
+ /// @return an Element that contains the results of configuration composed
+ /// of an integer status value (0 means successful, non-zero means failure),
+ /// and a string explanation of the outcome.
+ virtual isc::data::ConstElementPtr
+ configure(isc::data::ConstElementPtr config_set,
+ bool check_only = false);
+
+ /// @brief Returns a pointer to the configuration manager.
+ NetconfCfgMgrPtr getNetconfCfgMgr();
+
+private:
+
+ /// @brief Polls all ready handlers and then runs one handler if none
+ /// handlers have been executed as a result of polling.
+ ///
+ /// @return Number of executed handlers.
+ size_t runIO();
+
+ /// @brief Netconf agent.
+ NetconfAgent agent_;
+};
+
+/// @brief Defines a shared pointer to NetconfProcess.
+typedef boost::shared_ptr<NetconfProcess> NetconfProcessPtr;
+
+}; // namespace isc::netconf
+}; // namespace isc
+
+#endif // NETCONF_PROCESS_H
diff --git a/src/bin/netconf/parser_context.cc b/src/bin/netconf/parser_context.cc
new file mode 100644
index 0000000..d84e194
--- /dev/null
+++ b/src/bin/netconf/parser_context.cc
@@ -0,0 +1,200 @@
+// 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/parser_context.h>
+#include <netconf/netconf_parser.h>
+#include <netconf/netconf_log.h>
+#include <exceptions/exceptions.h>
+//#include <cc/dhcp_config_error.h>
+#include <cc/data.h>
+#include <fstream>
+#include <sstream>
+#include <limits>
+
+namespace isc {
+namespace netconf {
+
+ParserContext::ParserContext()
+ : sfile_(0), ctx_(NO_KEYWORDS), trace_scanning_(false), trace_parsing_(false)
+{
+}
+
+ParserContext::~ParserContext()
+{
+}
+
+isc::data::ElementPtr
+ParserContext::parseString(const std::string& str, ParserType parser_type)
+{
+ scanStringBegin(str, parser_type);
+ return (parseCommon());
+}
+
+isc::data::ElementPtr
+ParserContext::parseFile(const std::string& filename, ParserType parser_type) {
+ FILE* f = fopen(filename.c_str(), "r");
+ if (!f) {
+ isc_throw(ParseError, "Unable to open file " << filename);
+ }
+ scanFileBegin(f, filename, parser_type);
+ return (parseCommon());
+}
+
+isc::data::ElementPtr
+ParserContext::parseCommon() {
+ isc::netconf::NetconfParser parser(*this);
+ // Uncomment this to get detailed parser logs.
+ // trace_parsing_ = true;
+ parser.set_debug_level(trace_parsing_);
+ try {
+ int res = parser.parse();
+ if (res != 0) {
+ isc_throw(ParseError, "Parser abort");
+ }
+ scanEnd();
+ }
+ catch (...) {
+ scanEnd();
+ throw;
+ }
+ if (stack_.size() == 1) {
+ return (stack_[0]);
+ } else {
+ isc_throw(ParseError, "Expected exactly one terminal Element expected, found "
+ << stack_.size());
+ }
+}
+
+void
+ParserContext::error(const isc::netconf::location& loc,
+ const std::string& what,
+ size_t pos)
+{
+ if (pos == 0) {
+ isc_throw(ParseError, loc << ": " << what);
+ } else {
+ isc_throw(ParseError, loc << " (near " << pos << "): " << what);
+ }
+}
+
+void
+ParserContext::error(const std::string& what)
+{
+ isc_throw(ParseError, what);
+}
+
+void
+ParserContext::fatal(const std::string& what)
+{
+ isc_throw(ParseError, what);
+}
+
+isc::data::Element::Position
+ParserContext::loc2pos(isc::netconf::location& loc)
+{
+ const std::string& file = *loc.begin.filename;
+ const uint32_t line = loc.begin.line;
+ const uint32_t pos = loc.begin.column;
+ return (isc::data::Element::Position(file, line, pos));
+}
+
+void
+ParserContext::require(const std::string& name,
+ isc::data::Element::Position open_loc,
+ isc::data::Element::Position close_loc)
+{
+ ConstElementPtr value = stack_.back()->get(name);
+ if (!value) {
+ isc_throw(ParseError,
+ "missing parameter '" << name << "' ("
+ << stack_.back()->getPosition() << ") ["
+ << contextName() << " map between "
+ << open_loc << " and " << close_loc << "]");
+ }
+}
+
+void
+ParserContext::unique(const std::string& name,
+ isc::data::Element::Position loc)
+{
+ ConstElementPtr value = stack_.back()->get(name);
+ if (value) {
+ if (ctx_ != NO_KEYWORDS) {
+ isc_throw(ParseError, loc << ": duplicate " << name
+ << " entries in " << contextName()
+ << " map (previous at " << value->getPosition() << ")");
+ } else {
+ isc_throw(ParseError, loc << ": duplicate " << name
+ << " entries in JSON"
+ << " map (previous at " << value->getPosition() << ")");
+ }
+ }
+}
+
+void
+ParserContext::enter(const LexerContext& ctx)
+{
+ cstack_.push_back(ctx_);
+ ctx_ = ctx;
+}
+
+void
+ParserContext::leave()
+{
+ if (cstack_.empty()) {
+ fatal("unbalanced syntactic context");
+ }
+ ctx_ = cstack_.back();
+ cstack_.pop_back();
+}
+
+const std::string
+ParserContext::contextName()
+{
+ switch (ctx_) {
+ case NO_KEYWORDS:
+ return ("__no keywords__");
+ case CONFIG:
+ return ("toplevel");
+ case NETCONF:
+ return ("Netconf");
+ case MANAGED_SERVERS:
+ return ("managed-servers");
+ case SERVER:
+ return ("managed-servers entry");
+ case CONTROL_SOCKET:
+ return ("control-socket");
+ case SOCKET_TYPE:
+ return ("socket-type");
+ case HOOKS_LIBRARIES:
+ return ("hooks-libraries");
+ case LOGGERS:
+ return ("loggers");
+ case OUTPUT_OPTIONS:
+ return ("output-options");
+ default:
+ return ("__unknown__");
+ }
+}
+
+void
+ParserContext::warning(const isc::netconf::location& loc,
+ const std::string& what) {
+ std::ostringstream msg;
+ msg << loc << ": " << what;
+ LOG_WARN(netconf_logger, NETCONF_CONFIG_SYNTAX_WARNING)
+ .arg(msg.str());
+}
+
+void
+ParserContext::warnAboutExtraCommas(const isc::netconf::location& loc) {
+ warning(loc, "Extraneous comma. A piece of configuration may have been omitted.");
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/parser_context.h b/src/bin/netconf/parser_context.h
new file mode 100644
index 0000000..2498f83
--- /dev/null
+++ b/src/bin/netconf/parser_context.h
@@ -0,0 +1,313 @@
+// 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/.
+
+#ifndef NETCONF_PARSER_CONTEXT_H
+#define NETCONF_PARSER_CONTEXT_H
+#include <string>
+#include <map>
+#include <vector>
+#include <netconf/netconf_parser.h>
+#include <netconf/parser_context_decl.h>
+#include <cc/dhcp_config_error.h>
+#include <exceptions/exceptions.h>
+
+// Tell Flex the lexer's prototype ...
+#define YY_DECL isc::netconf::NetconfParser::symbol_type netconf_lex (ParserContext& driver)
+
+// ... and declare it for the parser's sake.
+YY_DECL;
+
+namespace isc {
+namespace netconf {
+
+/// @brief Parser context is a wrapper around flex/bison instances dedicated to
+/// Netconf-agent config file parser.
+///
+/// It follows the same principle as other components. The primary interface
+/// are @ref parseString and @ref parseFile methods. All other methods are
+/// public for testing purposes only. This interface allows parsing the
+/// whole configuration with syntactic checking (which is by far the most
+/// frequent use), but it also allows parsing input as generic JSON or
+/// parse only content of the Netconf-agent object, which is a subset
+/// of full grammar (this will be very useful for unit-tests to not duplicate
+/// unnecessary parts of the config file).
+class ParserContext
+{
+public:
+
+ /// @brief Defines currently supported scopes
+ ///
+ /// NetconfParser is able to parse several types of scope. Usually,
+ /// when it parses a config file, it expects the data to have a map
+ /// with Netconf-agent in it and all the parameters within that map.
+ /// However, sometimes the parser is expected to parse only a subset
+ /// of that information.
+ typedef enum {
+ /// This parser will parse the content as generic JSON.
+ PARSER_JSON,
+
+ /// This parser will expect the content as Netconf config wrapped
+ /// in a map (that's the regular config file)
+ PARSER_NETCONF,
+
+ /// This parser will expect only the content of Netconf.
+ PARSER_SUB_NETCONF
+ } ParserType;
+
+ /// @brief Default constructor.
+ ParserContext();
+
+ /// @brief destructor
+ virtual ~ParserContext();
+
+ /// @brief JSON elements being parsed.
+ std::vector<isc::data::ElementPtr> stack_;
+
+ /// @brief Method called before scanning starts on a string.
+ ///
+ /// @param str string to be parsed
+ /// @param type specifies expected content
+ void scanStringBegin(const std::string& str, ParserType type);
+
+ /// @brief Method called before scanning starts on a file.
+ ///
+ /// @param f stdio FILE pointer
+ /// @param filename file to be parsed
+ /// @param type specifies expected content
+ void scanFileBegin(FILE* f, const std::string& filename, ParserType type);
+
+ /// @brief Method called after the last tokens are scanned.
+ void scanEnd();
+
+ /// @brief Divert input to an include file.
+ ///
+ /// @param filename file to be included
+ void includeFile(const std::string& filename);
+
+ /// @brief Run the parser on the string specified.
+ ///
+ /// This method parses specified string. Depending on the value of
+ /// parser_type, parser may either check only that the input is valid
+ /// JSON, or may do more specific syntax checking. See @ref ParserType
+ /// for supported syntax checkers.
+ ///
+ /// @param str string to be parsed
+ /// @param parser_type specifies expected content (usually NETCONF or generic JSON)
+ /// @return Element structure representing parsed text.
+ isc::data::ElementPtr parseString(const std::string& str,
+ ParserType parser_type);
+
+ /// @brief Run the parser on the file specified.
+ ///
+ /// This method parses specified file. Depending on the value of
+ /// parser_type, parser may either check only that the input is valid
+ /// JSON, or may do more specific syntax checking. See @ref ParserType
+ /// for supported syntax checkers.
+ ///
+ /// @param filename file to be parsed
+ /// @param parser_type specifies expected content (usually PARSER_NETCONF
+ /// or PARSER_JSON)
+ /// @return Element structure representing parsed text.
+ isc::data::ElementPtr parseFile(const std::string& filename,
+ ParserType parser_type);
+
+ /// @brief Error handler
+ ///
+ /// @note The optional position for an error in a string begins by 1
+ /// so the caller should add 1 to the position of the C++ string.
+ ///
+ /// @param loc location within the parsed file where the problem was experienced.
+ /// @param what string explaining the nature of the error.
+ /// @param pos optional position for in string errors.
+ /// @throw ParseError
+ void error(const isc::netconf::location& loc,
+ const std::string& what,
+ size_t pos = 0);
+
+ /// @brief Error handler
+ ///
+ /// This is a simplified error reporting tool for possible future
+ /// cases when the NetconfParser is not able to handle the packet.
+ ///
+ /// @param what string explaining the nature of the error.
+ /// @throw ParseError
+ void error(const std::string& what);
+
+ /// @brief Fatal error handler
+ ///
+ /// This is for should not happen but fatal errors.
+ /// Used by YY_FATAL_ERROR macro so required to be static.
+ ///
+ /// @param what string explaining the nature of the error.
+ /// @throw ParseError
+ static void fatal(const std::string& what);
+
+ /// @brief Converts bison's position to one understandable by isc::data::Element
+ ///
+ /// Convert a bison location into an element position
+ /// (take the begin, the end is lost)
+ ///
+ /// @param loc location in bison format
+ /// @return Position in format accepted by Element
+ isc::data::Element::Position loc2pos(isc::netconf::location& loc);
+
+ /// @brief Check if a required parameter is present
+ ///
+ /// Check if a required parameter is present in the map at the top
+ /// of the stack and raise an error when it is not.
+ ///
+ /// @param name name of the parameter to check
+ /// @param open_loc location of the opening curly bracket
+ /// @param close_loc location of the closing curly bracket
+ /// @throw ParseError
+ void require(const std::string& name,
+ isc::data::Element::Position open_loc,
+ isc::data::Element::Position close_loc);
+
+ /// @brief Check if a parameter is already present
+ ///
+ /// Check if a parameter is already present in the map at the top
+ /// of the stack and raise an error when it is.
+ ///
+ /// @param name name of the parameter to check
+ /// @param loc location of the current parameter
+ /// @throw ParseError
+ void unique(const std::string& name,
+ isc::data::Element::Position loc);
+
+ /// @brief Warning handler
+ ///
+ /// @param loc location within the parsed file where the problem was experienced
+ /// @param what string explaining the nature of the error
+ ///
+ /// @throw ParseError
+ void warning(const isc::netconf::location& loc, const std::string& what);
+
+ /// @brief Warning for extra commas
+ ///
+ /// @param loc location within the parsed file of the extra comma
+ ///
+ /// @throw ParseError
+ void warnAboutExtraCommas(const isc::netconf::location& loc);
+
+ /// @brief Defines syntactic contexts for lexical tie-ins
+ typedef enum {
+ ///< This one is used in pure JSON mode.
+ NO_KEYWORDS,
+
+ ///< Used while parsing top level (that contains Netconf)
+ CONFIG,
+
+ ///< Used while parsing content of Netconf.
+ NETCONF,
+
+ /// Used while parsing Netconf/managed-servers.
+ MANAGED_SERVERS,
+
+ ///< Used while parsing Netconf/managed-servers/*.
+ SERVER,
+
+ ///< Used while parsing Netconf/manages-servers/*/control-socket
+ CONTROL_SOCKET,
+
+ ///< Used while parsing Netconf/managed-servers/*/control-socket/socket-type.
+ SOCKET_TYPE,
+
+ ///< Used while parsing Netconf/hooks-libraries.
+ HOOKS_LIBRARIES,
+
+ ///< Used while parsing Netconf/loggers structures.
+ LOGGERS,
+
+ ///< Used while parsing Netconf/loggers/output_options structures.
+ OUTPUT_OPTIONS
+
+ } LexerContext;
+
+ /// @brief File name
+ std::string file_;
+
+ /// @brief File name stack
+ std::vector<std::string> files_;
+
+ /// @brief Location of the current token
+ ///
+ /// The lexer will keep updating it. This variable will be useful
+ /// for logging errors.
+ isc::netconf::location loc_;
+
+ /// @brief Location stack
+ std::vector<isc::netconf::location> locs_;
+
+ /// @brief Lexer state stack
+ std::vector<struct yy_buffer_state*> states_;
+
+ /// @brief sFile (aka FILE)
+ FILE* sfile_;
+
+ /// @brief sFile (aka FILE) stack
+ ///
+ /// This is a stack of files. Typically there's only one file (the
+ /// one being currently parsed), but there may be more if one
+ /// file includes another.
+ std::vector<FILE*> sfiles_;
+
+ /// @brief Current syntactic context
+ LexerContext ctx_;
+
+ /// @brief Enter a new syntactic context
+ ///
+ /// Entering a new syntactic context is useful in several ways.
+ /// First, it allows the parser to avoid conflicts. Second, it
+ /// allows the lexer to return different tokens depending on
+ /// context (e.g. if "renew-timer" string is detected, the lexer
+ /// will return STRING token if in JSON mode or RENEW_TIMER if
+ /// in DHCP6 mode. Finally, the syntactic context allows the
+ /// error message to be more descriptive if the input string
+ /// does not parse properly. Netconf Agent parser uses simplified
+ /// contexts: either it recognizes keywords (value set to KEYWORDS)
+ /// or not (value set to NO_KEYWORDS).
+ ///
+ /// Make sure to call @ref leave() once the parsing of your
+ /// context is complete.
+ ///
+ /// @param ctx the syntactic context to enter into
+ void enter(const LexerContext& ctx);
+
+ /// @brief Leave a syntactic context
+ ///
+ /// @ref enter() must be called before (when entering a new scope
+ /// or context). Once you complete the parsing, this method
+ /// should be called.
+ ///
+ /// @throw isc::Unexpected if unbalanced (more leave() than enter() calls)
+ void leave();
+
+ /// @brief Get the syntactic context name
+ ///
+ /// @return printable name of the context.
+ const std::string contextName();
+
+ private:
+ /// @brief Flag determining scanner debugging.
+ bool trace_scanning_;
+
+ /// @brief Flag determining parser debugging.
+ bool trace_parsing_;
+
+ /// @brief Syntactic context stack
+ std::vector<LexerContext> cstack_;
+
+ /// @brief Common part of parseXXX
+ ///
+ /// @return Element structure representing parsed text.
+ isc::data::ElementPtr parseCommon();
+};
+
+} // end of isc::netconf namespace
+} // end of isc namespace
+
+#endif
diff --git a/src/bin/netconf/parser_context_decl.h b/src/bin/netconf/parser_context_decl.h
new file mode 100644
index 0000000..07c8c7a
--- /dev/null
+++ b/src/bin/netconf/parser_context_decl.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef NETCONF_CONTEXT_DECL_H
+#define NETCONF_CONTEXT_DECL_H
+
+/// @file netconf/parser_context_decl.h Forward declaration of the ParserContext class
+
+namespace isc {
+namespace netconf {
+
+class ParserContext;
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
+
+#endif
diff --git a/src/bin/netconf/simple_parser.cc b/src/bin/netconf/simple_parser.cc
new file mode 100644
index 0000000..1574ddf
--- /dev/null
+++ b/src/bin/netconf/simple_parser.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 <netconf/simple_parser.h>
+#include <netconf/netconf_config.h>
+#include <cc/data.h>
+#include <cc/dhcp_config_error.h>
+#include <hooks/hooks_manager.h>
+#include <hooks/hooks_parser.h>
+#include <boost/foreach.hpp>
+
+using namespace isc::data;
+
+namespace isc {
+namespace netconf {
+/// @brief This sets of arrays define the default values in various scopes
+/// of the Netconf Configuration.
+///
+/// Each of those is documented in @file netconf/simple_parser.cc. This
+/// is different than most other comments in Kea code. The reason
+/// for placing those in .cc rather than .h file is that it
+/// is expected to be one centralized place to look at for
+/// the default values. This is expected to be looked at also by
+/// people who are not skilled in C or C++, so they may be
+/// confused with the differences between declaration and definition.
+/// As such, there's one file to look at that hopefully is readable
+/// without any C or C++ skills.
+///
+/// @{
+
+/// @brief This table defines default values for global options.
+///
+/// These are global Netconf parameters.
+const SimpleDefaults NetconfSimpleParser::NETCONF_DEFAULTS = {
+ { "boot-update", Element::boolean, "true" },
+ { "subscribe-changes", Element::boolean, "true" },
+ { "validate-changes", Element::boolean, "true" }
+};
+
+/// @brief Supplies defaults for control-socket elements
+const SimpleDefaults NetconfSimpleParser::CTRL_SOCK_DEFAULTS = {
+ { "socket-type", Element::string, "stdout" },
+ { "socket-name", Element::string, "" },
+ { "socket-url" , Element::string, "http://127.0.0.1:8000/" }
+};
+
+/// @brief Supplies defaults for dhcp4 managed server
+const SimpleDefaults NetconfSimpleParser::DHCP4_DEFAULTS = {
+ { "model", Element::string, "kea-dhcp4-server" }
+};
+
+/// @brief Supplies defaults for dhcp6 managed server
+const SimpleDefaults NetconfSimpleParser::DHCP6_DEFAULTS = {
+ { "model", Element::string, "kea-dhcp6-server" }
+};
+
+/// @brief Supplies defaults for d2 managed server
+const SimpleDefaults NetconfSimpleParser::D2_DEFAULTS = {
+ { "model", Element::string, "kea-dhcp-ddns" }
+};
+
+/// @brief Supplies defaults for ca managed server
+const SimpleDefaults NetconfSimpleParser::CA_DEFAULTS = {
+ { "model", Element::string, "kea-ctrl-agent" }
+};
+
+/// @brief List of parameters that can be inherited to managed-servers scope.
+///
+/// Some parameters may be defined on both global (directly in Netconf) and
+/// servers (Netconf/managed-servers/...) scope. If not defined in the
+/// managed-servers scope, the value is being inherited (derived) from
+/// the global scope. This array lists all of such parameters.
+const ParamsList NetconfSimpleParser::INHERIT_TO_SERVERS = {
+ "boot-update",
+ "subscribe-changes",
+ "validate-changes"
+};
+
+/// @}
+
+/// ---------------------------------------------------------------------------
+/// --- end of default values -------------------------------------------------
+/// ---------------------------------------------------------------------------
+
+size_t NetconfSimpleParser::setAllDefaults(const ElementPtr& global) {
+ size_t cnt = 0;
+
+ // Set global defaults first.
+ cnt = setDefaults(global, NETCONF_DEFAULTS);
+
+ ConstElementPtr servers = global->get("managed-servers");
+ if (servers) {
+ for (auto it : servers->mapValue()) {
+ cnt += setServerDefaults(it.first, it.second);
+ }
+ }
+
+ return (cnt);
+}
+
+size_t NetconfSimpleParser::deriveParameters(ConstElementPtr global) {
+ size_t cnt = 0;
+
+ // Now derive global parameters into managed-servers.
+ ConstElementPtr servers = global->get("managed-servers");
+ if (servers) {
+ for (auto it : servers->mapValue()) {
+ ElementPtr mutable_server =
+ boost::const_pointer_cast<Element>(it.second);
+ cnt += SimpleParser::deriveParams(global,
+ mutable_server,
+ INHERIT_TO_SERVERS);
+ }
+ }
+
+ return (cnt);
+}
+
+size_t
+NetconfSimpleParser::setServerDefaults(const std::string name,
+ ConstElementPtr server) {
+ size_t cnt = 0;
+
+ ElementPtr mutable_server =
+ boost::const_pointer_cast<Element>(server);
+ if (name == "dhcp4") {
+ cnt += setDefaults(mutable_server, DHCP4_DEFAULTS);
+ } else if (name == "dhcp6") {
+ cnt += setDefaults(mutable_server, DHCP6_DEFAULTS);
+ } else if (name == "d2") {
+ cnt += setDefaults(mutable_server, D2_DEFAULTS);
+ } else if (name == "ca") {
+ cnt += setDefaults(mutable_server, CA_DEFAULTS);
+ }
+
+ ConstElementPtr ctrl_sock = server->get("control-socket");
+ if (!ctrl_sock) {
+ return (cnt);
+ }
+ ElementPtr mutable_ctrl_sock =
+ boost::const_pointer_cast<Element>(ctrl_sock);
+ cnt += setDefaults(mutable_ctrl_sock, CTRL_SOCK_DEFAULTS);
+
+ return (cnt);
+}
+
+void
+NetconfSimpleParser::parse(const NetconfConfigPtr& ctx,
+ const ConstElementPtr& config,
+ bool check_only) {
+
+ // User context can be done at anytime.
+ ConstElementPtr user_context = config->get("user-context");
+ if (user_context) {
+ ctx->setContext(user_context);
+ }
+
+ // get managed servers.
+ ConstElementPtr servers = config->get("managed-servers");
+ if (servers) {
+ for (auto it : servers->mapValue()) {
+ ServerConfigParser server_parser;
+ CfgServerPtr server = server_parser.parse(it.second);
+ ctx->getCfgServersMap()->insert(make_pair(it.first, server));
+ }
+ }
+
+ // Finally, let's get the hook libs!
+ using namespace isc::hooks;
+ HooksConfig& libraries = ctx->getHooksConfig();
+ ConstElementPtr hooks = config->get("hooks-libraries");
+ if (hooks) {
+ HooksLibrariesParser hooks_parser;
+ hooks_parser.parse(libraries, hooks);
+ libraries.verifyLibraries(hooks->getPosition());
+ }
+
+ if (!check_only) {
+ // This occurs last as if it succeeds, there is no easy way
+ // revert it. As a result, the failure to commit a subsequent
+ // change causes problems when trying to roll back.
+ HooksManager::prepareUnloadLibraries();
+ static_cast<void>(HooksManager::unloadLibraries());
+ libraries.loadLibraries();
+ }
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/simple_parser.h b/src/bin/netconf/simple_parser.h
new file mode 100644
index 0000000..5c9324d
--- /dev/null
+++ b/src/bin/netconf/simple_parser.h
@@ -0,0 +1,74 @@
+// 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 NETCONF_SIMPLE_PARSER_H
+#define NETCONF_SIMPLE_PARSER_H
+
+#include <cc/simple_parser.h>
+#include <netconf/netconf_cfg_mgr.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief SimpleParser specialized for Netconf
+///
+/// This class is a @ref isc::data::SimpleParser dedicated to Netconf.
+/// In particular, it contains all the default values for the whole
+/// netconf defaults.
+///
+/// For the actual values, see @file netconf/simple_parser.cc
+class NetconfSimpleParser : public isc::data::SimpleParser {
+public:
+ /// @brief Sets all defaults for Netconf configuration
+ ///
+ /// This method sets global, option data and option definitions defaults.
+ ///
+ /// @param global scope to be filled in with defaults.
+ /// @return number of default values added
+ static size_t setAllDefaults(const isc::data::ElementPtr& global);
+
+ /// @brief Derives (inherits) all parameters from global to more specific scopes.
+ ///
+ /// This method currently does the following:
+ /// - derives global parameters to managed servers (flags for now)
+ /// @param global scope to be modified if needed
+ /// @return number of default values derived
+ static size_t deriveParameters(isc::data::ConstElementPtr global);
+
+ /// @brief Adds default values to a Managed server entry.
+ ///
+ /// Adds server specific defaults, e.g. the default model.
+ ///
+ /// @param name server name / entry key
+ /// @param server server element / entry value
+ /// @return returns the number of default values added
+ static size_t setServerDefaults(const std::string name,
+ isc::data::ConstElementPtr server);
+
+ /// @brief Parses the netconf configuration
+ ///
+ /// @param ctx - parsed information will be stored here
+ /// @param config - Element tree structure that holds configuration
+ /// @param check_only - if true the configuration is verified only, not applied
+ ///
+ /// @throw ConfigError if any issues are encountered.
+ void parse(const NetconfConfigPtr& ctx,
+ const isc::data::ConstElementPtr& config,
+ bool check_only);
+
+ // see simple_parser.cc for comments for those parameters
+ static const isc::data::SimpleDefaults NETCONF_DEFAULTS;
+ static const isc::data::SimpleDefaults CTRL_SOCK_DEFAULTS;
+ static const isc::data::SimpleDefaults DHCP4_DEFAULTS;
+ static const isc::data::SimpleDefaults DHCP6_DEFAULTS;
+ static const isc::data::SimpleDefaults D2_DEFAULTS;
+ static const isc::data::SimpleDefaults CA_DEFAULTS;
+ static const isc::data::ParamsList INHERIT_TO_SERVERS;
+};
+
+};
+};
+#endif
diff --git a/src/bin/netconf/stdout_control_socket.cc b/src/bin/netconf/stdout_control_socket.cc
new file mode 100644
index 0000000..8dc925b
--- /dev/null
+++ b/src/bin/netconf/stdout_control_socket.cc
@@ -0,0 +1,56 @@
+// 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/.
+
+/// @file stdout_control_socket.cc
+/// Contains the stdout derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/stdout_control_socket.h>
+#include <cc/command_interpreter.h>
+
+using namespace std;
+using namespace isc::config;
+using namespace isc::data;
+
+namespace isc {
+namespace netconf {
+
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::STDOUT>(CfgControlSocketPtr ctrl_sock) {
+ return (StdoutControlSocketPtr(new StdoutControlSocket(ctrl_sock)));
+}
+
+StdoutControlSocket::StdoutControlSocket(CfgControlSocketPtr ctrl_sock)
+ : ControlSocketBase(ctrl_sock), output_(cout) {
+}
+
+StdoutControlSocket::StdoutControlSocket(CfgControlSocketPtr ctrl_sock,
+ ostream& output)
+ : ControlSocketBase(ctrl_sock), output_(output) {
+}
+
+ConstElementPtr
+StdoutControlSocket::configGet(const string& /*service*/) {
+ isc_throw(NotImplemented, "No config-get for stdout control socket");
+}
+
+ConstElementPtr
+StdoutControlSocket::configTest(ConstElementPtr /*config*/,
+ const string& /*service*/) {
+ return (createAnswer());
+}
+
+ConstElementPtr
+StdoutControlSocket::configSet(ConstElementPtr config,
+ const string& /*service*/) {
+ prettyPrint(config, output_);
+ output_ << endl;
+ return (createAnswer());
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/stdout_control_socket.h b/src/bin/netconf/stdout_control_socket.h
new file mode 100644
index 0000000..4bbd82f
--- /dev/null
+++ b/src/bin/netconf/stdout_control_socket.h
@@ -0,0 +1,90 @@
+// 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/.
+
+/// @file stdout_control_socket.h
+/// Contains declarations for stdout control socket communication.
+
+#ifndef STDOUT_CONTROL_SOCKET_H
+#define STDOUT_CONTROL_SOCKET_H
+
+#include <netconf/control_socket.h>
+#include <iostream>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Class for control socket communication over stdout.
+///
+/// This class is the derived class for control socket communication
+/// over stdout.
+/// This class implements config-test (always OK) and config-set.
+class StdoutControlSocket : public ControlSocketBase {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Use std::cout.
+ ///
+ /// @param ctrl_sock The control socket configuration.
+ StdoutControlSocket(CfgControlSocketPtr ctrl_sock);
+
+ /// @brief Destructor (does nothing).
+ virtual ~StdoutControlSocket() = default;
+
+ /// @brief Get configuration.
+ ///
+ /// Call config-get over the control socket.
+ ///
+ /// @param service The target service (ignored).
+ /// @return The JSON element answer of config-get.
+ /// @throw NotImplemented
+ virtual data::ConstElementPtr configGet(const std::string& service);
+
+ /// @brief Test configuration.
+ ///
+ /// Call config-test over the control socket.
+ ///
+ /// @param config The configuration to test (ignored).
+ /// @param service The target service (ignored).
+ /// @return The JSON element answer of config-test (fixed answer).
+ virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+ const std::string& service);
+
+ /// @brief Set configuration.
+ ///
+ /// Call config-set over the control socket.
+ ///
+ /// @param config The configuration to set.
+ /// @param service The target service.
+ /// @return The JSON element answer of config-set (fixed answer).
+ virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+ const std::string& service);
+
+protected:
+ /// @brief Alternative constructor for tests.
+ ///
+ /// @param ctrl_sock The control socket configuration.
+ /// @param output The output stream.
+ StdoutControlSocket(CfgControlSocketPtr ctrl_sock, std::ostream& output);
+
+ /// @brief The output stream (std::cout outside tests).
+ std::ostream& output_;
+};
+
+/// @brief Type definition for the pointer to the @c StdoutControlSocket.
+typedef boost::shared_ptr<StdoutControlSocket> StdoutControlSocketPtr;
+
+/// @brief Factory template specialization for stdout control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a stdout control socket communication object.
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::STDOUT>(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // STDOUT_CONTROL_SOCKET_H
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=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ 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=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ 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
+ }
+}
diff --git a/src/bin/netconf/unix_control_socket.cc b/src/bin/netconf/unix_control_socket.cc
new file mode 100644
index 0000000..51ef5d1
--- /dev/null
+++ b/src/bin/netconf/unix_control_socket.cc
@@ -0,0 +1,97 @@
+// 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/.
+
+/// @file unix_control_socket.cc
+/// Contains the UNIX socket derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/unix_control_socket.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <cc/command_interpreter.h>
+#include <cc/json_feed.h>
+#include <config/client_connection.h>
+#include <config/timeouts.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+
+namespace isc {
+namespace netconf {
+
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::UNIX>(CfgControlSocketPtr ctrl_sock) {
+ return (UnixControlSocketPtr(new UnixControlSocket(ctrl_sock)));
+}
+
+UnixControlSocket::UnixControlSocket(CfgControlSocketPtr ctrl_sock)
+ : ControlSocketBase(ctrl_sock) {
+}
+
+ConstElementPtr
+UnixControlSocket::configGet(const string& /*service*/) {
+ return (sendCommand(createCommand("config-get")));
+}
+
+ConstElementPtr
+UnixControlSocket::configTest(ConstElementPtr config,
+ const string& /*service*/) {
+ return (sendCommand(createCommand("config-test", config)));
+}
+
+ConstElementPtr
+UnixControlSocket::configSet(ConstElementPtr config,
+ const string& /*service*/) {
+ return (sendCommand(createCommand("config-set", config)));
+}
+
+ConstElementPtr
+UnixControlSocket::sendCommand(ConstElementPtr command) {
+ // We are using our own IO service because this method is synchronous.
+ IOServicePtr io_service(new IOService());
+ ClientConnection conn(*io_service);
+ boost::system::error_code received_ec;
+ ConstJSONFeedPtr received_feed;
+
+ conn.start(ClientConnection::SocketPath(getName()),
+ ClientConnection::ControlCommand(command->toWire()),
+ [&io_service, &received_ec, &received_feed]
+ (const boost::system::error_code& ec, ConstJSONFeedPtr feed) {
+ // Capture error code and parsed data.
+ received_ec = ec;
+ received_feed = feed;
+ // Got the IO service so stop IO service. This causes to
+ // stop IO service when all handlers have been invoked.
+ io_service->stopWork();
+ },
+ ClientConnection::Timeout(TIMEOUT_AGENT_FORWARD_COMMAND));
+
+ // Perform this synchronously.
+ io_service->run();
+
+ if (received_ec) {
+ // Got an error.
+ isc_throw(ControlSocketError, "communication error: "
+ << received_ec.message());
+ }
+
+ if (!received_feed) {
+ // Failed to get the answer.
+ isc_throw(ControlSocketError, "empty response");
+ }
+
+ try {
+ return (received_feed->toElement());
+ } catch (const std::exception& ex) {
+ isc_throw(ControlSocketError, "unparsable response: " << ex.what());
+ }
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/unix_control_socket.h b/src/bin/netconf/unix_control_socket.h
new file mode 100644
index 0000000..538ae17
--- /dev/null
+++ b/src/bin/netconf/unix_control_socket.h
@@ -0,0 +1,89 @@
+// 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/.
+
+/// @file unix_control_socket.h
+/// Contains declarations for UNIX control socket communication.
+
+#ifndef UNIX_CONTROL_SOCKET_H
+#define UNIX_CONTROL_SOCKET_H
+
+#include <netconf/control_socket.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Class for control socket communication over UNIX socket.
+///
+/// This class is the derived class for control socket communication
+/// over UNIX sockets.
+/// This class implements config-get, config-test and config-set.
+class UnixControlSocket : public ControlSocketBase {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// @param ctrl_sock The control socket configuration.
+ UnixControlSocket(CfgControlSocketPtr ctrl_sock);
+
+ /// @brief Destructor (does nothing).
+ virtual ~UnixControlSocket() = default;
+
+ /// @brief Get configuration.
+ ///
+ /// Call config-get over the control socket.
+ ///
+ /// @param service The target service (ignored).
+ /// @return The JSON element answer of config-get.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configGet(const std::string& service);
+
+ /// @brief Test configuration.
+ ///
+ /// Call config-test over the control socket.
+ ///
+ /// @param service The target service (ignored).
+ /// @param config The configuration to test.
+ /// @return The JSON element answer of config-test.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+ const std::string& service);
+
+ /// @brief Set configuration.
+ ///
+ /// Call config-set over the control socket.
+ ///
+ /// @param config The configuration to set.
+ /// @param service The target service (ignored).
+ /// @return The JSON element answer of config-set.
+ /// @throw ControlSocketError when a communication error occurs.
+ virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+ const std::string& service);
+
+private:
+ /// @brief Perform the actual communication.
+ ///
+ /// @note This method is synchronous, i.e. it blocks until it finishes
+ /// and returns.
+ ///
+ /// @param command The command to send.
+ /// @return The answer.
+ data::ConstElementPtr sendCommand(data::ConstElementPtr command);
+};
+
+/// @brief Type definition for the pointer to the @c UnixControlSocket.
+typedef boost::shared_ptr<UnixControlSocket> UnixControlSocketPtr;
+
+/// @brief Factory template specialization for unix control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a unix control socket communication object.
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::UNIX>(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // UNIX_CONTROL_SOCKET_H