Browse Source

[3770_rebase] Implemented logic checks for DHCPv6

Tomek Mrugalski 8 years ago
parent
commit
c732ac1338

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

@@ -153,6 +153,9 @@ main(int argc, char* argv[]) {
             }
             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;

+ 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

+ 32 - 0
src/bin/dhcp6/main.cc

@@ -9,6 +9,7 @@
 #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>
@@ -128,6 +129,13 @@ main(int argc, char* argv[]) {
 
     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);
@@ -138,6 +146,30 @@ main(int argc, char* argv[]) {
             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;

+ 55 - 17
src/bin/dhcp6/tests/dhcp6_process_tests.sh.in

@@ -60,6 +60,9 @@ 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\":
     {
@@ -135,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
@@ -142,10 +180,18 @@ 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 check works properly.
+# 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 "dhcpv6_srv.syntax_check"
+    test_start $TESTNAME
     # Remove dangling Kea instances and remove log files.
     cleanup
     # Create correct configuration file.
@@ -154,22 +200,12 @@ syntax_check_test() {
     printf "Running command %s.\n" "\"${bin_path}/${bin} -t -c ${CFG_FILE}\""
     ${bin_path}/${bin} -t -c ${CFG_FILE}
     exit_code=$?
-    if [ ${exit_code} -ne 0 ]; then
-        printf "ERROR: expected exit code 0, got ${exit_code}\n"
+    if [ ${exit_code} -ne $EXP_CODE ]; then
+        printf "ERROR: expected exit code $EXP_CODE, got ${exit_code}\n"
         clean_exit 1
     fi
-    # Create incorrect configuration file.
-    create_config "${CONFIG_BAD_SYNTAX}"
-    # Check it
-    printf "Running command %s.\n" "\"${bin_path}/${bin} -t -c ${CFG_FILE}\""
-    printf "A syntax error should be detected\n"
-    ${bin_path}/${bin} -t -c ${CFG_FILE}
-    if [ $? -eq 0 ]; then
-        printf "ERROR: expected exit code not 0, got 0\n"
-        clean_exit 1
-    fi
-    # All ok
-    clean_exit 0
+
+    test_finish 0
 }
 
 # This test verifies that DHCPv6 can be reconfigured with a SIGHUP signal.
@@ -451,4 +487,6 @@ shutdown_test "dhcpv6.sigint_test" 2
 version_test "dhcpv6.version"
 logger_vars_test "dhcpv6.variables"
 lfc_timer_test
-syntax_check_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