diff options
Diffstat (limited to 'src/bin/netconf')
54 files changed, 22877 insertions, 0 deletions
diff --git a/src/bin/netconf/Makefile.am b/src/bin/netconf/Makefile.am new file mode 100644 index 0000000..5f82d72 --- /dev/null +++ b/src/bin/netconf/Makefile.am @@ -0,0 +1,138 @@ +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 += $(LIBYANG_CPPFLAGS) +AM_CPPFLAGS += $(LIBYANG_INCLUDEDIR) +AM_CPPFLAGS += $(LIBYANGCPP_CPPFLAGS) +AM_CPPFLAGS += $(LIBYANGCPP_INCLUDEDIR) +AM_CPPFLAGS += $(SYSREPO_CPPFLAGS) +AM_CPPFLAGS += $(SYSREPO_INCLUDEDIR) +AM_CPPFLAGS += $(SYSREPOCPP_CPPFLAGS) +AM_CPPFLAGS += $(SYSREPOCPP_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/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) +kea_netconf_LDADD += $(LIBYANG_LIBS) +kea_netconf_LDADD += $(LIBYANGCPP_LIBS) +kea_netconf_LDADD += $(SYSREPO_LIBS) +kea_netconf_LDADD += $(SYSREPOCPP_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..fadff49 --- /dev/null +++ b/src/bin/netconf/Makefile.in @@ -0,0 +1,1099 @@ +# 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_cpp20.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_netconf.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__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/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__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_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@ +DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@ +DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_NETCONF = @HAVE_NETCONF@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@ +LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@ +LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@ +LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@ +LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@ +LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@ +LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@ +LIBYANG_LIBS = @LIBYANG_LIBS@ +LIBYANG_PREFIX = @LIBYANG_PREFIX@ +LIBYANG_VERSION = @LIBYANG_VERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_PLUGINS_PATH = @SR_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@ +SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@ +SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@ +SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_PREFIX = @SYSREPO_PREFIX@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . tests +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) $(LIBYANG_CPPFLAGS) \ + $(LIBYANG_INCLUDEDIR) $(LIBYANGCPP_CPPFLAGS) \ + $(LIBYANGCPP_INCLUDEDIR) $(SYSREPO_CPPFLAGS) \ + $(SYSREPO_INCLUDEDIR) $(SYSREPOCPP_CPPFLAGS) \ + $(SYSREPOCPP_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/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) $(LIBYANG_LIBS) \ + $(LIBYANGCPP_LIBS) $(SYSREPO_LIBS) $(SYSREPOCPP_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..8faadfb --- /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..05cfe74 --- /dev/null +++ b/src/bin/netconf/control_socket.h @@ -0,0 +1,129 @@ +// 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 <exceptions/exceptions.h> +#include <netconf/netconf_config.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) { } +}; // ControlSocketError + +/// @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() = default; + + /// @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::ElementPtr 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::ElementPtr config, + const std::string& service) = 0; + + /// @brief The control socket configuration. + CfgControlSocketPtr socket_cfg_; +}; // ControlSocketBase + +/// @brief Type definition for the pointer to the @c ControlSocketBase. +using ControlSocketBasePtr = std::shared_ptr<ControlSocketBase>; + +/// @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..02f5b50 --- /dev/null +++ b/src/bin/netconf/http_control_socket.cc @@ -0,0 +1,130 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// @file http_control_socket.cc +/// Contains the HTTP socket derived class for control socket communication. + +#include <config.h> + +#include <asiolink/asio_wrapper.h> +#include <asiolink/io_service.h> +#include <asiolink/tls_socket.h> +#include <cc/command_interpreter.h> +#include <config/timeouts.h> +#include <http/client.h> +#include <http/post_request_json.h> +#include <http/response_json.h> +#include <netconf/http_control_socket.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(ElementPtr config, const string& service) { + if (service == "ca") { + return (sendCommand(createCommand("config-test", config))); + } else { + return (sendCommand(createCommand("config-test", config, service))); + } +} + +ConstElementPtr +HttpControlSocket::configSet(ElementPtr 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 (exception const& ex) { + isc_throw(ControlSocketError, "failed to create request: " + << ex.what()); + } + + IOServicePtr io_service(new IOService()); + HttpClient client(*io_service, false); + 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 (exception const& 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..102f941 --- /dev/null +++ b/src/bin/netconf/http_control_socket.h @@ -0,0 +1,88 @@ +// 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 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. + data::ConstElementPtr configGet(const std::string& service) override final; + + /// @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. + data::ConstElementPtr configTest(data::ElementPtr config, + const std::string& service) override final; + + /// @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. + data::ConstElementPtr configSet(data::ElementPtr config, + const std::string& service) override final; + +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); +}; // HttpControlSocket + +/// @brief Type definition for the pointer to the @c HttpControlSocket. +using HttpControlSocketPtr = std::shared_ptr<HttpControlSocket>; + +/// @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..a6ac2a1 --- /dev/null +++ b/src/bin/netconf/main.cc @@ -0,0 +1,53 @@ +// 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 <exceptions/exceptions.h> +#include <netconf/netconf_controller.h> + +#include <cstdlib> +#include <iostream> + +using namespace isc::netconf; +using namespace isc::process; + +using namespace std; + +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) { + string msg(ex.what()); + if (!msg.empty()) { + cout << msg << endl; + } + } catch (const InvalidUsage& ex) { + string msg(ex.what()); + if (!msg.empty()) { + cerr << msg << endl; + } + ret = EXIT_FAILURE; + } catch (exception const& ex) { + cerr << "Service failed: " << ex.what() << endl; + ret = EXIT_FAILURE; + } catch (...) { + cerr << "Service failed" << 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..c515dec --- /dev/null +++ b/src/bin/netconf/netconf.cc @@ -0,0 +1,745 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +/// @file netconf.cc +/// Contains the Netconf agent methods. + +#include <config.h> + +#include <cc/command_interpreter.h> +#include <netconf/netconf.h> +#include <netconf/netconf_controller.h> +#include <netconf/netconf_log.h> +#include <yang/translator_config.h> +#include <yang/yang_revisions.h> + +#include <sstream> +#include <vector> + +using namespace std; +using namespace isc::config; +using namespace isc::data; +using namespace isc::netconf; +using namespace isc::yang; +using namespace libyang; +using namespace sysrepo; + +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. + sysrepo::ErrorCode module_change(Session sess, + uint32_t /* subscription_id */, + string_view module_name, + optional<string_view> /* sub_xpath */, + Event event, + uint32_t /* request_id */) { + ostringstream event_type; + switch (event) { + case Event::Update: + // This could potentially be a hook point for mid-flight + // configuration changes. + event_type << "Event::Update"; + break; + case Event::Change: + event_type << "Event::Change"; + break; + case Event::Done: + event_type << "Event::Done"; + break; + case Event::Abort: + event_type << "Event::Abort"; + break; + case Event::Enabled: + event_type << "Event::Enabled"; + break; + case Event::RPC: + event_type << "Event::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 Event::Change: + return (NetconfAgent::change(sess, service_pair_)); + case Event::Done: + return (NetconfAgent::done(sess, service_pair_)); + default: + return (sysrepo::ErrorCode::Ok); + } + } + + void event_notif(Session /* session */, + uint32_t /* subscription_id */, + NotificationType const notification_type, + optional<DataNode> const notification_tree, + NotificationTimeStamp const /* timestamp */) { + string n; + switch (notification_type) { + case NotificationType::Realtime: + n = "NotificationType::Realtime"; + break; + case NotificationType::Replay: + n = "NotificationType::Replay"; + break; + case NotificationType::ReplayComplete: + n = "NotificationType::ReplayComplete"; + break; + case NotificationType::Terminated: + n = "NotificationType::Terminated"; + break; + case NotificationType::Modified: + n = "NotificationType::Modified"; + break; + case NotificationType::Suspended: + n = "NotificationType::Suspended"; + break; + case NotificationType::Resumed: + n = "NotificationType::Resumed"; + break; + } + + optional<string> const str( + notification_tree->printStr(DataFormat::JSON, PrintFlags::WithDefaultsExplicit)); + string const tree(str ? *str : string()); + LOG_INFO(netconf_logger, NETCONF_NOTIFICATION_RECEIVED) + .arg(n) + .arg(service_pair_.first) + .arg(tree); + } +}; // NetconfAgentCallback + +} //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); + subscribeToDataChanges(pair); + subscribeToNotifications(pair); + } +} + +void +NetconfAgent::clear() { + subscriptions_.clear(); + running_sess_.reset(); + startup_sess_.reset(); +} + +void +NetconfAgent::keaConfig(const CfgServersMapPair& service_pair) { + string const& server(service_pair.first); + CfgServerPtr const& configuration(service_pair.second); + + // If the boot-update flag is not set. + if (!configuration->getBootUpdate()) { + return; + } + CfgControlSocketPtr ctrl_sock = configuration->getCfgControlSocket(); + if (!ctrl_sock) { + return; + } + ControlSocketBasePtr comm; + try { + comm = controlSocketFactory(ctrl_sock); + } catch (exception const& ex) { + ostringstream msg; + msg << "createControlSocket failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return; + } + ConstElementPtr answer; + int rcode; + ConstElementPtr config; + LOG_INFO(netconf_logger, NETCONF_GET_CONFIG_STARTED) + .arg(server); + try { + answer = comm->configGet(server); + config = parseAnswer(rcode, answer); + } catch (exception const& ex) { + ostringstream msg; + msg << "config-get command failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED) + .arg(server) + .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(server) + .arg(msg.str()); + return; + } + if (!config) { + LOG_ERROR(netconf_logger, NETCONF_GET_CONFIG_FAILED) + .arg(server) + .arg("config-get command returned an empty configuration"); + return; + } + LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA, + NETCONF_GET_CONFIG) + .arg(server) + .arg(prettyPrint(config)); +} + +void +NetconfAgent::initSysrepo() { + try { + running_sess_ = Connection{}.sessionStart(); + running_sess_->switchDatastore(Datastore::Running); + startup_sess_ = Connection{}.sessionStart(); + startup_sess_->switchDatastore(Datastore::Startup); + } catch (exception const& 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<Module> modules; + try { + Context context(running_sess_->getContext()); + modules = context.modules(); + } catch (Error const& ex) { + isc_throw(Unexpected, "can't retrieve available modules: " << ex.what()); + } + + for (Module const& module : modules) { + string const name(module.name()); + if (!module.revision()) { + isc_throw(Unexpected, + "could not retrieve module revision for module " << name); + } + string const revision(*module.revision()); + 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) { + string const& server(service_pair.first); + CfgServerPtr const& configuration(service_pair.second); + + // If we're shutting down, or the boot-update flag is not set or the model + // associated with it is not specified. + if (!configuration->getBootUpdate() || + configuration->getModel().empty()) { + return; + } + + // First we need a way to reach the actual servers. + CfgControlSocketPtr ctrl_sock = configuration->getCfgControlSocket(); + if (!ctrl_sock) { + return; + } + + LOG_INFO(netconf_logger, NETCONF_SET_CONFIG_STARTED) + .arg(server); + ElementPtr config; + try { + // Retrieve configuration from Sysrepo. + TranslatorConfig tc(*startup_sess_, configuration->getModel()); + config = tc.getConfig(); + if (!config) { + ostringstream msg; + msg << "YANG configuration for " + << configuration->getModel() + << " is empty"; + LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return; + } else { + LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA, + NETCONF_SET_CONFIG) + .arg(server) + .arg(prettyPrint(config)); + } + } catch (exception const& ex) { + ostringstream msg; + msg << "get YANG configuration for " << server + << " failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return; + } + ControlSocketBasePtr comm; + try { + comm = controlSocketFactory(ctrl_sock); + } catch (exception const& ex) { + ostringstream msg; + msg << "control socket creation failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return; + } + ConstElementPtr answer; + int rcode; + try { + answer = comm->configSet(config, server); + parseAnswer(rcode, answer); + } catch (exception const& ex) { + ostringstream msg; + msg << "config-set command failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_SET_CONFIG_FAILED) + .arg(server) + .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(server) + .arg(msg.str()); + return; + } + LOG_INFO(netconf_logger, NETCONF_BOOT_UPDATE_COMPLETED) + .arg(server); +} + +void +NetconfAgent::subscribeToDataChanges(const CfgServersMapPair& service_pair) { + string const& server(service_pair.first); + CfgServerPtr const& configuration(service_pair.second); + string const& model(configuration->getModel()); + + // If we're shutting down, or the subscribe-changes flag is not set or + // the model is not specified, give up on subscribing. + if (!configuration->getSubscribeChanges() || model.empty()) { + return; + } + LOG_INFO(netconf_logger, NETCONF_SUBSCRIBE_CONFIG) + .arg(server) + .arg(model); + auto callback = [=](Session session, + uint32_t subscription_id, + string_view module_name, + optional<string_view> sub_xpath, + Event event, + uint32_t request_id) { + NetconfAgentCallback agent(service_pair); + return agent.module_change(session, subscription_id, module_name, sub_xpath, event, request_id); + }; + try { + SubscribeOptions options(SubscribeOptions::Default); + if (!configuration->getValidateChanges()) { + options = options | SubscribeOptions::DoneOnly; + } + Subscription subscription( + running_sess_->onModuleChange(model, callback, nullopt, 0, options)); + subscriptions_.emplace(server, std::forward<Subscription>(subscription)); + } catch (exception const& 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(server) + .arg(configuration->getModel()) + .arg(msg.str()); + return; + } +} + +void +NetconfAgent::subscribeToNotifications(const CfgServersMapPair& service_pair) { + string const& server(service_pair.first); + CfgServerPtr const& configuration(service_pair.second); + string const& model(configuration->getModel()); + + // If we're shutting down, or the subscribe-changes flag is not set or + // the model is not specified, give up on subscribing. + if (!configuration->getSubscribeNotifications() || model.empty()) { + return; + } + LOG_INFO(netconf_logger, NETCONF_SUBSCRIBE_NOTIFICATIONS) + .arg(server) + .arg(model); + + auto callback = [=](Session session, + uint32_t subscription_id, + NotificationType const notification_type, + optional<DataNode> const notification_tree, + NotificationTimeStamp const timestamp) { + NetconfAgentCallback agent(service_pair); + return agent.event_notif(session, subscription_id, notification_type, notification_tree, timestamp); + }; + try { + Subscription subscription(running_sess_->onNotification(model, callback)); + subscriptions_.emplace(server, std::forward<Subscription>(subscription)); + } catch (exception const& ex) { + ostringstream msg; + msg << "event notification subscription for model " << model << + " failed with: " << ex.what(); + LOG_WARN(netconf_logger, NETCONF_NOT_SUBSCRIBED_TO_NOTIFICATIONS) + .arg(server) + .arg(configuration->getModel()) + .arg(msg.str()); + return; + } +} + +sysrepo::ErrorCode +NetconfAgent::change(Session sess, const CfgServersMapPair& service_pair) { + string const& server(service_pair.first); + CfgServerPtr const& configuration(service_pair.second); + + // 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 (!configuration->getSubscribeChanges() || + !configuration->getValidateChanges() || + configuration->getModel().empty()) { + return (sysrepo::ErrorCode::Ok); + } + CfgControlSocketPtr ctrl_sock = configuration->getCfgControlSocket(); + if (!ctrl_sock) { + return (sysrepo::ErrorCode::Ok); + } + LOG_INFO(netconf_logger, NETCONF_VALIDATE_CONFIG_STARTED) + .arg(server); + ElementPtr config; + try { + TranslatorConfig tc(sess, configuration->getModel()); + config = tc.getConfig(); + if (!config) { + ostringstream msg; + msg << "YANG configuration for " + << configuration->getModel() + << " is empty"; + LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::OperationFailed); + } else { + LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA, + NETCONF_VALIDATE_CONFIG) + .arg(server) + .arg(prettyPrint(config)); + } + } catch (exception const& ex) { + ostringstream msg; + msg << "get YANG configuration for " << server + << " failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::ValidationFailed); + } + ControlSocketBasePtr comm; + try { + comm = controlSocketFactory(ctrl_sock); + } catch (exception const& ex) { + ostringstream msg; + msg << "createControlSocket failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::Ok); + } + ConstElementPtr answer; + int rcode; + try { + answer = comm->configTest(config, server); + parseAnswer(rcode, answer); + } catch (exception const& ex) { + stringstream msg; + msg << "configTest failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::ValidationFailed); + } + if (rcode != CONTROL_RESULT_SUCCESS) { + stringstream msg; + msg << "configTest returned " << answerToText(answer); + LOG_ERROR(netconf_logger, NETCONF_VALIDATE_CONFIG_REJECTED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::ValidationFailed); + } + LOG_INFO(netconf_logger, NETCONF_VALIDATE_CONFIG_COMPLETED) + .arg(server); + return (sysrepo::ErrorCode::Ok); +} + +sysrepo::ErrorCode +NetconfAgent::done(Session sess, const CfgServersMapPair& service_pair) { + string const& server(service_pair.first); + CfgServerPtr const& configuration(service_pair.second); + + // Check if we should and can process this update. + if (!configuration->getSubscribeChanges() || + configuration->getModel().empty()) { + return (sysrepo::ErrorCode::Ok); + } + CfgControlSocketPtr ctrl_sock = configuration->getCfgControlSocket(); + if (!ctrl_sock) { + return (sysrepo::ErrorCode::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(server); + + // Retrieve the configuration from SYSREPO first. + ElementPtr config; + try { + TranslatorConfig tc(sess, configuration->getModel()); + config = tc.getConfig(); + if (!config) { + ostringstream msg; + msg << "YANG configuration for " + << configuration->getModel() + << " is empty"; + LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::ValidationFailed); + } else { + LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA, + NETCONF_UPDATE_CONFIG) + .arg(server) + .arg(prettyPrint(config)); + } + } catch (exception const& ex) { + ostringstream msg; + msg << "get YANG configuration for " << server + << " failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::ValidationFailed); + } + + // Ok, now open the control socket. We need this to send the config to + // the server. + ControlSocketBasePtr comm; + try { + comm = controlSocketFactory(ctrl_sock); + } catch (exception const& ex) { + ostringstream msg; + msg << "createControlSocket failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::Ok); + } + + // Now apply the config using config-set command. + ConstElementPtr answer; + int rcode; + try { + answer = comm->configSet(config, server); + parseAnswer(rcode, answer); + } catch (exception const& ex) { + stringstream msg; + msg << "configSet failed with " << ex.what(); + LOG_ERROR(netconf_logger, NETCONF_UPDATE_CONFIG_FAILED) + .arg(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::ValidationFailed); + } + + // 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(server) + .arg(msg.str()); + return (sysrepo::ErrorCode::ValidationFailed); + } + LOG_INFO(netconf_logger, NETCONF_UPDATE_CONFIG_COMPLETED) + .arg(server); + return (sysrepo::ErrorCode::Ok); +} + +void +NetconfAgent::logChanges(Session sess, string_view const& model) { + ostringstream stream; + stream << "/" << model << ":*//."; + string const xpath(stream.str()); + ChangeCollection const changes(sess.getChanges(xpath)); + for (Change const& change : changes) { + ostringstream msg; + switch (change.operation) { + case sysrepo::ChangeOperation::Created: + msg << "created: "; + break; + case sysrepo::ChangeOperation::Deleted: + msg << "deleted: "; + break; + case sysrepo::ChangeOperation::Modified: + msg << "modified: "; + break; + case sysrepo::ChangeOperation::Moved: + msg << "moved: "; + break; + default: + msg << "unknown operation (" << change.operation << "): "; + } + string const path(change.node.path()); + msg << path; + SchemaNode const& schema(change.node.schema()); + NodeType const node_type(schema.nodeType()); + if (node_type == NodeType::Container) { + msg << " (container)"; + } else if (node_type == NodeType::List) { + msg << " (list)"; + } else { + optional<string> const str( + Translator::translateToYang(Translator::translateFromYang(change.node), + LeafBaseType::Unknown)); + if (str) { + msg << " = " << *str; + } + } + + LOG_DEBUG(netconf_logger, NETCONF_DBG_TRACE_DETAIL_DATA, + NETCONF_CONFIG_CHANGED_DETAIL) + .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..bb37ea5 --- /dev/null +++ b/src/bin/netconf/netconf.h @@ -0,0 +1,180 @@ +// 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.h + +#ifndef NETCONF_H +#define NETCONF_H + +#include <netconf/control_socket.h> +#include <netconf/http_control_socket.h> +#include <netconf/netconf_cfg_mgr.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. +using NetconfAgentPtr = std::shared_ptr<NetconfAgent>; + +/// @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 Event::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 sysrepo::ErrorCode + change(sysrepo::Session sess, const CfgServersMapPair& service_pair); + + /// @brief Event::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 sysrepo::ErrorCode + done(sysrepo::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::Session sess, std::string_view const& 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 subscribeToDataChanges(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 startup datastore session. + std::optional<sysrepo::Session> startup_sess_; + + /// @brief Sysrepo running datastore session. + std::optional<sysrepo::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::Subscription> subscriptions_; +}; // NetconfAgent + +} // 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..019bfaf --- /dev/null +++ b/src/bin/netconf/netconf_cfg_mgr.cc @@ -0,0 +1,179 @@ +// 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 <cc/command_interpreter.h> +#include <cc/simple_parser.h> +#include <exceptions/exceptions.h> +#include <netconf/netconf_cfg_mgr.h> +#include <netconf/netconf_log.h> +#include <netconf/simple_parser.h> + +#include <map> +#include <sstream> + +using namespace isc::config; +using namespace isc::dhcp; +using namespace isc::process; +using namespace isc::data; +using namespace isc::hooks; + +using namespace std; + +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"); + } + + for (auto const& value : config->mapValue()) { + if (value.second->getType() != Element::list && + value.second->getType() != Element::map) { + addConfiguredGlobal(value.first, value.second); + } + } +} + +NetconfCfgMgr::NetconfCfgMgr() + : DCfgMgrBase(ConfigPtr(new NetconfConfig())) { +} + +string +NetconfCfgMgr::getConfigSummary(const uint32_t /*selection*/) { + + NetconfConfigPtr ctx = getNetconfConfig(); + + // No globals to print. + 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 HookLibsCollection libs = ctx->getHooksConfig().get(); + s << ", " << libs.size() << " lib(s):"; + for (HookLibInfo const& lib : libs) { + 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 = copy(config_set, 0); + NetconfSimpleParser::setAllDefaults(cfg); + NetconfSimpleParser::deriveParameters(cfg); + + // And parse the configuration. + ConstElementPtr answer; + 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_) { + ElementPtr 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); +} + +list<list<string>> +NetconfCfgMgr::jsonPathsToRedact() const { + static list<list<string>> const list({ + {"hooks-libraries", "[]", "parameters", "*"}, + }); + return list; +} + +} // namespace 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..d8c8b88 --- /dev/null +++ b/src/bin/netconf/netconf_cfg_mgr.h @@ -0,0 +1,183 @@ +// 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_CFG_MGR_H +#define NETCONF_CFG_MGR_H + +#include <cc/data.h> +#include <hooks/hooks_config.h> +#include <netconf/netconf_config.h> +#include <process/d_cfg_mgr.h> + +#include <string> + +namespace isc { +namespace netconf { + +class NetconfConfig; +/// @brief Pointer to a configuration context. +using NetconfConfigPtr = boost::shared_ptr<NetconfConfig>; + +/// @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::ElementPtr getConfiguredGlobals() const { + return (isc::data::ElementPtr(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 + isc::data::ElementPtr toElement() const override final; + +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_; +}; // NetconfConfig + +/// @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. + std::string getConfigSummary(const uint32_t selection) override final; + + /// @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 override final; + +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. + isc::data::ConstElementPtr + parse(isc::data::ConstElementPtr config, bool check_only) override final; + + /// @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. + process::ConfigPtr createNewContext() override final; +}; // NetconfCfgMgr + +/// @brief Defines a shared pointer to NetconfCfgMgr. +using NetconfCfgMgrPtr = boost::shared_ptr<NetconfCfgMgr>; + +} // namespace 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..eac9ce5 --- /dev/null +++ b/src/bin/netconf/netconf_config.cc @@ -0,0 +1,207 @@ +// 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 <asiolink/io_error.h> +#include <netconf/netconf_cfg_mgr.h> +#include <netconf/netconf_log.h> +#include <exceptions/exceptions.h> + +#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 (exception const& 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 (exception const& 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 (exception const& 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..b4f6339 --- /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/cfg_to_element.h> +#include <cc/data.h> +#include <cc/simple_parser.h> +#include <cc/user_context.h> +#include <exceptions/exceptions.h> +#include <http/url.h> + +#include <stdint.h> + +#include <string> +#include <unordered_map> + +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. + }; // Type + + /// @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 + isc::data::ElementPtr toElement() const override final; + +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_; +}; // CfgControlSocket + +/// @brief Defines a pointer for CfgControlSocket instances. +using CfgControlSocketPtr = std::shared_ptr<CfgControlSocket>; + +/// @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 + isc::data::ElementPtr toElement() const override final; + +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_; +}; // CfgServer + +/// @brief Defines a pointer for CfgServer instances. +using CfgServerPtr = std::shared_ptr<CfgServer>; + +/// @brief Defines a map of CfgServers, keyed by the name. +using CfgServersMap = std::unordered_map<std::string, CfgServerPtr>; + +/// @brief Defines a iterator pairing of name and CfgServer +using CfgServersMapPair = std::pair<std::string, CfgServerPtr>; + +/// @brief Defines a pointer to map of CfgServers. +using CfgServersMapPtr = std::shared_ptr<CfgServersMap>; + +/// @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); +}; // ControlSocketConfigParser + +/// @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); +}; // ServerConfigParser + +} // 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..aa999bd --- /dev/null +++ b/src/bin/netconf/netconf_controller.cc @@ -0,0 +1,72 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <netconf/netconf_controller.h> +#include <netconf/netconf_process.h> +#include <netconf/parser_context.h> +#include <process/cfgrpt/config_report.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(string const& 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())); +} + +} // namespace 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..ca1f725 --- /dev/null +++ b/src/bin/netconf/netconf_controller.h @@ -0,0 +1,85 @@ +// 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_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) override final; + + /// @brief Redefined application-level signal processing method. + /// + /// This method ignores SIGHUP as configuration reloading is not yet + /// supported. + /// @param signum signal number to process. + void processSignal(int signum) override final; + +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. + process::DProcessBase* createProcess() override final; + + /// @brief Constructor is declared private to maintain the integrity of + /// the singleton instance. + NetconfController(); +}; // NetconfController + +// @Defines a shared pointer to NetconfController +using NetconfControllerPtr = std::shared_ptr<NetconfController>; + +} // namespace 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..97079ea --- /dev/null +++ b/src/bin/netconf/netconf_log.cc @@ -0,0 +1,28 @@ +// 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_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..69619f5 --- /dev/null +++ b/src/bin/netconf/netconf_log.h @@ -0,0 +1,54 @@ +// 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_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..c1913ae --- /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_NOT_SUBSCRIBED_TO_NOTIFICATIONS = "NETCONF_NOT_SUBSCRIBED_TO_NOTIFICATIONS"; +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_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_NOT_SUBSCRIBED_TO_NOTIFICATIONS", "subscribing to notifications for %1 server with %2 module failed: %3", + "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", "kea-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_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..594c0c8 --- /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_NOT_SUBSCRIBED_TO_NOTIFICATIONS; +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_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..43c17e3 --- /dev/null +++ b/src/bin/netconf/netconf_messages.mes @@ -0,0 +1,175 @@ +# Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$NAMESPACE isc::netconf + +% NETCONF_BOOT_UPDATE_COMPLETED Boot-update configuration completed for server %1 +This informational message is issued when the initial configuration +was retrieved using 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 kea-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 kea-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 kea-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 kea-netconf +got an unrecoverable error from within the event loop. + +% NETCONF_GET_CONFIG got configuration from %1 server: %2 +This debug message indicates that kea-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 kea-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 kea-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_NOT_SUBSCRIBED_TO_NOTIFICATIONS subscribing to notifications for %1 server with %2 module failed: %3 +The warning message indicates that kea-netconf got an error subscribing to +notifications for a Kea server. The most probable cause is probably that the +model that kea-netconf subscribed to does not have any notification nodes, but +there may be other more unexpected causes as well. +The server name, module name and the error are printed. + +% NETCONF_RUN_EXIT application is exiting the event loop +This is a debug message issued when kea-netconf 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 kea-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 kea-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 kea-netconf is trying to set the +configuration to a Kea server. + +% NETCONF_STARTED kea-netconf (version %1) started +This informational message indicates that kea-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 kea-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 kea-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 kea-netconf is trying to subscribe to +notifications for a Kea server. The server name and module name are printed. + +% NETCONF_UPDATE_CONFIG updating configuration with %1 server: %2 +This debug message indicates that kea-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 kea-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 kea-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 kea-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 kea-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 kea-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 kea-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 kea-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 kea-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..bb96708 --- /dev/null +++ b/src/bin/netconf/netconf_parser.cc @@ -0,0 +1,2316 @@ +// 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 36 "netconf_parser.yy" + +#include <netconf/parser_context.h> + +// Avoid warnings with the error counter. +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif + +#line 57 "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 150 "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< 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< 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< 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< 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 115 "netconf_parser.yy" + { yyoutput << yysym.value.template as < string > (); } +#line 389 "netconf_parser.cc" + break; + + case symbol_kind::S_INTEGER: // "integer" +#line 115 "netconf_parser.yy" + { yyoutput << yysym.value.template as < int64_t > (); } +#line 395 "netconf_parser.cc" + break; + + case symbol_kind::S_FLOAT: // "floating point" +#line 115 "netconf_parser.yy" + { yyoutput << yysym.value.template as < double > (); } +#line 401 "netconf_parser.cc" + break; + + case symbol_kind::S_BOOLEAN: // "boolean" +#line 115 "netconf_parser.yy" + { yyoutput << yysym.value.template as < bool > (); } +#line 407 "netconf_parser.cc" + break; + + case symbol_kind::S_value: // value +#line 115 "netconf_parser.yy" + { yyoutput << yysym.value.template as < ElementPtr > (); } +#line 413 "netconf_parser.cc" + break; + + case symbol_kind::S_map_value: // map_value +#line 115 "netconf_parser.yy" + { yyoutput << yysym.value.template as < ElementPtr > (); } +#line 419 "netconf_parser.cc" + break; + + case symbol_kind::S_socket_type_value: // socket_type_value +#line 115 "netconf_parser.yy" + { yyoutput << yysym.value.template as < ElementPtr > (); } +#line 425 "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< 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 126 "netconf_parser.yy" + { ctx.ctx_ = ctx.NO_KEYWORDS; } +#line 700 "netconf_parser.cc" + break; + + case 4: // $@2: %empty +#line 127 "netconf_parser.yy" + { ctx.ctx_ = ctx.CONFIG; } +#line 706 "netconf_parser.cc" + break; + + case 6: // $@3: %empty +#line 128 "netconf_parser.yy" + { ctx.ctx_ = ctx.NETCONF; } +#line 712 "netconf_parser.cc" + break; + + case 8: // $@4: %empty +#line 136 "netconf_parser.yy" + { + // Parse the Netconf map + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(m); +} +#line 722 "netconf_parser.cc" + break; + + case 9: // sub_netconf: "{" $@4 global_params "}" +#line 140 "netconf_parser.yy" + { + // parsing completed +} +#line 730 "netconf_parser.cc" + break; + + case 10: // json: value +#line 147 "netconf_parser.yy" + { + // Push back the JSON value on the stack + ctx.stack_.push_back(yystack_[0].value.as < ElementPtr > ()); +} +#line 739 "netconf_parser.cc" + break; + + case 11: // value: "integer" +#line 153 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new IntElement(yystack_[0].value.as < int64_t > (), ctx.loc2pos(yystack_[0].location))); } +#line 745 "netconf_parser.cc" + break; + + case 12: // value: "floating point" +#line 154 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new DoubleElement(yystack_[0].value.as < double > (), ctx.loc2pos(yystack_[0].location))); } +#line 751 "netconf_parser.cc" + break; + + case 13: // value: "boolean" +#line 155 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new BoolElement(yystack_[0].value.as < bool > (), ctx.loc2pos(yystack_[0].location))); } +#line 757 "netconf_parser.cc" + break; + + case 14: // value: "constant string" +#line 156 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); } +#line 763 "netconf_parser.cc" + break; + + case 15: // value: "null" +#line 157 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new NullElement(ctx.loc2pos(yystack_[0].location))); } +#line 769 "netconf_parser.cc" + break; + + case 16: // value: map +#line 158 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); } +#line 775 "netconf_parser.cc" + break; + + case 17: // value: list_generic +#line 159 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); } +#line 781 "netconf_parser.cc" + break; + + case 18: // $@5: %empty +#line 163 "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 792 "netconf_parser.cc" + break; + + case 19: // map: "{" $@5 map_content "}" +#line 168 "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 802 "netconf_parser.cc" + break; + + case 20: // map_value: map +#line 174 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ctx.stack_.back(); ctx.stack_.pop_back(); } +#line 808 "netconf_parser.cc" + break; + + case 23: // not_empty_map: "constant string" ":" value +#line 188 "netconf_parser.yy" + { + // map containing a single entry + ctx.unique(yystack_[2].value.as < string > (), ctx.loc2pos(yystack_[2].location)); + ctx.stack_.back()->set(yystack_[2].value.as < string > (), yystack_[0].value.as < ElementPtr > ()); + } +#line 818 "netconf_parser.cc" + break; + + case 24: // not_empty_map: not_empty_map "," "constant string" ":" value +#line 193 "netconf_parser.yy" + { + // map consisting of a shorter map followed by + // comma and string:value + ctx.unique(yystack_[2].value.as < string > (), ctx.loc2pos(yystack_[2].location)); + ctx.stack_.back()->set(yystack_[2].value.as < string > (), yystack_[0].value.as < ElementPtr > ()); + } +#line 829 "netconf_parser.cc" + break; + + case 25: // not_empty_map: not_empty_map "," +#line 199 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 837 "netconf_parser.cc" + break; + + case 26: // $@6: %empty +#line 204 "netconf_parser.yy" + { + ElementPtr l(new ListElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.push_back(l); +} +#line 846 "netconf_parser.cc" + break; + + case 27: // list_generic: "[" $@6 list_content "]" +#line 207 "netconf_parser.yy" + { +} +#line 853 "netconf_parser.cc" + break; + + case 30: // not_empty_list: value +#line 214 "netconf_parser.yy" + { + // List consisting of a single element. + ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ()); + } +#line 862 "netconf_parser.cc" + break; + + case 31: // not_empty_list: not_empty_list "," value +#line 218 "netconf_parser.yy" + { + // List ending with , and a value. + ctx.stack_.back()->add(yystack_[0].value.as < ElementPtr > ()); + } +#line 871 "netconf_parser.cc" + break; + + case 32: // not_empty_list: not_empty_list "," +#line 222 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 879 "netconf_parser.cc" + break; + + case 33: // unknown_map_entry: "constant string" ":" +#line 234 "netconf_parser.yy" + { + const string& where = ctx.contextName(); + const string& keyword = yystack_[1].value.as < string > (); + error(yystack_[1].location, + "got unexpected keyword \"" + keyword + "\" in " + where + " map."); +} +#line 890 "netconf_parser.cc" + break; + + case 34: // $@7: %empty +#line 242 "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 901 "netconf_parser.cc" + break; + + case 35: // netconf_syntax_map: "{" $@7 global_object "}" +#line 247 "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 911 "netconf_parser.cc" + break; + + case 36: // $@8: %empty +#line 254 "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 929 "netconf_parser.cc" + break; + + case 37: // global_object: "Netconf" $@8 ":" "{" global_params "}" +#line 266 "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 940 "netconf_parser.cc" + break; + + case 39: // global_object_comma: global_object "," +#line 275 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); +} +#line 948 "netconf_parser.cc" + break; + + case 44: // not_empty_global_params: not_empty_global_params "," +#line 286 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 956 "netconf_parser.cc" + break; + + case 54: // boot_update: "boot-update" ":" "boolean" +#line 304 "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 966 "netconf_parser.cc" + break; + + case 55: // subscribe_changes: "subscribe-changes" ":" "boolean" +#line 310 "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 976 "netconf_parser.cc" + break; + + case 56: // validate_changes: "validate-changes" ":" "boolean" +#line 316 "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 986 "netconf_parser.cc" + break; + + case 57: // $@9: %empty +#line 322 "netconf_parser.yy" + { + ctx.enter(ctx.NO_KEYWORDS); +} +#line 994 "netconf_parser.cc" + break; + + case 58: // user_context: "user-context" $@9 ":" map_value +#line 324 "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")) { + 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 1021 "netconf_parser.cc" + break; + + case 59: // $@10: %empty +#line 347 "netconf_parser.yy" + { + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1029 "netconf_parser.cc" + break; + + case 60: // comment: "comment" $@10 ":" "constant string" +#line 349 "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 < 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")) { + 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 1058 "netconf_parser.cc" + break; + + case 61: // $@11: %empty +#line 375 "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 1070 "netconf_parser.cc" + break; + + case 62: // hooks_libraries: "hooks-libraries" $@11 ":" "[" hooks_libraries_list "]" +#line 381 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1079 "netconf_parser.cc" + break; + + case 67: // not_empty_hooks_libraries_list: not_empty_hooks_libraries_list "," +#line 392 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1087 "netconf_parser.cc" + break; + + case 68: // $@12: %empty +#line 397 "netconf_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} +#line 1097 "netconf_parser.cc" + break; + + case 69: // hooks_library: "{" $@12 hooks_params "}" +#line 401 "netconf_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1105 "netconf_parser.cc" + break; + + case 72: // hooks_params: hooks_params "," +#line 407 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1113 "netconf_parser.cc" + break; + + case 76: // $@13: %empty +#line 417 "netconf_parser.yy" + { + ctx.unique("library", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1122 "netconf_parser.cc" + break; + + case 77: // library: "library" $@13 ":" "constant string" +#line 420 "netconf_parser.yy" + { + ElementPtr lib(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("library", lib); + ctx.leave(); +} +#line 1132 "netconf_parser.cc" + break; + + case 78: // $@14: %empty +#line 426 "netconf_parser.yy" + { + ctx.unique("parameters", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1141 "netconf_parser.cc" + break; + + case 79: // parameters: "parameters" $@14 ":" map_value +#line 429 "netconf_parser.yy" + { + ctx.stack_.back()->set("parameters", yystack_[0].value.as < ElementPtr > ()); + ctx.leave(); +} +#line 1150 "netconf_parser.cc" + break; + + case 80: // $@15: %empty +#line 437 "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 1162 "netconf_parser.cc" + break; + + case 81: // managed_servers: "managed-servers" ":" "{" $@15 servers_entries "}" +#line 443 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1171 "netconf_parser.cc" + break; + + case 86: // not_empty_servers_entries: not_empty_servers_entries "," +#line 454 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1179 "netconf_parser.cc" + break; + + case 92: // $@16: %empty +#line 470 "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 1191 "netconf_parser.cc" + break; + + case 93: // dhcp4_server: "dhcp4" $@16 ":" "{" managed_server_params "}" +#line 476 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1200 "netconf_parser.cc" + break; + + case 94: // $@17: %empty +#line 482 "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 1212 "netconf_parser.cc" + break; + + case 95: // dhcp6_server: "dhcp6" $@17 ":" "{" managed_server_params "}" +#line 488 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1221 "netconf_parser.cc" + break; + + case 96: // $@18: %empty +#line 494 "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 1233 "netconf_parser.cc" + break; + + case 97: // d2_server: "d2" $@18 ":" "{" managed_server_params "}" +#line 500 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1242 "netconf_parser.cc" + break; + + case 98: // $@19: %empty +#line 506 "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 1254 "netconf_parser.cc" + break; + + case 99: // ca_server: "ca" $@19 ":" "{" managed_server_params "}" +#line 512 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1263 "netconf_parser.cc" + break; + + case 102: // managed_server_params: managed_server_params "," +#line 520 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1271 "netconf_parser.cc" + break; + + case 111: // $@20: %empty +#line 537 "netconf_parser.yy" + { + ctx.unique("model", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1280 "netconf_parser.cc" + break; + + case 112: // model: "model" $@20 ":" "constant string" +#line 540 "netconf_parser.yy" + { + ElementPtr model(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("model", model); + ctx.leave(); +} +#line 1290 "netconf_parser.cc" + break; + + case 113: // $@21: %empty +#line 547 "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 1302 "netconf_parser.cc" + break; + + case 114: // control_socket: "control-socket" $@21 ":" "{" control_socket_params "}" +#line 553 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1311 "netconf_parser.cc" + break; + + case 117: // control_socket_params: control_socket_params "," +#line 561 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1319 "netconf_parser.cc" + break; + + case 124: // $@22: %empty +#line 574 "netconf_parser.yy" + { + ctx.unique("socket-type", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.SOCKET_TYPE); +} +#line 1328 "netconf_parser.cc" + break; + + case 125: // socket_type: "socket-type" $@22 ":" socket_type_value +#line 577 "netconf_parser.yy" + { + ctx.stack_.back()->set("socket-type", yystack_[0].value.as < ElementPtr > ()); + ctx.leave(); +} +#line 1337 "netconf_parser.cc" + break; + + case 126: // socket_type_value: "unix" +#line 583 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("unix", ctx.loc2pos(yystack_[0].location))); } +#line 1343 "netconf_parser.cc" + break; + + case 127: // socket_type_value: "http" +#line 584 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("http", ctx.loc2pos(yystack_[0].location))); } +#line 1349 "netconf_parser.cc" + break; + + case 128: // socket_type_value: "stdout" +#line 585 "netconf_parser.yy" + { yylhs.value.as < ElementPtr > () = ElementPtr(new StringElement("stdout", ctx.loc2pos(yystack_[0].location))); } +#line 1355 "netconf_parser.cc" + break; + + case 129: // $@23: %empty +#line 588 "netconf_parser.yy" + { + ctx.unique("socket-name", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1364 "netconf_parser.cc" + break; + + case 130: // socket_name: "socket-name" $@23 ":" "constant string" +#line 591 "netconf_parser.yy" + { + ElementPtr name(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("socket-name", name); + ctx.leave(); +} +#line 1374 "netconf_parser.cc" + break; + + case 131: // $@24: %empty +#line 598 "netconf_parser.yy" + { + ctx.unique("socket-url", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1383 "netconf_parser.cc" + break; + + case 132: // socket_url: "socket-url" $@24 ":" "constant string" +#line 601 "netconf_parser.yy" + { + ElementPtr url(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("socket-url", url); + ctx.leave(); +} +#line 1393 "netconf_parser.cc" + break; + + case 133: // $@25: %empty +#line 611 "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 1405 "netconf_parser.cc" + break; + + case 134: // loggers: "loggers" $@25 ":" "[" loggers_entries "]" +#line 617 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1414 "netconf_parser.cc" + break; + + case 137: // loggers_entries: loggers_entries "," +#line 626 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1422 "netconf_parser.cc" + break; + + case 138: // $@26: %empty +#line 632 "netconf_parser.yy" + { + ElementPtr l(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(l); + ctx.stack_.push_back(l); +} +#line 1432 "netconf_parser.cc" + break; + + case 139: // logger_entry: "{" $@26 logger_params "}" +#line 636 "netconf_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1440 "netconf_parser.cc" + break; + + case 142: // logger_params: logger_params "," +#line 642 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1448 "netconf_parser.cc" + break; + + case 150: // $@27: %empty +#line 656 "netconf_parser.yy" + { + ctx.unique("name", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1457 "netconf_parser.cc" + break; + + case 151: // name: "name" $@27 ":" "constant string" +#line 659 "netconf_parser.yy" + { + ElementPtr name(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("name", name); + ctx.leave(); +} +#line 1467 "netconf_parser.cc" + break; + + case 152: // debuglevel: "debuglevel" ":" "integer" +#line 665 "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 1477 "netconf_parser.cc" + break; + + case 153: // $@28: %empty +#line 671 "netconf_parser.yy" + { + ctx.unique("severity", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1486 "netconf_parser.cc" + break; + + case 154: // severity: "severity" $@28 ":" "constant string" +#line 674 "netconf_parser.yy" + { + ElementPtr sev(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("severity", sev); + ctx.leave(); +} +#line 1496 "netconf_parser.cc" + break; + + case 155: // $@29: %empty +#line 680 "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 1508 "netconf_parser.cc" + break; + + case 156: // output_options_list: "output_options" $@29 ":" "[" output_options_list_content "]" +#line 686 "netconf_parser.yy" + { + ctx.stack_.pop_back(); + ctx.leave(); +} +#line 1517 "netconf_parser.cc" + break; + + case 159: // output_options_list_content: output_options_list_content "," +#line 693 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1525 "netconf_parser.cc" + break; + + case 160: // $@30: %empty +#line 698 "netconf_parser.yy" + { + ElementPtr m(new MapElement(ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->add(m); + ctx.stack_.push_back(m); +} +#line 1535 "netconf_parser.cc" + break; + + case 161: // output_entry: "{" $@30 output_params_list "}" +#line 702 "netconf_parser.yy" + { + ctx.stack_.pop_back(); +} +#line 1543 "netconf_parser.cc" + break; + + case 164: // output_params_list: output_params_list "," +#line 708 "netconf_parser.yy" + { + ctx.warnAboutExtraCommas(yystack_[0].location); + } +#line 1551 "netconf_parser.cc" + break; + + case 170: // $@31: %empty +#line 720 "netconf_parser.yy" + { + ctx.unique("output", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1560 "netconf_parser.cc" + break; + + case 171: // output: "output" $@31 ":" "constant string" +#line 723 "netconf_parser.yy" + { + ElementPtr sev(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("output", sev); + ctx.leave(); +} +#line 1570 "netconf_parser.cc" + break; + + case 172: // flush: "flush" ":" "boolean" +#line 729 "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 1580 "netconf_parser.cc" + break; + + case 173: // maxsize: "maxsize" ":" "integer" +#line 735 "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 1590 "netconf_parser.cc" + break; + + case 174: // maxver: "maxver" ":" "integer" +#line 741 "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 1600 "netconf_parser.cc" + break; + + case 175: // $@32: %empty +#line 747 "netconf_parser.yy" + { + ctx.unique("pattern", ctx.loc2pos(yystack_[0].location)); + ctx.enter(ctx.NO_KEYWORDS); +} +#line 1609 "netconf_parser.cc" + break; + + case 176: // pattern: "pattern" $@32 ":" "constant string" +#line 750 "netconf_parser.yy" + { + ElementPtr sev(new StringElement(yystack_[0].value.as < string > (), ctx.loc2pos(yystack_[0].location))); + ctx.stack_.back()->set("pattern", sev); + ctx.leave(); +} +#line 1619 "netconf_parser.cc" + break; + + +#line 1623 "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, 126, 126, 126, 127, 127, 128, 128, 136, 136, + 147, 153, 154, 155, 156, 157, 158, 159, 163, 163, + 174, 179, 180, 188, 193, 199, 204, 204, 210, 211, + 214, 218, 222, 234, 242, 242, 254, 254, 272, 275, + 280, 281, 284, 285, 286, 293, 294, 295, 296, 297, + 298, 299, 300, 301, 304, 310, 316, 322, 322, 347, + 347, 375, 375, 386, 387, 390, 391, 392, 397, 397, + 405, 406, 407, 410, 413, 414, 417, 417, 426, 426, + 437, 437, 448, 449, 452, 453, 454, 462, 463, 464, + 465, 466, 470, 470, 482, 482, 494, 494, 506, 506, + 518, 519, 520, 526, 527, 528, 529, 530, 531, 532, + 533, 537, 537, 547, 547, 559, 560, 561, 566, 567, + 568, 569, 570, 571, 574, 574, 583, 584, 585, 588, + 588, 598, 598, 611, 611, 624, 625, 626, 632, 632, + 640, 641, 642, 647, 648, 649, 650, 651, 652, 653, + 656, 656, 665, 671, 671, 680, 680, 691, 692, 693, + 698, 698, 706, 707, 708, 713, 714, 715, 716, 717, + 720, 720, 729, 735, 741, 747, 747 + }; + + 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 2308 "netconf_parser.cc" + +#line 756 "netconf_parser.yy" + + +void +isc::netconf::NetconfParser::error(const location_type& loc, + const 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..54266fa --- /dev/null +++ b/src/bin/netconf/netconf_parser.h @@ -0,0 +1,2390 @@ +// 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 <cc/data.h> +#include <netconf/parser_context_decl.h> + +#include <boost/lexical_cast.hpp> + +#include <sstream> +#include <string> + +using namespace isc::netconf; +using namespace isc::data; +using namespace std; + +#line 63 "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 212 "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 (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< 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, string&& v, location_type&& l) + : Base (t) + , value (std::move (v)) + , location (std::move (l)) + {} +#else + basic_symbol (typename Base::kind_type t, const 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< 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, string v, location_type l) + : super_type (token_kind_type (tok), std::move (v), std::move (l)) +#else + symbol_type (int tok, const 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 (string v, location_type l) + { + return symbol_type (token::TOKEN_STRING, std::move (v), std::move (l)); + } +#else + static + symbol_type + make_STRING (const 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< 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< 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 2386 "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..db3e79a --- /dev/null +++ b/src/bin/netconf/netconf_parser.yy @@ -0,0 +1,762 @@ +/* Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +%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 <cc/data.h> +#include <netconf/parser_context_decl.h> + +#include <boost/lexical_cast.hpp> + +#include <sstream> +#include <string> + +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> + +// Avoid warnings with the error counter. +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" +#endif +} + + +%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 <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 string& where = ctx.contextName(); + const 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")) { + 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")) { + 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 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..559c4e5 --- /dev/null +++ b/src/bin/netconf/netconf_process.cc @@ -0,0 +1,97 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <asiolink/asio_wrapper.h> +#include <asiolink/io_address.h> +#include <asiolink/io_error.h> +#include <cc/command_interpreter.h> +#include <config/timeouts.h> +#include <netconf/netconf.h> +#include <netconf/netconf_controller.h> +#include <netconf/netconf_log.h> +#include <netconf/netconf_process.h> + +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(CONTROL_RESULT_SUCCESS, "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 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..0c8b6ce --- /dev/null +++ b/src/bin/netconf/netconf_process.h @@ -0,0 +1,106 @@ +// 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_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. + void init() override final; + + /// @brief Implements the process's event loop. + /// + /// @throw DProcessBaseError if an operational error is encountered. + void run() override final; + + /// @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. + isc::data::ConstElementPtr + shutdown(isc::data::ConstElementPtr args) override final; + + /// @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. + isc::data::ConstElementPtr + configure(isc::data::ConstElementPtr config_set, + bool check_only = false) override final; + + /// @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_; +}; // NetconfProcess + +/// @brief Defines a shared pointer to NetconfProcess. +using NetconfProcessPtr = boost::shared_ptr<NetconfProcess>; + +} // namespace 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..d0e20fc --- /dev/null +++ b/src/bin/netconf/parser_context.cc @@ -0,0 +1,200 @@ +// 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 <cc/data.h> +#include <exceptions/exceptions.h> +#include <netconf/netconf_log.h> +#include <netconf/netconf_parser.h> +#include <netconf/parser_context.h> + +#include <fstream> +#include <limits> +#include <sstream> + +namespace isc { +namespace netconf { + +ParserContext::ParserContext() + : sfile_(0), ctx_(NO_KEYWORDS), trace_scanning_(false), trace_parsing_(false) +{ +} + +ParserContext::~ParserContext() +{ +} + +isc::data::ElementPtr +ParserContext::parseString(string const& str, ParserType parser_type) +{ + scanStringBegin(str, parser_type); + return (parseCommon()); +} + +isc::data::ElementPtr +ParserContext::parseFile(string const& 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, + string const& what, + size_t pos) +{ + if (pos == 0) { + isc_throw(ParseError, loc << ": " << what); + } else { + isc_throw(ParseError, loc << " (near " << pos << "): " << what); + } +} + +void +ParserContext::error(string const& what) +{ + isc_throw(ParseError, what); +} + +void +ParserContext::fatal(string const& what) +{ + isc_throw(ParseError, what); +} + +isc::data::Element::Position +ParserContext::loc2pos(isc::netconf::location& loc) +{ + string const& 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(string const& 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(string const& 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(); +} + +string const +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, + string const& what) { + 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..cb62152 --- /dev/null +++ b/src/bin/netconf/parser_context.h @@ -0,0 +1,313 @@ +// 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_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(); +}; // ParserContext + +} // namespace netconf +} // namespace isc + +#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..43cb515 --- /dev/null +++ b/src/bin/netconf/parser_context_decl.h @@ -0,0 +1,20 @@ +// 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_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; + +} // namespace dhcp +} // namespace isc + +#endif diff --git a/src/bin/netconf/simple_parser.cc b/src/bin/netconf/simple_parser.cc new file mode 100644 index 0000000..0e7b8f8 --- /dev/null +++ b/src/bin/netconf/simple_parser.cc @@ -0,0 +1,197 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <cc/data.h> +#include <cc/dhcp_config_error.h> +#include <hooks/hooks_manager.h> +#include <hooks/hooks_parser.h> +#include <netconf/netconf_config.h> +#include <netconf/simple_parser.h> + +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) { + ElementPtr mutable_servers(copy(servers, 0)); + for (auto it : mutable_servers->mapValue()) { + ElementPtr server(copy(it.second, 0)); + cnt += setServerDefaults(it.first, server); + mutable_servers->set(it.first, server); + } + global->set("managed-servers", mutable_servers); + } + + return (cnt); +} + +size_t NetconfSimpleParser::deriveParameters(ElementPtr global) { + size_t cnt = 0; + + // Now derive global parameters into managed-servers. + ConstElementPtr servers = global->get("managed-servers"); + if (servers) { + ElementPtr mutable_servers(copy(servers, 0)); + for (auto it : mutable_servers->mapValue()) { + ElementPtr mutable_server = copy(it.second, 0); + cnt += SimpleParser::deriveParams(global, + mutable_server, + INHERIT_TO_SERVERS); + mutable_servers->set(it.first, mutable_server); + } + global->set("managed-servers", mutable_servers); + } + + return (cnt); +} + +size_t +NetconfSimpleParser::setServerDefaults(const std::string name, + ElementPtr server) { + size_t cnt = 0; + + if (name == "dhcp4") { + cnt += setDefaults(server, DHCP4_DEFAULTS); + } else if (name == "dhcp6") { + cnt += setDefaults(server, DHCP6_DEFAULTS); + } else if (name == "d2") { + cnt += setDefaults(server, D2_DEFAULTS); + } else if (name == "ca") { + cnt += setDefaults(server, CA_DEFAULTS); + } + + ConstElementPtr ctrl_sock = server->get("control-socket"); + if (!ctrl_sock) { + return (cnt); + } + ElementPtr mutable_ctrl_sock(copy(ctrl_sock, 0)); + cnt += setDefaults(mutable_ctrl_sock, CTRL_SOCK_DEFAULTS); + server->set("control-socket", mutable_ctrl_sock); + + return (cnt); +} + +void +NetconfSimpleParser::parse(const NetconfConfigPtr& ctx, + const ElementPtr& 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(), false); + } + + 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(false); + } +} + +} // 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..61338c5 --- /dev/null +++ b/src/bin/netconf/simple_parser.h @@ -0,0 +1,74 @@ +// 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_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::ElementPtr 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::ElementPtr 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::ElementPtr& 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; +}; // NetconfSimpleParser + +} // namespace netconf +} // namespace isc +#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..7141d80 --- /dev/null +++ b/src/bin/netconf/stdout_control_socket.cc @@ -0,0 +1,58 @@ +// 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 <cc/command_interpreter.h> +#include <netconf/stdout_control_socket.h> + +#include <iostream> + +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(ElementPtr /*config*/, + const string& /*service*/) { + return (createAnswer()); +} + +ConstElementPtr +StdoutControlSocket::configSet(ElementPtr 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..cd03cbe --- /dev/null +++ b/src/bin/netconf/stdout_control_socket.h @@ -0,0 +1,89 @@ +// 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.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 + data::ConstElementPtr configGet(const std::string& service) override final; + + /// @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). + data::ConstElementPtr configTest(data::ElementPtr config, + const std::string& service) override final; + + /// @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). + data::ConstElementPtr configSet(data::ElementPtr config, + const std::string& service) override final; + +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_; +}; // StdoutControlSocket + +/// @brief Type definition for the pointer to the @c StdoutControlSocket. +using StdoutControlSocketPtr = std::shared_ptr<StdoutControlSocket>; + +/// @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..41e394c --- /dev/null +++ b/src/bin/netconf/tests/Makefile.am @@ -0,0 +1,94 @@ +SUBDIRS = . shtests + +EXTRA_DIST = testdata/get_config.json + +AM_CPPFLAGS = +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 += -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/netconf\" +AM_CPPFLAGS += -DKEATEST_MODULE +AM_CPPFLAGS += -DSYNTAX_FILE=\"$(abs_srcdir)/../netconf_parser.yy\" +AM_CPPFLAGS += -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/netconf/tests\" +AM_CPPFLAGS += $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) +AM_CPPFLAGS += $(LIBYANG_CPPFLAGS) +AM_CPPFLAGS += $(LIBYANG_INCLUDEDIR) +AM_CPPFLAGS += $(LIBYANGCPP_CPPFLAGS) +AM_CPPFLAGS += $(LIBYANGCPP_INCLUDEDIR) +AM_CPPFLAGS += $(SYSREPO_CPPFLAGS) +AM_CPPFLAGS += $(SYSREPO_INCLUDEDIR) +AM_CPPFLAGS += $(SYSREPOCPP_CPPFLAGS) +AM_CPPFLAGS += $(SYSREPOCPP_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/yang/testutils/libyangtest.la +netconf_unittests_LDADD += $(top_builddir)/src/lib/yang/libkea-yang.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/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) +netconf_unittests_LDADD += $(LIBYANG_LIBS) +netconf_unittests_LDADD += $(LIBYANGCPP_LIBS) +netconf_unittests_LDADD += $(SYSREPO_LIBS) +netconf_unittests_LDADD += $(SYSREPOCPP_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..309a963 --- /dev/null +++ b/src/bin/netconf/tests/Makefile.in @@ -0,0 +1,1244 @@ +# 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_cpp20.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_netconf.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = 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/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) $(am__DEPENDENCIES_1) \ +@HAVE_GTEST_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +netconf_unittests_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) $(netconf_unittests_LDFLAGS) \ + $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libbasic_la-basic_library.Plo \ + ./$(DEPDIR)/netconf_unittests-control_socket_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-get_config_unittest.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_cfg_mgr_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_controller_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_process_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-netconf_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-parser_unittests.Po \ + ./$(DEPDIR)/netconf_unittests-run_unittests.Po +am__mv = mv -f +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libbasic_la_SOURCES) $(netconf_unittests_SOURCES) \ + $(nodist_netconf_unittests_SOURCES) +DIST_SOURCES = $(am__libbasic_la_SOURCES_DIST) \ + $(am__netconf_unittests_SOURCES_DIST) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/test_data_files_config.h.in \ + $(srcdir)/test_libraries.h.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@ +DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@ +DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_NETCONF = @HAVE_NETCONF@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@ +LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@ +LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@ +LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@ +LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@ +LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@ +LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@ +LIBYANG_LIBS = @LIBYANG_LIBS@ +LIBYANG_PREFIX = @LIBYANG_PREFIX@ +LIBYANG_VERSION = @LIBYANG_VERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_PLUGINS_PATH = @SR_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@ +SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@ +SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@ +SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_PREFIX = @SYSREPO_PREFIX@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . 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 \ + -DCFG_EXAMPLES=\"$(abs_top_srcdir)/doc/examples/netconf\" \ + -DKEATEST_MODULE \ + -DSYNTAX_FILE=\"$(abs_srcdir)/../netconf_parser.yy\" \ + -DTEST_DATA_BUILDDIR=\"$(abs_top_builddir)/src/bin/netconf/tests\" \ + $(BOOST_INCLUDES) $(CRYPTO_CFLAGS) $(CRYPTO_INCLUDES) \ + $(LIBYANG_CPPFLAGS) $(LIBYANG_INCLUDEDIR) \ + $(LIBYANGCPP_CPPFLAGS) $(LIBYANGCPP_INCLUDEDIR) \ + $(SYSREPO_CPPFLAGS) $(SYSREPO_INCLUDEDIR) \ + $(SYSREPOCPP_CPPFLAGS) $(SYSREPOCPP_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) +@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/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) $(LIBYANG_LIBS) \ +@HAVE_GTEST_TRUE@ $(LIBYANGCPP_LIBS) $(SYSREPO_LIBS) \ +@HAVE_GTEST_TRUE@ $(SYSREPOCPP_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..0838586 --- /dev/null +++ b/src/bin/netconf/tests/basic_library.cc @@ -0,0 +1,71 @@ +// 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 +/// @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); +} + +} // extern "C" +} // namespace 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..22beff6 --- /dev/null +++ b/src/bin/netconf/tests/control_socket_unittests.cc @@ -0,0 +1,863 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <gtest/gtest.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 <netconf/http_control_socket.h> +#include <netconf/netconf_config.h> +#include <netconf/stdout_control_socket.h> +#include <netconf/unix_control_socket.h> +#include <testutils/sandbox.h> +#include <testutils/threaded_test.h> +#include <yang/tests/sysrepo_setup.h> + +#include <iostream> +#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. +using ThreadPtr = shared_ptr<thread>; + +//////////////////////////////// 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) { + } +}; // TestStdoutControlSocket + +/// @brief Type definition for the pointer to the @c TestStdoutControlSocket. +using TestStdoutControlSocketPtr = shared_ptr<TestStdoutControlSocket>; + +// 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 = + 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_MSG(scs->configGet("foo"), NotImplemented, + "No config-get for stdout control socket"); +} + +// 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(ElementPtr(), "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); + ElementPtr 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() { + string socket_path; + const char* env = getenv("KEA_SOCKET_TEST_DIR"); + if (env) { + socket_path = 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_; +}; // UnixControlSocketTest + +/// @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_LOG(acceptor.open()); + boost::asio::local::stream_protocol::endpoint + endpoint(unixSocketFilePath()); + EXPECT_NO_THROW_LOG(acceptor.bind(endpoint)); + EXPECT_NO_THROW_LOG(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_LOG(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 = + 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_LOG(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. + ElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + ConstElementPtr reflected; + EXPECT_NO_THROW_LOG(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. + ElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + ConstElementPtr reflected; + EXPECT_NO_THROW_LOG(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_MSG(ucs->configGet("foo"), ControlSocketError, + "communication error: No such file or directory"); + 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. +using Response = TestHttpResponseBase<HttpResponseJson>; + +/// @brief Pointer to test HTTP JSON response. +using ResponsePtr = boost::shared_ptr<Response>; + +/// @brief Generic test HTTP response. +using GenericResponse = TestHttpResponseBase<HttpResponse>; + +/// @brief Pointer to generic test HTTP response. +using GenericResponsePtr = boost::shared_ptr<GenericResponse>; + +/// @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. + HttpRequestPtr createNewHttpRequest() const override final { + return (HttpRequestPtr(new PostHttpRequestJson())); + } + +protected: + /// @brief Creates HTTP response. + /// + /// @param request Pointer to the HTTP request. + /// @return Pointer to the generated HTTP response. + HttpResponsePtr + createStockHttpResponse(const HttpRequestPtr& request, + const HttpStatusCode& status_code) const override final { + // 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) override { + // 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); + } +}; // TestHttpResponseCreator + +/// @brief Implementation of the test HttpResponseCreatorFactory. +class TestHttpResponseCreatorFactory : public HttpResponseCreatorFactory { +public: + + /// @brief Creates @ref TestHttpResponseCreator instance. + HttpResponseCreatorPtr create() const override final { + HttpResponseCreatorPtr response_creator(new TestHttpResponseCreator()); + return (response_creator); + } +}; // TestHttpResponseCreatorFactory + +/// @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() { + // If the thread is ready to go, start the listener. + if (listener_) { + ASSERT_NO_THROW_LOG(listener_->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(); + } + + /// @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_; +}; // HttpControlSocketTest + +/// @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 = + 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_LOG(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_LOG(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. + ElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configTest. + ConstElementPtr reflected; + EXPECT_NO_THROW_LOG(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. + ElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configTest. + ConstElementPtr reflected; + EXPECT_NO_THROW_LOG(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. + ElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configSet. + ConstElementPtr reflected; + EXPECT_NO_THROW_LOG(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. + ElementPtr json = Element::fromJSON("{ \"bar\": 1 }"); + + // Try configSet. + ConstElementPtr reflected; + EXPECT_NO_THROW_LOG(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 (exception const& 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. + ElementPtr json = Element::fromJSON("{ \"want-partial\": true }"); + + // Warn this makes time. + cout << "Waiting 2s..." << 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 (exception const& 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..2eaa7b1 --- /dev/null +++ b/src/bin/netconf/tests/get_config_unittest.cc @@ -0,0 +1,291 @@ +// 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 <gtest/gtest.h> + +#include <cc/command_interpreter.h> +#include <cc/data.h> +#include <netconf/netconf_cfg_mgr.h> +#include <netconf/parser_context.h> +#include <process/testutils/d_test_stubs.h> +#include <testutils/gtest_utils.h> +#include <testutils/user_context_utils.h> + +#include <fstream> +#include <iostream> +#include <string> + +#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 +string +readFile(const string& file_path) { + ifstream ifs(file_path); + if (!ifs.is_open()) { + ADD_FAILURE() << "readFile cannot open " << file_path; + isc_throw(isc::Unexpected, "readFile cannot open " << file_path); + } + string lines; + string line; + while (getline(ifs, line)) { + lines += line + "\n"; + } + ifs.close(); + return (lines); +} + +/// @brief Runs parser in JSON mode +ElementPtr +parseJSON(const string& in, bool verbose = false) { + try { + ParserContext ctx; + return (ctx.parseString(in, ParserContext::PARSER_JSON)); + } catch (exception const& ex) { + if (verbose) { + cout << "EXCEPTION: " << ex.what() << endl; + } + throw; + } +} + +/// @brief Runs parser in NETCONF mode +ElementPtr +parseNETCONF(const string& in, bool verbose = false) { + try { + ParserContext ctx; + return (ctx.parseString(in, ParserContext::PARSER_NETCONF)); + } catch (exception const& ex) { + if (verbose) { + cout << "EXCEPTION: " << ex.what() << 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); + 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; +}; // NakedNetconfCfgMgr + +} // 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 string& config, const char* operation) { + // try JSON parser + ElementPtr json; + try { + json = parseJSON(config, true); + } catch (exception const& 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 (exception const& 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")); + } + + unique_ptr<NakedNetconfCfgMgr> srv_; ///< Netconf server under test + int rcode_; ///< Return code from element parsing + ConstElementPtr comment_; ///< Reason for parse fail +}; // NetconfGetCfgTest + +// Test a simple configuration. +TEST_F(NetconfGetCfgTest, simple) { + + // get the simple configuration + string simple_file = string(CFG_EXAMPLES) + "/" + "simple-dhcp4.json"; + string config; + ASSERT_NO_THROW_LOG(config = readFile(simple_file)); + + // get the expected configuration + string expected_file = + string(NETCONF_TEST_DATA_DIR) + "/" + "get_config.json"; + 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(); + ElementPtr unparsed; + ASSERT_NO_THROW_LOG(unparsed = context->toElement()); + + // dump if wanted else check + if (generate_action) { + cerr << "// Generated Configuration (remove this line)\n"; + ASSERT_NO_THROW_LOG(expected = prettyPrint(unparsed)); + prettyPrint(unparsed, cerr, 0, 4); + 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 + string current = prettyPrint(unparsed, 0, 4); + 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(); + ElementPtr 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..897e292 --- /dev/null +++ b/src/bin/netconf/tests/netconf_cfg_mgr_unittests.cc @@ -0,0 +1,737 @@ +// 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 <gtest/gtest.h> + +#include <cc/command_interpreter.h> +#include <exceptions/exceptions.h> +#include <netconf/netconf_cfg_mgr.h> +#include <netconf/parser_context.h> +#include <netconf/tests/test_libraries.h> +#include <process/d_cfg_mgr.h> +#include <process/testutils/d_test_stubs.h> +#include <testutils/gtest_utils.h> +#include <testutils/test_to_element.h> +#include <yang/yang_models.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; +}; // NakedNetconfCfgMgr + +// Tests construction of NetconfCfgMgr class. +TEST(NetconfCfgMgr, construction) { + unique_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_LOG(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_LOG(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_FALSE(ctx.getCfgServersMap()->contains("dhcp4")); + + // Now set the v6 server and sanity check again + EXPECT_NO_THROW_LOG(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_LOG(ctx.getCfgServersMap()->insert(make_pair("dhcp4", server3))); + EXPECT_NO_THROW_LOG(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", ElementPtr()); + 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. + ElementPtr 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. + EXPECT_THROW_MSG(ctx.extractConfiguredGlobals(Element::create(777)), BadValue, + "extractConfiguredGlobals must be given a map element"); + + // 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. + ElementPtr 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" + "}" +}; // NETCONF_CONFIGS + +// 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) { + ElementPtr json; + ParserContext parser; + EXPECT_NO_THROW_LOG(json = parser.parseString(NETCONF_CONFIGS[10], + ParserContext::PARSER_JSON)); + ConstElementPtr answer; + NakedNetconfCfgMgr cfg_mgr; + EXPECT_NO_THROW_LOG(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; + ElementPtr json = parser.parseString(config, ParserContext::PARSER_SUB_NETCONF); + + EXPECT_NO_THROW_LOG(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_; +}; // NetconfParserTest + +// 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..03002f7 --- /dev/null +++ b/src/bin/netconf/tests/netconf_controller_unittests.cc @@ -0,0 +1,189 @@ +// 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 <gtest/gtest.h> + +#include <asiolink/testutils/timed_signal.h> +#include <cc/data.h> +#include <netconf/netconf_controller.h> +#include <netconf/netconf_process.h> +#include <process/testutils/d_test_stubs.h> +#include <testutils/gtest_utils.h> + +using namespace isc::asiolink::test; +using namespace isc::netconf; +using namespace isc::data; +using namespace isc::http; +using namespace isc::process; +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); + } +}; // NetconfControllerTest + +// 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_LOG(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_MSG(parseArgs(argc, argv2), InvalidUsage, "unsupported option: [x] "); +} + +// 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..b71fdad --- /dev/null +++ b/src/bin/netconf/tests/netconf_process_unittests.cc @@ -0,0 +1,85 @@ +// 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 <gtest/gtest.h> + +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> +#include <netconf/netconf_cfg_mgr.h> +#include <netconf/netconf_process.h> +#include <process/testutils/d_test_stubs.h> +#include <testutils/gtest_utils.h> + +#include <functional> + +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 Callback that will invoke shutdown method. + void genShutdownCallback() { + shutdown(isc::data::ElementPtr()); + } +}; // NetconfProcessTest + +// 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_MSG(NetconfProcess("TestProcess", lcl_io_service), DProcessBaseError, + "IO Service cannot be null"); + + // 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_LOG(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..b1bff51 --- /dev/null +++ b/src/bin/netconf/tests/netconf_unittests.cc @@ -0,0 +1,1174 @@ +// 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 <gtest/gtest.h> + +#include <asiolink/asio_wrapper.h> +#include <asiolink/interval_timer.h> +#include <asiolink/io_service.h> +#include <cc/command_interpreter.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 <testutils/log_utils.h> +#include <testutils/sandbox.h> +#include <testutils/threaded_test.h> +#include <yang/tests/sysrepo_setup.h> +#include <yang/testutils/translator_test.h> +#include <yang/translator_config.h> +#include <yang/yang_models.h> +#include <yang/yang_revisions.h> + +#include <sysrepo-cpp/utils/exception.hpp> + +#include <chrono> +#include <iostream> +#include <sstream> +#include <thread> +#include <vector> + +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 libyang; +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. +using ThreadPtr = shared_ptr<thread>; + +/// @brief Test version of the NetconfAgent class. +class NakedNetconfAgent : public NetconfAgent { +public: + /// Export protected methods and fields. + using NetconfAgent::keaConfig; + using NetconfAgent::initSysrepo; + using NetconfAgent::checkModule; + using NetconfAgent::checkModules; + using NetconfAgent::yangConfig; + using NetconfAgent::subscribeToDataChanges; + using NetconfAgent::startup_sess_; + using NetconfAgent::running_sess_; + using NetconfAgent::modules_; + using NetconfAgent::subscriptions_; +}; // NakedNetconfAgent + +/// @brief Type definition for the pointer to NakedNetconfAgent objects. +using NakedNetconfAgentPtr = shared_ptr<NakedNetconfAgent>; + +/// @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_LOG(agent->startup_sess_->deleteItem(xpath)); + EXPECT_NO_THROW_LOG(agent->startup_sess_->applyChanges()); + } +} + +// Empirically the requested subnets have sometimes returned in decreasing +// order of subnet ID. To avoid flaky test failures, sort them before +// comparing. +ElementPtr sortSubnets(ElementPtr const& map) { + ElementPtr arguments(copy(map->get("arguments"), 0)); + ElementPtr dhcp4(copy(arguments->get("Dhcp4"), 0)); + ElementPtr subnet4(copy(dhcp4->get("subnet4"))); + + boost::dynamic_pointer_cast<ListElement>(subnet4)->sort("id"); + + ElementPtr result(copy(map)); + result->set("arguments", arguments); + arguments->set("Dhcp4", dhcp4); + dhcp4->set("subnet4", subnet4); + return result; +} + +/// @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() { + string socket_path; + const char* env = getenv("KEA_SOCKET_TEST_DIR"); + if (env) { + socket_path = 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_; +}; // NetconfAgentTest + +/// @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). + sysrepo::ErrorCode callback(Session sess, + uint32_t /* subscription_id */, + string_view module_name, + optional<string_view> /* sub_xpath */, + Event /* event */, + uint32_t /* request_id */) { + NetconfAgent::logChanges(sess, module_name); + finished_ = true; + return (sysrepo::ErrorCode::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() { + auto const timeout(2s); + cout << "Waiting 2s for callback..." << endl; + auto const start(chrono::steady_clock::now()); + while (!finished_) { + auto const duration(chrono::steady_clock::now() - start); + if (timeout < duration) { + FAIL() << "Timeout of 2s expired while waiting for callback."; + } + this_thread::sleep_for(1ms); + } + } + + /// @brief To know when the callback was called. + atomic<bool> finished_; + + /// @brief IOService object. + IOServicePtr io_service_; + + /// @brief Pointer to server thread. + ThreadPtr thread_; + + /// @brief Test netconf agent. + NakedNetconfAgentPtr agent_; +}; // NetconfAgentLogTest + +/// @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_LOG(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_LOG(acceptor.bind(endpoint)); + EXPECT_NO_THROW_LOG(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); + ElementPtr json; + EXPECT_NO_THROW_LOG(json = Element::fromJSON(rbuf)); + EXPECT_TRUE(json); + string command; + ElementPtr config; + if (json) { + ConstElementPtr arg; + EXPECT_NO_THROW_LOG(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_LOG(socket.close()); + } + EXPECT_NO_THROW_LOG(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_LOG(agent_->initSysrepo()); + 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) { + // Various modules should be available. + EXPECT_EQ(1, YANG_REVISIONS.count("keatest-module")); + ASSERT_EQ(1, YANG_REVISIONS.count("kea-dhcp4-server")); + ASSERT_EQ(1, YANG_REVISIONS.count("kea-dhcp6-server")); + + // Non-existing modules should not. + EXPECT_EQ(0, agent_->modules_.count("does-not-exist")); + + // kea-dhcp[46]-server should be available. + EXPECT_NO_THROW_LOG(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_LOG(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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", LeafBaseType::String, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, 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_)); + + // Subscribe configuration changes. + auto cb = [=, this](Session session, uint32_t subscription_id, string_view module_name, + optional<string_view> sub_xpath, Event event, uint32_t request_id) { + return callback(session, subscription_id, module_name, sub_xpath, event, request_id); + }; + SubscribeOptions const options(SubscribeOptions::Default | SubscribeOptions::DoneOnly); + optional<Subscription> subscription; + EXPECT_NO_THROW_LOG(subscription = agent_->running_sess_->onModuleChange(KEA_DHCP4_SERVER, cb, + nullopt, 0, + options)); + 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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.1.0/24", LeafBaseType::String, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, true } + }); + EXPECT_NO_THROW_LOG(repr.set(tree1, *agent_->running_sess_)); + + // Check that the debug output was correct. + addString("NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: created: " + "/kea-dhcp4-server:config/subnet4[id='2'] (list)"); + 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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", LeafBaseType::String, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, 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_)); + + // Subscribe configuration changes. + auto cb = [=, this](Session session, uint32_t subscription_id, string_view module_name, + optional<string_view> sub_xpath, Event event, uint32_t request_id) { + return callback(session, subscription_id, module_name, sub_xpath, event, request_id); + }; + SubscribeOptions const options(SubscribeOptions::Default | SubscribeOptions::DoneOnly); + optional<Subscription> subscription; + EXPECT_NO_THROW_LOG(subscription = agent_->running_sess_->onModuleChange(KEA_DHCP4_SERVER, cb, + nullopt, 0, + options)); + 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_LOG(agent_->running_sess_->deleteItem(xpath)); + const YRTree tree1 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config/subnet4[id='10']/id", + "10", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='10']/subnet", + "10.0.0.0/24", LeafBaseType::String, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, true } + }); + EXPECT_NO_THROW_LOG(repr.set(tree1, *agent_->running_sess_)); + + // Check that the debug output was correct. + addString( + "NETCONF_CONFIG_CHANGED_DETAIL YANG configuration changed: deleted: " + "/kea-dhcp4-server:config/subnet4[id='1'] (list)"); + 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_LOG(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 = copy(netconf_json, 0); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW_LOG(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_LOG(agent_->keaConfig(service_pair)); + + // Wait server to be stopped. + waitStopped(); + + // Check request. + ASSERT_EQ(1, requests_.size()); + const string& request_str = requests_[0]; + ElementPtr request; + ASSERT_NO_THROW_LOG(request = Element::fromJSON(request_str)); + string expected_str = "{\n" + "\"command\": \"config-get\"\n" + "}"; + ElementPtr 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]; + ElementPtr 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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", LeafBaseType::String, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, 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_)); + + // 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_LOG(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 = copy(netconf_json, 0); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW_LOG(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_LOG(agent_->yangConfig(service_pair)); + + // Wait server to be stopped. + waitStopped(); + + // Check request. + ASSERT_EQ(1, requests_.size()); + const string& request_str = requests_[0]; + ElementPtr 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" + "}"; + ElementPtr expected; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + + expected = sortSubnets(expected); + request = sortSubnets(request); + EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check response. + ASSERT_EQ(1, responses_.size()); + const string& response_str = responses_[0]; + ElementPtr 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 subscribeToDataChanges method works as expected. +TEST_F(NetconfAgentTest, subscribeToDataChanges) { + // 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_LOG(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 = copy(netconf_json, 0); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW_LOG(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 subscribeToDataChanges. + EXPECT_EQ(0, agent_->subscriptions_.size()); + ASSERT_NO_THROW_LOG(agent_->initSysrepo()); + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW_LOG(agent_->subscribeToDataChanges(service_pair)); + EXPECT_EQ(1, agent_->subscriptions_.size()); + + /// Unsubscribe. + EXPECT_NO_THROW_LOG(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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", LeafBaseType::String, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, 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_)); + + // 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_LOG(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 = copy(netconf_json, 0); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW_LOG(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 to YANG changes. + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW_LOG(agent_->subscribeToDataChanges(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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.1.0/24", LeafBaseType::String, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, true } + }); + EXPECT_NO_THROW_LOG(repr.set(tree1, *agent_->running_sess_)); + + // Wait server to be stopped. + waitStopped(); + + // Check request. + ASSERT_EQ(1, requests_.size()); + const string& request_str = requests_[0]; + ElementPtr 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" + "}"; + ElementPtr expected; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + + expected = sortSubnets(expected); + request = sortSubnets(request); + EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check response. + ASSERT_EQ(1, responses_.size()); + const string& response_str = responses_[0]; + ElementPtr 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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", LeafBaseType::String, true }, + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, 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_)); + + // 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_LOG(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 = copy(netconf_json, 0); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW_LOG(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 to YANG changes. + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW_LOG(agent_->subscribeToDataChanges(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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.1.0/24", LeafBaseType::String, true }, // The change is here! + { "/kea-dhcp4-server:config/subnet4[id='2']/id", + "2", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='2']/subnet", + "10.0.2.0/24", LeafBaseType::String, true } + }); + EXPECT_NO_THROW_LOG(repr.set(tree1, *agent_->running_sess_)); + + // 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]; + ElementPtr 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" + "}"; + ElementPtr expected; + ASSERT_NO_THROW_LOG(expected = Element::fromJSON(expected_str)); + + expected = sortSubnets(expected); + request = 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)); + + expected = sortSubnets(expected); + request = sortSubnets(request); + EXPECT_EQ(prettyPrint(expected), prettyPrint(request)); + + // Check responses. + ASSERT_EQ(2, responses_.size()); + string response_str = responses_[0]; + ElementPtr 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/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", LeafBaseType::String, 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_)); + + // 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_LOG(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 = copy(netconf_json, 0); + ASSERT_TRUE(json); + NetconfSimpleParser::setAllDefaults(json); + NetconfSimpleParser::deriveParameters(json); + NetconfSimpleParser parser; + EXPECT_NO_THROW_LOG(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 to YANG changes. + EXPECT_EQ(0, agent_->subscriptions_.size()); + EXPECT_NO_THROW_LOG(agent_->subscribeToDataChanges(service_pair)); + EXPECT_EQ(1, agent_->subscriptions_.size()); + + // Change configuration (add invalid user context). + const YRTree tree1 = YangRepr::buildTreeFromVector({ + { "/kea-dhcp4-server:config/subnet4[id='1']/id", + "1", LeafBaseType::Uint32, false }, + { "/kea-dhcp4-server:config/subnet4[id='1']/subnet", + "10.0.0.0/24", LeafBaseType::String, true }, + { "/kea-dhcp4-server:config/subnet4[id='1']/user-context", + "BOGUS", LeafBaseType::String, true } + }); + EXPECT_THROW_MSG(repr.set(tree1, *agent_->running_sess_), sysrepo::Error, + "Session::applyChanges: Couldn't apply changes: SR_ERR_CALLBACK_FAILED"); +} + +} // 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..ddb37d3 --- /dev/null +++ b/src/bin/netconf/tests/parser_unittests.cc @@ -0,0 +1,986 @@ +// Copyright (C) 2018-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <gtest/gtest.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/test_to_element.h> +#include <testutils/user_context_utils.h> + +#include <iostream> +#include <fstream> +#include <vector> + +#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()) +#ifdef HAVE_CREATE_UNIFIED_DIFF + << "\nDiff:\n" << generateDiff(prettyPrint(a), prettyPrint(b)) << "\n" +#endif + ; +} + +/// @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 string& txt, ParserContext::ParserType parser_type, + bool compare = true) { + SCOPED_TRACE("\n* Tested config: \n---\n" + txt + "\n---"); + + ConstElementPtr test_json; + ASSERT_NO_THROW_LOG({ + try { + ParserContext ctx; + test_json = ctx.parseString(txt, parser_type); + } catch (exception const &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 string& fname) { + ElementPtr json; + ElementPtr reference_json; + ConstElementPtr test_json; + + string decommented = decommentJSONfile(fname); + + cout << "Parsing file " << fname << "(" << decommented << ")" << endl; + + EXPECT_NO_THROW_LOG(json = Element::fromJSONFile(decommented, true)); + reference_json = moveComments(json); + + // remove the temporary file + EXPECT_NO_THROW_LOG(::remove(decommented.c_str())); + + EXPECT_NO_THROW_LOG( + try { + ParserContext ctx; + test_json = ctx.parseFile(fname, ParserContext::PARSER_NETCONF); + } catch (exception const &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 string& txt, + ParserContext::ParserType parser_type, + const string& msg) { + SCOPED_TRACE("\n* Tested config: \n---\n" + txt + "\n---"); + + ParserContext ctx; + EXPECT_THROW_MSG(ctx.parseString(txt, parser_type), ParseError, msg); +} + +// 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 (exception const &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 (exception const &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_LOG(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. + using KeywordSet = set<string>; + + // 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(); + SCOPED_TRACE("\n* Tested config: \n---\n" + json->str() + "\n---"); + + 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); +} + +// 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_LOG(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; + } + + SCOPED_TRACE("\n* Tested duplicate element: " + elem.first); + + // Perform tests. + string dup = elem.first + "DDDD"; + json->set(dup, elem.second); + testDuplicate(config); + json->remove(dup); + ++cnt; + + // Recursive call. + ElementPtr mutable_json(copy(elem.second, 0)); + json->set(elem.first, mutable_json); + 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); + } +}; // TrailingCommasTest + +// 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..15391d2 --- /dev/null +++ b/src/bin/netconf/tests/run_unittests.cc @@ -0,0 +1,28 @@ +// 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 <gtest/gtest.h> + +#include <log/logger_support.h> +#include <util/unittests/run_all.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..718095b --- /dev/null +++ b/src/bin/netconf/tests/shtests/Makefile.am @@ -0,0 +1,19 @@ +SUBDIRS = . + +# Shell tests +SHTESTS = netconf_tests.sh + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) + +if HAVE_GTEST + +# Run shell tests on "make check". +check_SCRIPTS = $(SHTESTS) +TESTS = $(SHTESTS) + +endif + +# 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..3c5ad29 --- /dev/null +++ b/src/bin/netconf/tests/shtests/Makefile.in @@ -0,0 +1,865 @@ +# 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_cpp20.m4 \ + $(top_srcdir)/m4macros/ax_crypto.m4 \ + $(top_srcdir)/m4macros/ax_find_library.m4 \ + $(top_srcdir)/m4macros/ax_gssapi.m4 \ + $(top_srcdir)/m4macros/ax_gtest.m4 \ + $(top_srcdir)/m4macros/ax_isc_rpath.m4 \ + $(top_srcdir)/m4macros/ax_netconf.m4 \ + $(top_srcdir)/m4macros/libtool.m4 \ + $(top_srcdir)/m4macros/ltoptions.m4 \ + $(top_srcdir)/m4macros/ltsugar.m4 \ + $(top_srcdir)/m4macros/ltversion.m4 \ + $(top_srcdir)/m4macros/lt~obsolete.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = netconf_tests.sh +CONFIG_CLEAN_VPATH_FILES = +SCRIPTS = $(noinst_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/netconf_tests.sh.in +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BOOST_INCLUDES = @BOOST_INCLUDES@ +BOOST_LIBS = @BOOST_LIBS@ +BOTAN_TOOL = @BOTAN_TOOL@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONTRIB_DIR = @CONTRIB_DIR@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPTO_CFLAGS = @CRYPTO_CFLAGS@ +CRYPTO_INCLUDES = @CRYPTO_INCLUDES@ +CRYPTO_LDFLAGS = @CRYPTO_LDFLAGS@ +CRYPTO_LIBS = @CRYPTO_LIBS@ +CRYPTO_PACKAGE = @CRYPTO_PACKAGE@ +CRYPTO_RPATH = @CRYPTO_RPATH@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DISTCHECK_BOOST_CONFIGURE_FLAG = @DISTCHECK_BOOST_CONFIGURE_FLAG@ +DISTCHECK_CONTRIB_CONFIGURE_FLAG = @DISTCHECK_CONTRIB_CONFIGURE_FLAG@ +DISTCHECK_CRYPTO_CONFIGURE_FLAG = @DISTCHECK_CRYPTO_CONFIGURE_FLAG@ +DISTCHECK_GSSAPI_CONFIGURE_FLAG = @DISTCHECK_GSSAPI_CONFIGURE_FLAG@ +DISTCHECK_GTEST_CONFIGURE_FLAG = @DISTCHECK_GTEST_CONFIGURE_FLAG@ +DISTCHECK_KEA_SHELL_CONFIGURE_FLAG = @DISTCHECK_KEA_SHELL_CONFIGURE_FLAG@ +DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG = @DISTCHECK_LIBYANGCPP_CONFIGURE_FLAG@ +DISTCHECK_LIBYANG_CONFIGURE_FLAG = @DISTCHECK_LIBYANG_CONFIGURE_FLAG@ +DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG = @DISTCHECK_LOG4CPLUS_CONFIGURE_FLAG@ +DISTCHECK_MYSQL_CONFIGURE_FLAG = @DISTCHECK_MYSQL_CONFIGURE_FLAG@ +DISTCHECK_PERFDHCP_CONFIGURE_FLAG = @DISTCHECK_PERFDHCP_CONFIGURE_FLAG@ +DISTCHECK_PGSQL_CONFIGURE_FLAG = @DISTCHECK_PGSQL_CONFIGURE_FLAG@ +DISTCHECK_PREMIUM_CONFIGURE_FLAG = @DISTCHECK_PREMIUM_CONFIGURE_FLAG@ +DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG = @DISTCHECK_SYSREPOCPP_CONFIGURE_FLAG@ +DISTCHECK_SYSREPO_CONFIGURE_FLAG = @DISTCHECK_SYSREPO_CONFIGURE_FLAG@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GENHTML = @GENHTML@ +GREP = @GREP@ +GSSAPI_CFLAGS = @GSSAPI_CFLAGS@ +GSSAPI_LIBS = @GSSAPI_LIBS@ +GTEST_CONFIG = @GTEST_CONFIG@ +GTEST_INCLUDES = @GTEST_INCLUDES@ +GTEST_LDADD = @GTEST_LDADD@ +GTEST_LDFLAGS = @GTEST_LDFLAGS@ +GTEST_SOURCE = @GTEST_SOURCE@ +HAVE_NETCONF = @HAVE_NETCONF@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KEA_CXXFLAGS = @KEA_CXXFLAGS@ +KEA_SRCID = @KEA_SRCID@ +KRB5_CONFIG = @KRB5_CONFIG@ +LCOV = @LCOV@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBYANGCPP_CPPFLAGS = @LIBYANGCPP_CPPFLAGS@ +LIBYANGCPP_INCLUDEDIR = @LIBYANGCPP_INCLUDEDIR@ +LIBYANGCPP_LIBS = @LIBYANGCPP_LIBS@ +LIBYANGCPP_PREFIX = @LIBYANGCPP_PREFIX@ +LIBYANGCPP_VERSION = @LIBYANGCPP_VERSION@ +LIBYANG_CPPFLAGS = @LIBYANG_CPPFLAGS@ +LIBYANG_INCLUDEDIR = @LIBYANG_INCLUDEDIR@ +LIBYANG_LIBS = @LIBYANG_LIBS@ +LIBYANG_PREFIX = @LIBYANG_PREFIX@ +LIBYANG_VERSION = @LIBYANG_VERSION@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOG4CPLUS_INCLUDES = @LOG4CPLUS_INCLUDES@ +LOG4CPLUS_LIBS = @LOG4CPLUS_LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_TYPE = @PACKAGE_VERSION_TYPE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PDFLATEX = @PDFLATEX@ +PERL = @PERL@ +PGSQL_CPPFLAGS = @PGSQL_CPPFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PKGPYTHONDIR = @PKGPYTHONDIR@ +PKG_CONFIG = @PKG_CONFIG@ +PLANTUML = @PLANTUML@ +PREMIUM_DIR = @PREMIUM_DIR@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +SED = @SED@ +SEP = @SEP@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SPHINXBUILD = @SPHINXBUILD@ +SRPD_PLUGINS_PATH = @SRPD_PLUGINS_PATH@ +SR_PLUGINS_PATH = @SR_PLUGINS_PATH@ +SR_REPO_PATH = @SR_REPO_PATH@ +STRIP = @STRIP@ +SYSREPOCPP_CPPFLAGS = @SYSREPOCPP_CPPFLAGS@ +SYSREPOCPP_INCLUDEDIR = @SYSREPOCPP_INCLUDEDIR@ +SYSREPOCPP_LIBS = @SYSREPOCPP_LIBS@ +SYSREPOCPP_PREFIX = @SYSREPOCPP_PREFIX@ +SYSREPOCPP_VERSION = @SYSREPOCPP_VERSION@ +SYSREPO_CPPFLAGS = @SYSREPO_CPPFLAGS@ +SYSREPO_INCLUDEDIR = @SYSREPO_INCLUDEDIR@ +SYSREPO_LIBS = @SYSREPO_LIBS@ +SYSREPO_PREFIX = @SYSREPO_PREFIX@ +SYSREPO_VERSION = @SYSREPO_VERSION@ +USE_LCOV = @USE_LCOV@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WARNING_GCC_44_STRICT_ALIASING_CFLAG = @WARNING_GCC_44_STRICT_ALIASING_CFLAG@ +YACC = @YACC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +SUBDIRS = . + +# Shell tests +SHTESTS = netconf_tests.sh + +# As with every file generated by ./configure, clean them up when running +# "make distclean", but not on "make clean". +DISTCLEANFILES = $(SHTESTS) + +# Run shell tests on "make check". +@HAVE_GTEST_TRUE@check_SCRIPTS = $(SHTESTS) +@HAVE_GTEST_TRUE@TESTS = $(SHTESTS) + +# Don't install shell tests. +noinst_SCRIPTS = $(SHTESTS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/bin/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..2bf269a --- /dev/null +++ b/src/bin/netconf/tests/test_libraries.h.in @@ -0,0 +1,24 @@ +// 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 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..e2f5d31 --- /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 DHCPv4 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..e89e522 --- /dev/null +++ b/src/bin/netconf/unix_control_socket.cc @@ -0,0 +1,97 @@ +// 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 unix_control_socket.cc +/// Contains the UNIX socket derived class for control socket communication. + +#include <config.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> +#include <netconf/unix_control_socket.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(ElementPtr config, + const string& /*service*/) { + return (sendCommand(createCommand("config-test", config))); +} + +ConstElementPtr +UnixControlSocket::configSet(ElementPtr 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 (exception const& 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..1c8ccb6 --- /dev/null +++ b/src/bin/netconf/unix_control_socket.h @@ -0,0 +1,88 @@ +// 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 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. + data::ConstElementPtr configGet(const std::string& service) override final; + + /// @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. + data::ConstElementPtr configTest(data::ElementPtr config, + const std::string& service) override final; + + /// @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. + data::ConstElementPtr configSet(data::ElementPtr config, + const std::string& service) override final; + +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); +}; // UnixControlSocket + +/// @brief Type definition for the pointer to the @c UnixControlSocket. +using UnixControlSocketPtr = std::shared_ptr<UnixControlSocket>; + +/// @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 |