Browse Source

[master] Merged (rebased) trac3770 (command line test config)

Francis Dupont 8 years ago
parent
commit
30023d9a74

+ 12 - 0
doc/guide/dhcp4-srv.xml

@@ -44,6 +44,18 @@
           </listitem>
           <listitem>
             <simpara>
+            <command>-t <replaceable>file</replaceable></command> -
+            specifies the configuration file to be tested. Kea-dhcp4
+            will attempt to load it, and will conduct sanity
+            checks. Note that certain checks are possible only while
+            running the actual server. The actual status is reported
+            with exit code (0 = configuration looks ok, 1 = error
+            encountered). Kea will print out log messages to standard
+            output and error to standard error when testing
+            configuration.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>
               <command>-v</command> - prints out the Kea version and exits.
             </simpara>
           </listitem>

+ 12 - 0
doc/guide/dhcp6-srv.xml

@@ -44,6 +44,18 @@
           </listitem>
           <listitem>
             <simpara>
+            <command>-t <replaceable>file</replaceable></command> -
+            specifies the configuration file to be tested. Kea-dhcp6
+            will attempt to load it, and will conduct sanity
+            checks. Note that certain checks are possible only while
+            running the actual server. The actual status is reported
+            with exit code (0 = configuration looks ok, 1 = error
+            encountered). Kea will print out log messages to standard
+            output and error to standard error when testing
+            configuration.</simpara>
+          </listitem>
+          <listitem>
+            <simpara>
               <command>-v</command> - prints out the Kea version and exits.
             </simpara>
           </listitem>

+ 12 - 1
src/bin/d2/kea-dhcp-ddns.xml

@@ -52,7 +52,8 @@
       <arg><option>-V</option></arg>
       <arg><option>-W</option></arg>
       <arg><option>-d</option></arg>
-      <arg><option>-s</option></arg>
+      <!-- not yet <arg><option>-t</option></arg> -->
+      <arg><option>-c</option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -104,6 +105,16 @@
         </para></listitem>
       </varlistentry>
 
+<!-- not yet
+      <varlistentry>
+        <term><option>-t</option></term>
+        <listitem><para>
+          Check the syntax of the configuration file and report the first
+          error if any.
+        </para></listitem>
+      </varlistentry>
+-->
+
       <varlistentry>
         <term><option>-c</option></term>
         <listitem><para>

+ 15 - 4
src/bin/dhcp4/json_config_parser.cc

@@ -406,7 +406,8 @@ void configureCommandChannel() {
 }
 
 isc::data::ConstElementPtr
-configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
+configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set,
+                     bool check_only) {
     if (!config_set) {
         ConstElementPtr answer = isc::config::createAnswer(1,
                                  string("Can't parse NULL config"));
@@ -583,9 +584,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
                       << " (" << config_pair.second->getPosition() << ")");
         }
 
-        // Setup the command channel.
-        configureCommandChannel();
-
         // Apply global options in the staging config.
         Dhcp4ConfigParser global_parser;
         global_parser.parse(srv_cfg, mutable_cfg);
@@ -608,12 +606,25 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
         rollback = true;
     }
 
+    if (check_only) {
+        rollback = true;
+        if (!answer) {
+            answer = isc::config::createAnswer(0,
+            "Configuration seems sane. Control-socket, hook-libraries, and D2 "
+            "configuration were sanity checked, but not applied.");
+        }
+    }
+
     // So far so good, there was no parsing error so let's commit the
     // configuration. This will add created subnets and option values into
     // the server's configuration.
     // This operation should be exception safe but let's make sure.
     if (!rollback) {
         try {
+
+            // Setup the command channel.
+            configureCommandChannel();
+
             // No need to commit interface names as this is handled by the
             // CfgMgr::commit() function.
 

+ 7 - 1
src/bin/dhcp4/json_config_parser.h

@@ -40,6 +40,10 @@ class Dhcpv4Srv;
 /// extra parameter is a reference to DHCPv4 server component. It is currently
 /// not used and CfgMgr::instance() is accessed instead.
 ///
+/// Test-only mode added. If check_only flag is set to true, the configuration
+/// is parsed, but the actual change is not applied. The goal is to have
+/// the ability to test configuration.
+///
 /// This method does not throw. It catches all exceptions and returns them as
 /// reconfiguration statuses. It may return the following response codes:
 /// 0 - configuration successful
@@ -48,10 +52,12 @@ class Dhcpv4Srv;
 /// values in to server's configuration)
 ///
 /// @param config_set a new configuration (JSON) for DHCPv4 server
+/// @param check_only whether this configuration is for testing only
 /// @return answer that contains result of reconfiguration
 isc::data::ConstElementPtr
 configureDhcp4Server(Dhcpv4Srv&,
-                     isc::data::ConstElementPtr config_set);
+                     isc::data::ConstElementPtr config_set,
+                     bool check_only = false);
 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 13 - 2
src/bin/dhcp4/kea-dhcp4.xml

@@ -52,8 +52,9 @@
       <arg><option>-V</option></arg>
       <arg><option>-W</option></arg>
       <arg><option>-d</option></arg>
-      <arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
-      <arg><option>-p<replaceable class="parameter">port-number</replaceable></option></arg>
+      <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+      <arg><option>-t <replaceable class="parameter">config-file</replaceable></option></arg>
+      <arg><option>-p <replaceable class="parameter">port-number</replaceable></option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -110,6 +111,16 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>-t</option></term>
+        <listitem><para> Check the configuration file
+          and report the first error if any. Note
+          that not all parameters are completely checked, in
+          particular, service and control channel sockets
+          are not opened, and hook libraries are not loaded.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-p</option></term>
         <listitem><para>
           Port number (1-65535) on which the server listens. This is useful

+ 61 - 3
src/bin/dhcp4/main.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,9 @@
 
 #include <dhcp4/ctrl_dhcp4_srv.h>
 #include <dhcp4/dhcp4_log.h>
+#include <dhcp4/parser_context.h>
+#include <dhcp4/json_config_parser.h>
+#include <cc/command_interpreter.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <log/logger_support.h>
 #include <log/logger_manager.h>
@@ -17,6 +20,7 @@
 
 #include <iostream>
 
+using namespace isc::data;
 using namespace isc::dhcp;
 using namespace std;
 
@@ -38,12 +42,13 @@ usage() {
     cerr << "Kea DHCPv4 server, version " << VERSION << endl;
     cerr << endl;
     cerr << "Usage: " << DHCP4_NAME
-         << " -[v|V|W] [-d] [-c cfgfile] [-p number]" << endl;
+         << " -[v|V|W] [-d] [-{c|t} cfgfile] [-p number]" << endl;
     cerr << "  -v: print version number and exit" << endl;
     cerr << "  -V: print extended version and exit" << endl;
     cerr << "  -W: display the configuration report and exit" << endl;
     cerr << "  -d: debug mode with extra verbosity (former -v)" << endl;
     cerr << "  -c file: specify configuration file" << endl;
+    cerr << "  -t file: check the configuration file syntax and exit" << endl;
     cerr << "  -p number: specify non-standard port number 1-65535 "
          << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
@@ -56,11 +61,12 @@ main(int argc, char* argv[]) {
     int port_number = DHCP4_SERVER_PORT; // The default. any other values are
                                          // useful for testing only.
     bool verbose_mode = false; // Should server be verbose?
+    bool check_mode = false;   // Check syntax
 
     // The standard config file
     std::string config_file("");
 
-    while ((ch = getopt(argc, argv, "dvVWc:p:")) != -1) {
+    while ((ch = getopt(argc, argv, "dvVWc:p:t:")) != -1) {
         switch (ch) {
         case 'd':
             verbose_mode = true;
@@ -78,6 +84,10 @@ main(int argc, char* argv[]) {
             cout << isc::detail::getConfigReport() << endl;
             return (EXIT_SUCCESS);
 
+        case 't':
+            check_mode = true;
+            // falls through
+
         case 'c': // config file
             config_file = optarg;
             break;
@@ -114,6 +124,54 @@ main(int argc, char* argv[]) {
         usage();
     }
 
+    if (check_mode) {
+        try {
+
+            // We need to initialize logging, in case any error messages are to be printed.
+            // This is just a test, so we don't care about lockfile.
+            setenv("KEA_LOCKFILE_DIR", "none", 0);
+            CfgMgr::instance().setDefaultLoggerName(DHCP4_ROOT_LOGGER_NAME);
+            Daemon::loggerInit(DHCP4_ROOT_LOGGER_NAME, verbose_mode);
+
+            // Check the syntax first.
+            Parser4Context parser;
+            ConstElementPtr json;
+            json = parser.parseFile(config_file, Parser4Context::PARSER_DHCP4);
+            if (!json) {
+                cerr << "No configuration found" << endl;
+                return (EXIT_FAILURE);
+            }
+            if (verbose_mode) {
+                cerr << "Syntax check OK" << endl;
+            }
+
+            // Check the logic next.
+            ConstElementPtr dhcp4 = json->get("Dhcp4");
+            if (!dhcp4) {
+                cerr << "Missing mandatory Dhcp4 element" << endl;
+                return (EXIT_FAILURE);
+            }
+            ControlledDhcpv4Srv server(0);
+            ConstElementPtr answer;
+
+            // Now we pass the Dhcp4 configuration to the server, but
+            // tell it to check the configuration only (check_only = true)
+            answer = configureDhcp4Server(server, dhcp4, true);
+
+            int status_code = 0;
+            answer = isc::config::parseAnswer(status_code, answer);
+            if (status_code == 0) {
+                return (EXIT_SUCCESS);
+            } else {
+                cerr << "Error encountered: " << answer->stringValue() << endl;
+                return (EXIT_FAILURE);
+            }
+        } catch (const std::exception& ex) {
+            cerr << "Syntax check failed with: " << ex.what() << endl;
+        }
+        return (EXIT_FAILURE);
+    }
+
     int ret = EXIT_SUCCESS;
     try {
         // It is important that we set a default logger name because this name

+ 109 - 1
src/bin/dhcp4/tests/dhcp4_process_tests.sh.in

@@ -1,4 +1,4 @@
-# Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -57,6 +57,84 @@ CONFIG="{
         ]
     }
 }"
+# Invalid configuration (syntax error) to check that Kea can check syntax.
+# This config has following errors:
+# - it should be interfaces-config/interfaces, not interfaces
+# - it should be subnet4/pools, no subnet4/pool
+CONFIG_BAD_SYNTAX="{
+    \"Dhcp4\":
+    {
+        \"interfaces\": [ ],
+        \"valid-lifetime\": 4000,
+        \"renew-timer\": 1000,
+        \"rebind-timer\": 2000,
+        \"lease-database\":
+        {
+            \"type\": \"memfile\",
+            \"persist\": false
+        },
+        \"subnet4\": [
+        {
+            \"subnet\": \"10.0.0.0/8\",
+            \"pool\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
+        } ]
+    },
+
+    \"Logging\":
+    {
+        \"loggers\": [
+        {
+            \"name\": \"kea-dhcp4\",
+            \"output_options\": [
+                {
+                    \"output\": \"$LOG_FILE\"
+                }
+            ],
+            \"severity\": \"INFO\"
+        }
+        ]
+    }
+}"
+
+# This config has bad pool values. The pool it out of scope for the subnet
+# it is defined in. Syntactically the config is correct, though.
+CONFIG_BAD_VALUES="{
+    \"Dhcp4\":
+    {
+        \"interfaces-config\": {
+            \"interfaces\": [ ]
+        },
+        \"valid-lifetime\": 4000,
+        \"renew-timer\": 1000,
+        \"rebind-timer\": 2000,
+        \"lease-database\":
+        {
+            \"type\": \"memfile\",
+            \"persist\": false
+        },
+        \"subnet4\": [
+        {
+            \"subnet\": \"10.0.0.0/8\",
+            \"pools\": [ { \"pool\": \"192.168.0.10-192.168.0.100\" } ]
+        } ]
+    },
+
+    \"Logging\":
+    {
+        \"loggers\": [
+        {
+            \"name\": \"kea-dhcp4\",
+            \"output_options\": [
+                {
+                    \"output\": \"$LOG_FILE\"
+                }
+            ],
+            \"severity\": \"INFO\"
+        }
+        ]
+    }
+}"
+
 # Invalid configuration (negative valid-lifetime) to check that Kea
 # gracefully handles reconfiguration errors.
 CONFIG_INVALID="{
@@ -103,6 +181,33 @@ bin_path=@abs_top_builddir@/src/bin/dhcp4
 # Import common test library.
 . @abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh
 
+# This test verifies that syntax checking works properly. This function
+# requires 3 parameters:
+# testname
+# config - string with a content of the config (will be written to a file)
+# exp_code - expected exit code returned by kea (0 - success, 1 - failure)
+syntax_check_test() {
+    local TESTNAME="${1}"
+    local CONFIG="${2}"
+    local EXP_CODE="${3}"
+
+    # Log the start of the test and print test name.
+    test_start $TESTNAME
+    # Remove dangling Kea instances and remove log files.
+    cleanup
+    # Create correct configuration file.
+    create_config "${CONFIG}"
+    # Check it
+    printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\""
+    ${bin_path}/${bin} -t ${CFG_FILE}
+    exit_code=$?
+    if [ ${exit_code} -ne $EXP_CODE ]; then
+        printf "ERROR: expected exit code ${EXP_CODE}, got ${exit_code}\n"
+        clean_exit 1
+    fi
+    test_finish 0
+}
+
 # This test verifies that DHCPv4 can be reconfigured with a SIGHUP signal.
 dynamic_reconfiguration_test() {
     # Log the start of the test and print test name.
@@ -380,3 +485,6 @@ shutdown_test "dhcpv4.sigint_test" 2
 version_test "dhcpv4.version"
 logger_vars_test "dhcpv4.variables"
 lfc_timer_test
+syntax_check_test "dhcpv4.syntax_check_success" "${CONFIG}" 0
+syntax_check_test "dhcpv4.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1
+syntax_check_test "dhcpv4.syntax_check_bad_values" "${CONFIG_BAD_VALUES}" 1

+ 15 - 4
src/bin/dhcp6/json_config_parser.cc

@@ -612,7 +612,9 @@ void configureCommandChannel() {
 }
 
 isc::data::ConstElementPtr
-configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
+configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set,
+                     bool check_only) {
+
     if (!config_set) {
         ConstElementPtr answer = isc::config::createAnswer(1,
                                  string("Can't parse NULL config"));
@@ -807,9 +809,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
                       << " (" << config_pair.second->getPosition() << ")");
         }
 
-        // Setup the command channel.
-        configureCommandChannel();
-
         // Apply global options in the staging config.
         Dhcp6ConfigParser global_parser;
         global_parser.parse(srv_config, mutable_cfg);
@@ -830,6 +829,15 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
         rollback = true;
     }
 
+    if (check_only) {
+        rollback = true;
+        if (!answer) {
+            answer = isc::config::createAnswer(0,
+            "Configuration seems sane. Control-socket, hook-libraries, and D2 "
+            "configuration were sanity checked, but not applied.");
+        }
+    }
+
     // So far so good, there was no parsing error so let's commit the
     // configuration. This will add created subnets and option values into
     // the server's configuration.
@@ -837,6 +845,9 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
     if (!rollback) {
         try {
 
+            // Setup the command channel.
+            configureCommandChannel();
+            
             // No need to commit interface names as this is handled by the
             // CfgMgr::commit() function.
 

+ 6 - 1
src/bin/dhcp6/json_config_parser.h

@@ -24,6 +24,10 @@ class Dhcpv6Srv;
 /// extra parameter is a reference to DHCPv6 server component. It is currently
 /// not used and CfgMgr::instance() is accessed instead.
 ///
+/// Test-only mode is supported. If check_only flag is set to true, the
+/// configuration is parsed, but the actual change is not applied. The goal is
+/// to have the ability to test configuration.
+///
 /// This method does not throw. It catches all exceptions and returns them as
 /// reconfiguration statuses. It may return the following response codes:
 /// 0 - configuration successful
@@ -36,7 +40,8 @@ class Dhcpv6Srv;
 /// @return answer that contains result of the reconfiguration.
 /// @throw Dhcp6ConfigError if trying to create a parser for NULL config.
 isc::data::ConstElementPtr
-configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set);
+configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
+                     bool check_only = false);
 
 }; // end of isc::dhcp namespace
 }; // end of isc namespace

+ 13 - 2
src/bin/dhcp6/kea-dhcp6.xml

@@ -52,8 +52,9 @@
       <arg><option>-V</option></arg>
       <arg><option>-W</option></arg>
       <arg><option>-d</option></arg>
-      <arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
-      <arg><option>-p<replaceable class="parameter">port-number</replaceable></option></arg>
+      <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
+      <arg><option>-t <replaceable class="parameter">config-file</replaceable></option></arg>
+      <arg><option>-p <replaceable class="parameter">port-number</replaceable></option></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
 
@@ -111,6 +112,16 @@
       </varlistentry>
 
       <varlistentry>
+        <term><option>-t</option></term>
+        <listitem><para> Check the configuration file
+          and report the first error if any. Note
+          that not all parameters are completely checked, in
+          particular, service and control channel sockets
+          are not opened, and hook libraries are not loaded.
+        </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>-p</option></term>
         <listitem><para>
           Port number (1-65535) on which the server listens. This is useful

+ 61 - 3
src/bin/dhcp6/main.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2017 Internet Systems Consortium, Inc. ("ISC")
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -8,6 +8,8 @@
 
 #include <dhcp6/ctrl_dhcp6_srv.h>
 #include <dhcp6/dhcp6_log.h>
+#include <dhcp6/parser_context.h>
+#include <dhcp6/json_config_parser.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <log/logger_support.h>
 #include <log/logger_manager.h>
@@ -18,6 +20,7 @@
 
 #include <iostream>
 
+using namespace isc::data;
 using namespace isc::dhcp;
 using namespace std;
 
@@ -43,12 +46,13 @@ usage() {
     cerr << "Kea DHCPv6 server, version " << VERSION << endl;
     cerr << endl;
     cerr << "Usage: " << DHCP6_NAME
-         << " -[v|V|W] [-d] [-c cfgfile] [-p port_number]" << endl;
+         << " -[v|V|W] [-d] [-{c|t} cfgfile] [-p port_number]" << endl;
     cerr << "  -v: print version number and exit." << endl;
     cerr << "  -V: print extended version and exit" << endl;
     cerr << "  -W: display the configuration report and exit" << endl;
     cerr << "  -d: debug mode with extra verbosity (former -v)" << endl;
     cerr << "  -c file: specify configuration file" << endl;
+    cerr << "  -t file: check the configuration file syntax and exit" << endl;
     cerr << "  -p number: specify non-standard port number 1-65535 "
          << "(useful for testing only)" << endl;
     exit(EXIT_FAILURE);
@@ -61,11 +65,12 @@ main(int argc, char* argv[]) {
     int port_number = DHCP6_SERVER_PORT; // The default. Any other values are
                                          // useful for testing only.
     bool verbose_mode = false; // Should server be verbose?
+    bool check_mode = false;   // Check syntax
 
     // The standard config file
     std::string config_file("");
 
-    while ((ch = getopt(argc, argv, "dvVWc:p:")) != -1) {
+    while ((ch = getopt(argc, argv, "dvVWc:p:t:")) != -1) {
         switch (ch) {
         case 'd':
             verbose_mode = true;
@@ -83,6 +88,10 @@ main(int argc, char* argv[]) {
             cout << isc::detail::getConfigReport() << endl;
             return (EXIT_SUCCESS);
 
+        case 't':
+            check_mode = true;
+            // falls through
+
         case 'c': // config file
             config_file = optarg;
             break;
@@ -118,6 +127,55 @@ main(int argc, char* argv[]) {
         usage();
     }
 
+    if (check_mode) {
+        try {
+            // We need to initialize logging, in case any error messages are to be printed.
+            // This is just a test, so we don't care about lockfile.
+            setenv("KEA_LOCKFILE_DIR", "none", 0);
+            CfgMgr::instance().setDefaultLoggerName(DHCP6_ROOT_LOGGER_NAME);
+            Daemon::loggerInit(DHCP6_ROOT_LOGGER_NAME, verbose_mode);
+
+            // Check the syntax first.
+            Parser6Context parser;
+            ConstElementPtr json;
+            json = parser.parseFile(config_file, Parser6Context::PARSER_DHCP6);
+            if (!json) {
+                cerr << "No configuration found" << endl;
+                return (EXIT_FAILURE);
+            }
+            if (verbose_mode) {
+                cerr << "Syntax check OK" << endl;
+            }
+
+            // Check the logic next.
+            ConstElementPtr dhcp6 = json->get("Dhcp6");
+            if (!dhcp6) {
+                cerr << "Missing mandatory Dhcp6 element" << endl;
+                return (EXIT_FAILURE);
+            }
+            ControlledDhcpv6Srv server(0);
+            ConstElementPtr answer;
+
+            // Now we pass the Dhcp6 configuration to the server, but
+            // tell it to check the configuration only (check_only = true)
+            answer = configureDhcp6Server(server, dhcp6, true);
+
+            int status_code = 0;
+            answer = isc::config::parseAnswer(status_code, answer);
+            if (status_code == 0) {
+                return (EXIT_SUCCESS);
+            } else {
+                cerr << "Error encountered: " << answer->stringValue() << endl;
+                return (EXIT_FAILURE);
+            }
+
+
+            return (EXIT_SUCCESS);
+        } catch (const std::exception& ex) {
+            cerr << "Syntax check failed with " << ex.what() << endl;
+        }
+        return (EXIT_FAILURE);
+    }
 
     int ret = EXIT_SUCCESS;
     try {

+ 108 - 2
src/bin/dhcp6/tests/dhcp6_process_tests.sh.in

@@ -59,12 +59,53 @@ CONFIG="{
         ]
     }
 }"
+# Invalid configuration (syntax error) to check that Kea can check syntax.
+# This config has following errors:
+# - it should be interfaces-config/interfaces, not interfaces
+# - it should be subnet6/pools, no subnet6/pool
+CONFIG_BAD_SYNTAX="{
+    \"Dhcp6\":
+    {
+        \"interfaces\": [ ],
+        \"preferred-lifetime\": 3000,
+        \"valid-lifetime\": 4000,
+        \"renew-timer\": 1000,
+        \"rebind-timer\": 2000,
+        \"lease-database\":
+        {
+            \"type\": \"memfile\",
+            \"persist\": false
+        },
+        \"subnet6\": [
+        {
+            \"subnet\": \"2001:db8:1::/64\",
+            \"pool\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
+        } ]
+    },
+
+    \"Logging\":
+    {
+        \"loggers\": [
+        {
+            \"name\": \"kea-dhcp6\",
+            \"output_options\": [
+                {
+                    \"output\": \"$LOG_FILE\"
+                }
+            ],
+            \"severity\": \"INFO\"
+        }
+        ]
+    }
+}"
 # Invalid configuration (negative preferred-lifetime) to check that Kea
 # gracefully handles reconfiguration errors.
 CONFIG_INVALID="{
     \"Dhcp6\":
     {
-        \"interfaces\": [ ],
+        \"interfaces-config\": {
+          \"interfaces\": [ ]
+        },
         \"preferred-lifetime\": -3,
         \"valid-lifetime\": 4000,
         \"renew-timer\": 1000,
@@ -97,6 +138,41 @@ CONFIG_INVALID="{
     }
 }"
 
+# This config has bad pool values. The pool it out of scope for the subnet
+# it is defined in. Syntactically the config is correct, though.
+CONFIG_BAD_VALUES="{
+    \"Dhcp6\":
+    {   \"interfaces-config\": {
+          \"interfaces\": [ ]
+        },
+        \"server-id\": {
+          \"type\": \"LLT\",
+          \"persist\": false
+        },
+        \"preferred-lifetime\": 3000,
+        \"valid-lifetime\": 4000,
+        \"renew-timer\": 1000,
+        \"rebind-timer\": 2000,
+        \"lease-database\":
+        {
+            \"type\": \"memfile\",
+            \"name\": \"$LEASE_FILE\",
+            \"persist\": false,
+            \"lfc-interval\": 0
+        },
+        \"subnet6\": [
+        {
+            \"subnet\": \"2001:db8::/64\",
+            \"pools\": [ { \"pool\": \"3000::-3000::ffff\" } ]
+        } ],
+        \"dhcp-ddns\": {
+            \"enable-updates\": true,
+            \"qualifying-suffix\": \"\"
+        }
+    }
+}"
+
+
 # Set the location of the executable.
 bin="kea-dhcp6"
 bin_path=@abs_top_builddir@/src/bin/dhcp6
@@ -104,6 +180,34 @@ bin_path=@abs_top_builddir@/src/bin/dhcp6
 # Import common test library.
 . @abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh
 
+# This test verifies that syntax checking works properly. This function
+# requires 3 parameters:
+# testname
+# config - string with a content of the config (will be written to a file)
+# exp_code - expected exit code returned by kea (0 - success, 1 - failure)
+syntax_check_test() {
+    local TESTNAME="${1}"
+    local CONFIG="${2}"
+    local EXP_CODE="${3}"
+
+    # Log the start of the test and print test name.
+    test_start $TESTNAME
+    # Remove dangling Kea instances and remove log files.
+    cleanup
+    # Create correct configuration file.
+    create_config "${CONFIG}"
+    # Check it
+    printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\""
+    ${bin_path}/${bin} -t ${CFG_FILE}
+    exit_code=$?
+    if [ ${exit_code} -ne $EXP_CODE ]; then
+        printf "ERROR: expected exit code $EXP_CODE, got ${exit_code}\n"
+        clean_exit 1
+    fi
+
+    test_finish 0
+}
+
 # This test verifies that DHCPv6 can be reconfigured with a SIGHUP signal.
 dynamic_reconfiguration_test() {
     # Log the start of the test and print test name.
@@ -383,4 +487,6 @@ shutdown_test "dhcpv6.sigint_test" 2
 version_test "dhcpv6.version"
 logger_vars_test "dhcpv6.variables"
 lfc_timer_test
-
+syntax_check_test "dhcpv6.syntax_check_success" "${CONFIG}" 0
+syntax_check_test "dhcpv6.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1
+syntax_check_test "dhcpv6.syntax_check_bad_values" "${CONFIG_BAD_VALUES}" 1