]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2722] config set follows the same path as config test and can rollback
authorRazvan Becheriu <razvan@isc.org>
Sun, 26 Feb 2023 15:23:40 +0000 (17:23 +0200)
committerRazvan Becheriu <razvan@isc.org>
Thu, 16 Mar 2023 19:26:11 +0000 (21:26 +0200)
16 files changed:
src/bin/agent/ca_process.cc
src/bin/d2/d2_process.cc
src/bin/d2/tests/d2_command_unittest.cc
src/bin/dhcp4/ctrl_dhcp4_srv.cc
src/bin/dhcp4/json_config_parser.cc
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp6/ctrl_dhcp6_srv.cc
src/bin/dhcp6/json_config_parser.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/bin/netconf/netconf_process.cc
src/lib/cc/command_interpreter.cc
src/lib/cc/tests/command_interpreter_unittests.cc
src/lib/config/hooked_command_mgr.cc
src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc
src/lib/process/d_cfg_mgr.cc
src/lib/process/testutils/d_test_stubs.cc

index 6e6825eb79a24dd2b941bf194565a133dee4a0e9..03287d4445cb687e200ecd6a42e4f610236cf2a6 100644 (file)
@@ -98,7 +98,8 @@ CtrlAgentProcess::runIO() {
 isc::data::ConstElementPtr
 CtrlAgentProcess::shutdown(isc::data::ConstElementPtr /*args*/) {
     setShutdownFlag(true);
-    return (isc::config::createAnswer(0, "Control Agent is shutting down"));
+    return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
+                                      "Control Agent is shutting down"));
 }
 
 isc::data::ConstElementPtr
index c06e0bea9a1008064c0a14c3db516b38b2c863ca..dc7dff89157fb128e24bfd758f1823535601c24e 100644 (file)
@@ -17,6 +17,7 @@
 #include <hooks/hooks.h>
 #include <hooks/hooks_manager.h>
 
+using namespace isc::config;
 using namespace isc::hooks;
 using namespace isc::process;
 
@@ -221,16 +222,18 @@ D2Process::shutdown(isc::data::ConstElementPtr args) {
                 shutdown_type_ = SD_NOW;
             } else {
                 setShutdownFlag(false);
-                return (isc::config::createAnswer(1, "Invalid Shutdown type: "
-                                                  + type_str));
+                return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+                                                  "Invalid Shutdown type: " +
+                                                  type_str));
             }
         }
     }
 
     // Set the base class's shutdown flag.
     setShutdownFlag(true);
-    return (isc::config::createAnswer(0, "Shutdown initiated, type is: "
-                                      + type_str));
+    return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
+                                      "Shutdown initiated, type is: " +\
+                                      type_str));
 }
 
 isc::data::ConstElementPtr
@@ -295,7 +298,7 @@ D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) {
             LOG_ERROR(d2_logger, DHCP_DDNS_CONFIGURED_CALLOUT_DROP)
                 .arg(error);
             reconf_queue_flag_ = false;
-            answer = isc::config::createAnswer(1, error);
+            answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, error);
             return (answer);
         }
     }
index 8f956e79abfeecc641d82425afc9fea275322ecc..cf4fb2aa4900ce821f26d2380779f117f8f911e7 100644 (file)
@@ -370,7 +370,7 @@ public:
         // both structures are built using the same order.
         EXPECT_EQ(Element::fromJSON(expected_command)->str(),
                  entire_command->str());
-        return (createAnswer(0, "long command received ok"));
+        return (createAnswer(CONTROL_RESULT_SUCCESS, "long command received ok"));
     }
 
     /// @brief Command handler which generates long response.
@@ -386,7 +386,7 @@ public:
             s << std::setw(5) << i;
             arguments->add(Element::create(s.str()));
         }
-        return (createAnswer(0, arguments));
+        return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
     }
 };
 
index 6e51e81c567817c4f7b387948ccb78c84007d3df..e5f626188718f7eab2dbe6c5f11b87389c292c08 100644 (file)
@@ -246,12 +246,12 @@ ControlledDhcpv4Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
         }
     } catch (const std::exception& ex) {
         LOG_ERROR(dhcp4_logger, DHCP4_HOOKS_LIBS_RELOAD_FAIL);
-        ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
         return (answer);
     }
-    ConstElementPtr answer = isc::config::createAnswer(0,
-                             "Hooks libraries successfully reloaded"
-                             " (WARNING: libreload is deprecated).");
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
+                             "Hooks libraries successfully reloaded "
+                             "(WARNING: libreload is deprecated).");
     return (answer);
 }
 
@@ -281,7 +281,7 @@ ControlledDhcpv4Srv::commandConfigGetHandler(const string&,
                                              ConstElementPtr /*args*/) {
     ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
 
-    return (createAnswer(0, config));
+    return (createAnswer(CONTROL_RESULT_SUCCESS, config));
 }
 
 ConstElementPtr
@@ -391,25 +391,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
     // configuration attempts.
     CfgMgr::instance().rollback();
 
-    // Let's first check the config
-    ConstElementPtr result = checkConfig(dhcp4);
-
-    int rcode = 0;
-    isc::config::parseAnswer(rcode, result);
-    if (rcode != CONTROL_RESULT_SUCCESS) {
-        return (result);
-    }
-
-    // disable multi-threading (it will be applied by new configuration)
-    // this must be done in order to properly handle MT to ST transition
-    // when 'multi-threading' structure is missing from new config
-    MultiThreadingMgr::instance().apply(false, 0, 0);
-
-    // We are starting the configuration process so we should remove any
-    // staging configuration that has been created during previous
-    // configuration attempts.
-    CfgMgr::instance().rollback();
-
     // Parse the logger configuration explicitly into the staging config.
     // Note this does not alter the current loggers, they remain in
     // effect until we apply the logging config below.  If no logging
@@ -421,11 +402,12 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
     CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
 
     // Now we configure the server proper.
-    result = processConfig(dhcp4);
+    ConstElementPtr result = processConfig(dhcp4);
 
     // If the configuration parsed successfully, apply the new logger
     // configuration and the commit the new configuration.  We apply
     // the logging first in case there's a configuration failure.
+    int rcode = 0;
     isc::config::parseAnswer(rcode, result);
     if (rcode == CONTROL_RESULT_SUCCESS) {
         CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
@@ -645,7 +627,7 @@ ControlledDhcpv4Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
     ElementPtr extended = Element::create(Dhcpv4Srv::getVersion(true));
     ElementPtr arguments = Element::createMap();
     arguments->set("extended", extended);
-    ConstElementPtr answer = isc::config::createAnswer(0,
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
                                 Dhcpv4Srv::getVersion(false),
                                 arguments);
     return (answer);
@@ -655,7 +637,7 @@ ConstElementPtr
 ControlledDhcpv4Srv::commandBuildReportHandler(const string&,
                                                ConstElementPtr) {
     ConstElementPtr answer =
-        isc::config::createAnswer(0, isc::detail::getConfigReport());
+        isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport());
     return (answer);
 }
 
@@ -791,7 +773,7 @@ ControlledDhcpv4Srv::commandStatusGetHandler(const string&,
     }
     status->set("sockets", sockets);
 
-    return (createAnswer(0, status));
+    return (createAnswer(CONTROL_RESULT_SUCCESS, status));
 }
 
 ConstElementPtr
@@ -830,7 +812,7 @@ ControlledDhcpv4Srv::processCommand(const string& command,
     ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
 
     if (!srv) {
-        ConstElementPtr no_srv = isc::config::createAnswer(1,
+        ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR,
             "Server object not initialized, so can't process command '" +
             command + "', arguments: '" + txt + "'.");
         return (no_srv);
@@ -883,11 +865,11 @@ ControlledDhcpv4Srv::processCommand(const string& command,
             return (srv->commandStatusGetHandler(command, args));
         }
 
-        return (isc::config::createAnswer(1, "Unrecognized command:"
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command:"
                                           + command));
 
     } catch (const isc::Exception& ex) {
-        return (isc::config::createAnswer(1, "Error while processing command '"
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '"
                                           + command + "': " + ex.what() +
                                           ", params: '" + txt + "'"));
     }
@@ -902,7 +884,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
 
     if (!srv) {
         err << "Server object not initialized, can't process config.";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
@@ -920,7 +902,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         }
     } catch (const std::exception& ex) {
         err << "Failed to process configuration:" << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Re-open lease and host database with new parameters.
@@ -945,7 +927,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         srv->getNetworkState()->reset(NetworkState::Origin::DB_CONNECTION);
     } catch (const std::exception& ex) {
         err << "Unable to open database: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Server will start DDNS communications if its enabled.
@@ -954,7 +936,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         err << "Error starting DHCP_DDNS client after server reconfiguration: "
             << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Setup DHCPv4-over-DHCPv6 IPC
@@ -963,7 +945,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         err << "error starting DHCPv4-over-DHCPv6 IPC "
                " after server reconfiguration: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Configure DHCP packet queueing
@@ -978,7 +960,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         err << "Error setting packet queue controls after server reconfiguration: "
             << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Configure a callback to shut down the server when the bind socket
@@ -1008,7 +990,7 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
         err << "unable to setup timers for periodically running the"
             " reclamation of the expired leases: "
             << ex.what() << ".";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Setup config backend polling, if configured for it.
@@ -1094,12 +1076,10 @@ ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) {
 
     ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
 
-    // Single stream instance used in all error clauses
-    std::ostringstream err;
-
     if (!srv) {
-        err << "Server object not initialized, can't process config.";
-        return (isc::config::createAnswer(1, err.str()));
+        ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR,
+            "Server object not initialized, can't process config.");
+        return (no_srv);
     }
 
     return (configureDhcp4Server(*srv, config, true));
index c2244d5da191eecf075900be27d8db72ddca950b..a602c7c98b16b5f90574584b2d9dd4ff058e827a 100644 (file)
@@ -321,29 +321,11 @@ void configureCommandChannel() {
 }
 
 isc::data::ConstElementPtr
-configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
-                     bool check_only) {
-    if (!config_set) {
-        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
-                                 string("Can't parse NULL config"));
-        return (answer);
-    }
-
-    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_START)
-        .arg(server.redactConfig(config_set)->str());
-
+processDhcp4Config(isc::data::ConstElementPtr config_set) {
     // Before starting any subnet operations, let's reset the subnet-id counter,
     // so newly recreated configuration starts with first subnet-id equal 1.
     Subnet::resetSubnetID();
 
-    // Close DHCP sockets and remove any existing timers.
-    if (!check_only) {
-        IfaceMgr::instance().closeSockets();
-        TimerMgr::instance()->unregisterTimers();
-        server.discardPackets();
-        server.getCBControl()->reset();
-    }
-
     // Revert any runtime option definitions configured so far and not committed.
     LibDHCP::revertRuntimeOptionDefs();
     // Let's set empty container in case a user hasn't specified any configuration
@@ -355,9 +337,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
 
     // Answer will hold the result.
     ConstElementPtr answer;
-    // Rollback informs whether error occurred and original data
-    // have to be restored to global storages.
-    bool rollback = false;
+
     // Global parameter name in case of an error.
     string parameter_name;
     ElementPtr mutable_cfg;
@@ -459,7 +439,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
         if (ifaces_config) {
             parameter_name = "interfaces-config";
-            IfacesConfigParser parser(AF_INET, check_only);
+            IfacesConfigParser parser(AF_INET, true);
             CfgIfacePtr cfg_iface = srv_config->getCfgIface();
             parser.parse(cfg_iface, ifaces_config);
         }
@@ -740,25 +720,95 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
         LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
                   .arg(parameter_name).arg(ex.what());
         answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
-
-        // An error occurred, so make sure that we restore original data.
-        rollback = true;
     } catch (...) {
         // For things like bad_cast in boost::lexical_cast
         LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
-        answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
-                                           " processing error");
+        answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
+                                           "processing error");
+    }
 
-        // An error occurred, so make sure that we restore original data.
-        rollback = true;
+    if (!answer) {
+        answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
+                                           "Control-socket, hook-libraries, and D2 configuration "
+                                           "were sanity checked, but not applied.");
     }
 
-    if (check_only) {
+    return (answer);
+}
+
+isc::data::ConstElementPtr
+configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
+                     bool check_only) {
+    if (!config_set) {
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
+                                                           "Can't parse NULL config");
+        return (answer);
+    }
+
+    LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_START)
+        .arg(server.redactConfig(config_set)->str());
+
+    // Rollback informs whether error occurred and original data
+    // have to be restored to global storages.
+    bool rollback = false;
+
+    auto answer = processDhcp4Config(config_set);
+
+    int status_code = 0;
+    isc::config::parseAnswer(status_code, answer);
+    if (status_code != CONTROL_RESULT_SUCCESS) {
         rollback = true;
-        if (!answer) {
-            answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
-            "Configuration seems sane. Control-socket, hook-libraries, and D2 "
-            "configuration were sanity checked, but not applied.");
+    }
+
+    SrvConfigPtr srv_config;
+
+    if (!rollback) {
+        if (!check_only) {
+            string parameter_name;
+            ElementPtr mutable_cfg;
+
+            // Close DHCP sockets and remove any existing timers.
+            IfaceMgr::instance().closeSockets();
+            TimerMgr::instance()->unregisterTimers();
+            server.discardPackets();
+            server.getCBControl()->reset();
+
+            try {
+
+                // Get the staging configuration.
+                srv_config = CfgMgr::instance().getStagingCfg();
+
+                // This is a way to convert ConstElementPtr to ElementPtr.
+                // We need a config that can be edited, because we will insert
+                // default values and will insert derived values as well.
+                mutable_cfg = boost::const_pointer_cast<Element>(config_set);
+
+                ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
+                if (ifaces_config) {
+                    parameter_name = "interfaces-config";
+                    IfacesConfigParser parser(AF_INET, false);
+                    CfgIfacePtr cfg_iface = srv_config->getCfgIface();
+                    cfg_iface->reset();
+                    parser.parse(cfg_iface, ifaces_config);
+                }
+            } catch (const isc::Exception& ex) {
+                LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
+                          .arg(parameter_name).arg(ex.what());
+                answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
+
+                // An error occurred, so make sure that we restore original data.
+                rollback = true;
+            } catch (...) {
+                // For things like bad_cast in boost::lexical_cast
+                LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
+                answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
+                                                   " processing error");
+
+                // An error occurred, so make sure that we restore original data.
+                rollback = true;
+            }
+        } else {
+            rollback = true;
         }
     }
 
index 87ea2ea0d38da48ad9b7c3398a56c089d2d6a267..f827f0ebff665ecb7536d5c82e7bc1b42d7e6b66 100644 (file)
@@ -380,7 +380,7 @@ public:
         // both structures are built using the same order.
         EXPECT_EQ(Element::fromJSON(expected_command)->str(),
                   entire_command->str());
-        return (createAnswer(0, "long command received ok"));
+        return (createAnswer(CONTROL_RESULT_SUCCESS, "long command received ok"));
     }
 
     /// @brief Command handler which generates long response
@@ -396,7 +396,7 @@ public:
             s << std::setw(5) << i;
             arguments->add(Element::create(s.str()));
         }
-        return (createAnswer(0, arguments));
+        return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
     }
 };
 
index be4475d74621f1a0cceeadf75fdafedc9574d2b3..62c37e27549a4a1c21f1c6a4a9bb3dc73c88e24b 100644 (file)
@@ -249,10 +249,10 @@ ControlledDhcpv6Srv::commandLibReloadHandler(const string&, ConstElementPtr) {
         }
     } catch (const std::exception& ex) {
         LOG_ERROR(dhcp6_logger, DHCP6_HOOKS_LIBS_RELOAD_FAIL);
-        ConstElementPtr answer = isc::config::createAnswer(1, ex.what());
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
         return (answer);
     }
-    ConstElementPtr answer = isc::config::createAnswer(0,
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
                              "Hooks libraries successfully reloaded "
                              "(WARNING: libreload is deprecated).");
     return (answer);
@@ -284,7 +284,7 @@ ControlledDhcpv6Srv::commandConfigGetHandler(const string&,
                                              ConstElementPtr /*args*/) {
     ConstElementPtr config = CfgMgr::instance().getCurrentCfg()->toElement();
 
-    return (createAnswer(0, config));
+    return (createAnswer(CONTROL_RESULT_SUCCESS, config));
 }
 
 ConstElementPtr
@@ -394,25 +394,6 @@ ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
     // configuration attempts.
     CfgMgr::instance().rollback();
 
-    // Let's first check the config
-    ConstElementPtr result = checkConfig(dhcp6);
-
-    int rcode = 0;
-    isc::config::parseAnswer(rcode, result);
-    if (rcode != CONTROL_RESULT_SUCCESS) {
-        return (result);
-    }
-
-    // disable multi-threading (it will be applied by new configuration)
-    // this must be done in order to properly handle MT to ST transition
-    // when 'multi-threading' structure is missing from new config
-    MultiThreadingMgr::instance().apply(false, 0, 0);
-
-    // We are starting the configuration process so we should remove any
-    // staging configuration that has been created during previous
-    // configuration attempts.
-    CfgMgr::instance().rollback();
-
     // Parse the logger configuration explicitly into the staging config.
     // Note this does not alter the current loggers, they remain in
     // effect until we apply the logging config below.  If no logging
@@ -424,11 +405,12 @@ ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
     CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
 
     // Now we configure the server proper.
-    result = processConfig(dhcp6);
+    ConstElementPtr result = processConfig(dhcp6);
 
     // If the configuration parsed successfully, apply the new logger
     // configuration and the commit the new configuration.  We apply
     // the logging first in case there's a configuration failure.
+    int rcode = 0;
     isc::config::parseAnswer(rcode, result);
     if (rcode == CONTROL_RESULT_SUCCESS) {
         CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
@@ -648,7 +630,7 @@ ControlledDhcpv6Srv::commandVersionGetHandler(const string&, ConstElementPtr) {
     ElementPtr extended = Element::create(Dhcpv6Srv::getVersion(true));
     ElementPtr arguments = Element::createMap();
     arguments->set("extended", extended);
-    ConstElementPtr answer = isc::config::createAnswer(0,
+    ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
                                 Dhcpv6Srv::getVersion(false),
                                 arguments);
     return (answer);
@@ -658,7 +640,7 @@ ConstElementPtr
 ControlledDhcpv6Srv::commandBuildReportHandler(const string&,
                                                ConstElementPtr) {
     ConstElementPtr answer =
-        isc::config::createAnswer(0, isc::detail::getConfigReport());
+        isc::config::createAnswer(CONTROL_RESULT_SUCCESS, isc::detail::getConfigReport());
     return (answer);
 }
 
@@ -794,7 +776,7 @@ ControlledDhcpv6Srv::commandStatusGetHandler(const string&,
     }
     status->set("sockets", sockets);
 
-    return (createAnswer(0, status));
+    return (createAnswer(CONTROL_RESULT_SUCCESS, status));
 }
 
 ConstElementPtr
@@ -833,7 +815,7 @@ ControlledDhcpv6Srv::processCommand(const string& command,
     ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
 
     if (!srv) {
-        ConstElementPtr no_srv = isc::config::createAnswer(1,
+        ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR,
             "Server object not initialized, so can't process command '" +
             command + "', arguments: '" + txt + "'.");
         return (no_srv);
@@ -886,11 +868,11 @@ ControlledDhcpv6Srv::processCommand(const string& command,
             return (srv->commandStatusGetHandler(command, args));
         }
 
-        return (isc::config::createAnswer(1, "Unrecognized command:"
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Unrecognized command:"
                                           + command));
 
     } catch (const isc::Exception& ex) {
-        return (isc::config::createAnswer(1, "Error while processing command '"
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, "Error while processing command '"
                                           + command + "': " + ex.what() +
                                           ", params: '" + txt + "'"));
     }
@@ -906,7 +888,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
 
     if (!srv) {
         err << "Server object not initialized, can't process config.";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_RECEIVED)
@@ -924,7 +906,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         }
     } catch (const std::exception& ex) {
         err << "Failed to process configuration:" << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Re-open lease and host database with new parameters.
@@ -949,7 +931,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         srv->getNetworkState()->reset(NetworkState::Origin::DB_CONNECTION);
     } catch (const std::exception& ex) {
         err << "Unable to open database: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Regenerate server identifier if needed.
@@ -967,7 +949,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
 
     } catch (const std::exception& ex) {
         err << "unable to configure server identifier: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Server will start DDNS communications if its enabled.
@@ -976,7 +958,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         err << "Error starting DHCP_DDNS client after server reconfiguration: "
             << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Setup DHCPv4-over-DHCPv6 IPC
@@ -985,7 +967,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         err << "error starting DHCPv4-over-DHCPv6 IPC "
                " after server reconfiguration: " << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Configure DHCP packet queueing
@@ -1000,7 +982,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
     } catch (const std::exception& ex) {
         err << "Error setting packet queue controls after server reconfiguration: "
             << ex.what();
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Configure a callback to shut down the server when the bind socket
@@ -1029,7 +1011,7 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
         err << "unable to setup timers for periodically running the"
             " reclamation of the expired leases: "
             << ex.what() << ".";
-        return (isc::config::createAnswer(1, err.str()));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
     }
 
     // Setup config backend polling, if configured for it.
@@ -1116,7 +1098,7 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
     ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
 
     if (!srv) {
-        ConstElementPtr no_srv = isc::config::createAnswer(1,
+        ConstElementPtr no_srv = isc::config::createAnswer(CONTROL_RESULT_ERROR,
             "Server object not initialized, can't process config.");
         return (no_srv);
     }
index 91a3d68873a8d711f684aa3a4a15371de76fb377..92cc046da76f5f5a7b6dd83852de43fc0f880ca1 100644 (file)
@@ -424,29 +424,11 @@ void configureCommandChannel() {
 }
 
 isc::data::ConstElementPtr
-configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
-                     bool check_only) {
-    if (!config_set) {
-        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
-                                 string("Can't parse NULL config"));
-        return (answer);
-    }
-
-    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START)
-        .arg(server.redactConfig(config_set)->str());
-
+processDhcp6Config(isc::data::ConstElementPtr config_set) {
     // Before starting any subnet operations, let's reset the subnet-id counter,
     // so newly recreated configuration starts with first subnet-id equal 1.
     Subnet::resetSubnetID();
 
-    // Close DHCP sockets and remove any existing timers.
-    if (!check_only) {
-        IfaceMgr::instance().closeSockets();
-        TimerMgr::instance()->unregisterTimers();
-        server.discardPackets();
-        server.getCBControl()->reset();
-    }
-
     // Revert any runtime option definitions configured so far and not committed.
     LibDHCP::revertRuntimeOptionDefs();
     // Let's set empty container in case a user hasn't specified any configuration
@@ -458,9 +440,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
 
     // Answer will hold the result.
     ConstElementPtr answer;
-    // Rollback informs whether error occurred and original data
-    // have to be restored to global storages.
-    bool rollback = false;
+
     // Global parameter name in case of an error.
     string parameter_name;
     ElementPtr mutable_cfg;
@@ -585,7 +565,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
         ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
         if (ifaces_config) {
             parameter_name = "interfaces-config";
-            IfacesConfigParser parser(AF_INET6, check_only);
+            IfacesConfigParser parser(AF_INET6, true);
             CfgIfacePtr cfg_iface = srv_config->getCfgIface();
             parser.parse(cfg_iface, ifaces_config);
         }
@@ -869,25 +849,95 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
         LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
                   .arg(parameter_name).arg(ex.what());
         answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
-
-        // An error occurred, so make sure that we restore original data.
-        rollback = true;
     } catch (...) {
         // For things like bad_cast in boost::lexical_cast
         LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
-        answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
-                                           " processing error");
+        answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
+                                           "processing error");
+    }
 
-        // An error occurred, so make sure that we restore original data.
-        rollback = true;
+    if (!answer) {
+        answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
+                                           "Control-socket, hook-libraries, and D2 configuration "
+                                           "were sanity checked, but not applied.");
     }
 
-    if (check_only) {
+    return (answer);
+}
+
+isc::data::ConstElementPtr
+configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
+                     bool check_only) {
+    if (!config_set) {
+        ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
+                                                           "Can't parse NULL config");
+        return (answer);
+    }
+
+    LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_START)
+        .arg(server.redactConfig(config_set)->str());
+
+    // Rollback informs whether error occurred and original data
+    // have to be restored to global storages.
+    bool rollback = false;
+
+    auto answer = processDhcp6Config(config_set);
+
+    int status_code = 0;
+    isc::config::parseAnswer(status_code, answer);
+    if (status_code != CONTROL_RESULT_SUCCESS) {
         rollback = true;
-        if (!answer) {
-            answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS,
-            "Configuration seems sane. Control-socket, hook-libraries, and D2 "
-            "configuration were sanity checked, but not applied.");
+    }
+
+    SrvConfigPtr srv_config;
+
+    if (!rollback) {
+        if (!check_only) {
+            string parameter_name;
+            ElementPtr mutable_cfg;
+
+            // Close DHCP sockets and remove any existing timers.
+            IfaceMgr::instance().closeSockets();
+            TimerMgr::instance()->unregisterTimers();
+            server.discardPackets();
+            server.getCBControl()->reset();
+
+            try {
+
+                // Get the staging configuration.
+                srv_config = CfgMgr::instance().getStagingCfg();
+
+                // This is a way to convert ConstElementPtr to ElementPtr.
+                // We need a config that can be edited, because we will insert
+                // default values and will insert derived values as well.
+                mutable_cfg = boost::const_pointer_cast<Element>(config_set);
+
+                ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
+                if (ifaces_config) {
+                    parameter_name = "interfaces-config";
+                    IfacesConfigParser parser(AF_INET6, false);
+                    CfgIfacePtr cfg_iface = srv_config->getCfgIface();
+                    cfg_iface->reset();
+                    parser.parse(cfg_iface, ifaces_config);
+                }
+            } catch (const isc::Exception& ex) {
+                LOG_ERROR(dhcp6_logger, DHCP6_PARSER_FAIL)
+                          .arg(parameter_name).arg(ex.what());
+                answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
+
+                // An error occurred, so make sure that we restore original data.
+                rollback = true;
+            } catch (...) {
+                // For things like bad_cast in boost::lexical_cast
+                LOG_ERROR(dhcp6_logger, DHCP6_PARSER_EXCEPTION).arg(parameter_name);
+                answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
+                                                   " processing error");
+
+                // An error occurred, so make sure that we restore original data.
+                rollback = true;
+            }
+        } else {
+            rollback = true;
         }
     }
 
index d6a34655431c3dfb7d06303673f1c08a422855d0..a5837514213815864407672a60589020720b2aae 100644 (file)
@@ -398,7 +398,7 @@ public:
         // both structures are built using the same order.
         EXPECT_EQ(Element::fromJSON(expected_command)->str(),
                   entire_command->str());
-        return (createAnswer(0, "long command received ok"));
+        return (createAnswer(CONTROL_RESULT_SUCCESS, "long command received ok"));
     }
 
     /// @brief Command handler which generates long response
@@ -414,7 +414,7 @@ public:
             s << std::setw(5) << i;
             arguments->add(Element::create(s.str()));
         }
-        return (createAnswer(0, arguments));
+        return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
     }
 };
 
index eafc036108e766bc1abc66db45f853ede05dc6ff..c4a6b756e30ef1dc23e43ed79180a5d15522baf7 100644 (file)
@@ -75,7 +75,7 @@ NetconfProcess::runIO() {
 isc::data::ConstElementPtr
 NetconfProcess::shutdown(isc::data::ConstElementPtr /*args*/) {
     setShutdownFlag(true);
-    return (isc::config::createAnswer(0, "Netconf is shutting down"));
+    return (isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Netconf is shutting down"));
 }
 
 isc::data::ConstElementPtr
index 68f880cfc183e87006eacb840cbb4113a974e4ec..c3c2a7a233b2e24c9748676501e763f5b70a0cab 100644 (file)
@@ -52,7 +52,7 @@ createAnswer(const int status_code, const std::string& text,
 
 ConstElementPtr
 createAnswer() {
-    return (createAnswer(0, string(""), ConstElementPtr()));
+    return (createAnswer(CONTROL_RESULT_SUCCESS, string(""), ConstElementPtr()));
 }
 
 ConstElementPtr
index 12b3eab6587f699ad955ceeb95f6ddc8c5bee7a7..e393eeb473408b3106bd5cc4e211708d0ca74ce0 100644 (file)
@@ -42,17 +42,17 @@ TEST(CommandInterpreterTest, createAnswer) {
     EXPECT_EQ("{ \"result\": 0 }", answer->str());
 
     // Let's check if we can generate an error.
-    answer = createAnswer(1, "error");
+    answer = createAnswer(CONTROL_RESULT_ERROR, "error");
     EXPECT_EQ("{ \"result\": 1, \"text\": \"error\" }", answer->str());
 
     // This is expected to throw. When status code is non-zero (indicating error),
     // textual explanation is mandatory.
-    EXPECT_THROW(createAnswer(1, ElementPtr()), CtrlChannelError);
-    EXPECT_THROW(createAnswer(1, Element::create(1)), CtrlChannelError);
+    EXPECT_THROW(createAnswer(CONTROL_RESULT_ERROR, ElementPtr()), CtrlChannelError);
+    EXPECT_THROW(createAnswer(CONTROL_RESULT_ERROR, Element::create(1)), CtrlChannelError);
 
     // Let's check if answer can be generate with some data in it.
     ConstElementPtr arg = el("[ \"just\", \"some\", \"data\" ]");
-    answer = createAnswer(0, arg);
+    answer = createAnswer(CONTROL_RESULT_SUCCESS, arg);
     EXPECT_EQ("{ \"arguments\": [ \"just\", \"some\", \"data\" ], \"result\": 0 }",
               answer->str());
 }
index 3562e71ff35193071e5c2eaad5029d755147264d..8ec31c75f46c1f5979352021abe4d8805e3e5c47 100644 (file)
@@ -120,7 +120,7 @@ HookedCommandMgr::handleCommand(const std::string& cmd_name,
             // registered
             // for it, combine the lists of commands.
             if (!hooks_commands->empty()) {
-                response = combineCommandsLists(response, createAnswer(0, hooks_commands));
+                response = combineCommandsLists(response, createAnswer(CONTROL_RESULT_SUCCESS, hooks_commands));
             }
         }
     }
index 038314500f7a0dddf5143dd69718178174734470..2f052804f1fcb0b20d0a6d3b3b28b036335fab19 100644 (file)
@@ -191,7 +191,7 @@ public:
         // Answer will hold the result.
         ConstElementPtr answer;
         if (!config_set) {
-            answer = isc::config::createAnswer(1,
+            answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
                                  string("Can't parse NULL config"));
             return (answer);
         }
@@ -302,13 +302,13 @@ public:
             }
 
             // Everything was fine. Configuration is successful.
-            answer = isc::config::createAnswer(0, "Configuration committed.");
+            answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration committed.");
         } catch (const isc::Exception& ex) {
-            answer = isc::config::createAnswer(1,
+            answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
                         string("Configuration parsing failed: ") + ex.what());
 
         } catch (...) {
-            answer = isc::config::createAnswer(1,
+            answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
                                         string("Configuration parsing failed"));
         }
 
index 4a67183128d279580cc0ae88d0df7402dd75386f..5ee15ec7fb869a3d00a5896fdf1e197adc3ec078 100644 (file)
@@ -26,6 +26,7 @@
 
 using namespace std;
 using namespace isc;
+using namespace isc::config;
 using namespace isc::dhcp;
 using namespace isc::data;
 using namespace isc::asiolink;
@@ -76,8 +77,8 @@ DCfgMgrBase::simpleParseConfig(isc::data::ConstElementPtr config_set,
                                bool check_only,
                                const std::function<void()>& post_config_cb) {
     if (!config_set) {
-        return (isc::config::createAnswer(1,
-                                    std::string("Can't parse NULL config")));
+        return (isc::config::createAnswer(CONTROL_RESULT_ERROR,
+                                          std::string("Can't parse NULL config")));
     }
     LOG_DEBUG(dctl_logger, isc::log::DBGLVL_COMMAND, DCTL_CONFIG_START)
         .arg(redactConfig(config_set)->str());
@@ -122,7 +123,7 @@ DCfgMgrBase::simpleParseConfig(isc::data::ConstElementPtr config_set,
             }
 
             // Use the answer provided.
-            //answer = isc::config::createAnswer(0, "Configuration committed.");
+            //answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration committed.");
         } else {
             LOG_INFO(dctl_logger, DCTL_CONFIG_CHECK_COMPLETE)
                 .arg(getConfigSummary(0))
@@ -131,7 +132,7 @@ DCfgMgrBase::simpleParseConfig(isc::data::ConstElementPtr config_set,
 
     } catch (const std::exception& ex) {
         LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(ex.what());
-        answer = isc::config::createAnswer(1, ex.what());
+        answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
         rollback = true;
     }
 
index 1186729e06f0cf86a5ec29a8ebcbf58d46f067ac..78b73d6ecf74d32b0446e86738450dcd3e72bd26 100644 (file)
@@ -61,15 +61,16 @@ DStubProcess::shutdown(isc::data::ConstElementPtr /* args */) {
 
     setShutdownFlag(true);
     stopIOService();
-    return (isc::config::createAnswer(0, "Shutdown initiated."));
+    return (isc::config::createAnswer(isc::config::CONTROL_RESULT_SUCCESS,
+                                      "Shutdown initiated."));
 }
 
 isc::data::ConstElementPtr
 DStubProcess::configure(isc::data::ConstElementPtr config_set, bool check_only) {
     if (SimFailure::shouldFailOn(SimFailure::ftProcessConfigure)) {
         // Simulates a process configure failure.
-        return (isc::config::createAnswer(1,
-                "Simulated process configuration error."));
+        return (isc::config::createAnswer(isc::config::CONTROL_RESULT_ERROR,
+                                          "Simulated process configuration error."));
     }
 
     return (getCfgMgr()->simpleParseConfig(config_set, check_only));
@@ -329,7 +330,8 @@ DStubCfgMgr::createNewContext() {
 
 isc::data::ConstElementPtr
 DStubCfgMgr::parse(isc::data::ConstElementPtr /*config*/, bool /*check_only*/) {
-    return (isc::config::createAnswer(0, "It all went fine. I promise"));
+    return (isc::config::createAnswer(isc::config::CONTROL_RESULT_SUCCESS,
+                                      "It all went fine. I promise"));
 }
 
 } // namespace isc::process