2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-22 09:57:41 +00:00

[#1671] the -t parameter now loads and checks hook libraries config

This commit is contained in:
Razvan Becheriu 2023-03-16 21:16:22 +02:00
parent 9cba85e911
commit 35238f03aa
31 changed files with 823 additions and 175 deletions

View File

@ -357,8 +357,56 @@ Since Kea 1.9.6, the ``kea-shell`` tool supports TLS.
.. _agent-launch:
Starting the Control Agent
==========================
Starting and Stopping the Control Agent
=======================================
``kea-ctrl-agent`` accepts the following command-line switches:
- ``-c file`` - specifies the configuration file.
- ``-d`` - specifies whether the agent logging should be switched to
debug/verbose mode. In verbose mode, the logging severity and
debuglevel specified in the configuration file are ignored and
"debug" severity and the maximum debuglevel (99) are assumed. The
flag is convenient for temporarily switching the server into maximum
verbosity, e.g. when debugging.
- ``-t file`` - specifies the configuration file to be tested.
``kea-netconf`` attempts to load it and conducts sanity checks;
certain checks are possible only while running the actual server. The
actual status is reported with exit code (0 = configuration appears valid,
1 = error encountered). Kea prints out log messages to standard
output and error to standard error when testing the configuration.
- ``-v`` - displays the version of ``kea-ctrl-agent`` and exits.
- ``-V`` - displays the extended version information for ``kea-ctrl-agent``
and exits. The listing includes the versions of the libraries
dynamically linked to Kea.
- ``-W`` - displays the Kea configuration report and exits. The report
is a copy of the ``config.report`` file produced by ``./configure``;
it is embedded in the executable binary.
The ``config.report`` file may also be accessed directly, via the
following command. The binary ``path`` may be found in the install
directory or in the ``.libs`` subdirectory in the source tree. For
example: ``kea/src/lib/process/.libs/``.
::
strings path/libkea-process.so | sed -n 's/;;;; //p'
::
strings path/libkea-process.a | sed -n 's/;;;; //p'
The libcfgrpt.a library can also be used from the source tree with path:
``src/lib/process/cfgrpt/.libs/``.
::
strings path/libcfgrpt.a | sed -n 's/;;;; //p'
The CA is started by running its binary and specifying the configuration
file it should use. For example:

View File

@ -141,11 +141,22 @@ directly. It accepts the following command-line switches:
The ``config.report`` file may also be accessed directly, via the
following command. The binary ``path`` may be found in the install
directory or in the ``.libs`` subdirectory in the source tree. For
example: ``kea/src/bin/d2/.libs/kea-dhcp-ddns``.
example: ``kea/src/lib/process/.libs/``.
::
strings path/kea-dhcp-ddns | sed -n 's/;;;; //p'
strings path/libkea-process.so | sed -n 's/;;;; //p'
::
strings path/libkea-process.a | sed -n 's/;;;; //p'
The libcfgrpt.a library can also be used from the source tree with path:
``src/lib/process/cfgrpt/.libs/``.
::
strings path/libcfgrpt.a | sed -n 's/;;;; //p'
Upon startup, the module loads its configuration and begins listening
for NCRs based on that configuration.

View File

@ -42,6 +42,14 @@ the following command-line switches:
comprehensive; certain checks are possible only when running the
server.
- ``-T file`` - specifies a configuration file to be tested. ``kea-dhcp4``
loads it, checks it, and exits. It performs extra checks beside ``-t`` is
doing, like establising database connections (lease db, host db, CB db,
forensic logging db), hook libraries loading and configuration parsing, etc.
It does not open unix or TCP/UDP sockets, neither does it open or rotate
files, as all these actions could interfere with a running process on the
same machine.
- ``-v`` - displays the Kea version and exits.
- ``-V`` - displays the Kea extended version with additional parameters
@ -52,6 +60,26 @@ the following command-line switches:
is a copy of the ``config.report`` file produced by ``./configure``;
it is embedded in the executable binary.
The ``config.report`` file may also be accessed directly, via the
following command. The binary ``path`` may be found in the install
directory or in the ``.libs`` subdirectory in the source tree. For
example: ``kea/src/lib/process/.libs/``.
::
strings path/libkea-process.so | sed -n 's/;;;; //p'
::
strings path/libkea-process.a | sed -n 's/;;;; //p'
The libcfgrpt.a library can also be used from the source tree with path:
``src/lib/process/cfgrpt/.libs/``.
::
strings path/libcfgrpt.a | sed -n 's/;;;; //p'
On startup, the server detects available network interfaces and
attempts to open UDP sockets on all interfaces listed in the
configuration file. Since the DHCPv4 server opens privileged ports, it

View File

@ -42,6 +42,14 @@ the following command-line switches:
comprehensive; certain checks are possible only when running the
server.
- ``-T file`` - specifies a configuration file to be tested. ``kea-dhcp4``
loads it, checks it, and exits. It performs extra checks beside ``-t`` is
doing, like establising database connections (lease db, host db, CB db,
forensic logging db), hook libraries loading and configuration parsing, etc.
It does not open unix or TCP/UDP sockets, neither does it open or rotate
files, as all these actions could interfere with a running process on the
same machine.
- ``-v`` - displays the Kea version and exits.
- ``-V`` - displays the Kea extended version with additional parameters
@ -52,6 +60,26 @@ the following command-line switches:
is a copy of the ``config.report`` file produced by ``./configure``;
it is embedded in the executable binary.
The ``config.report`` file may also be accessed directly, via the
following command. The binary ``path`` may be found in the install
directory or in the ``.libs`` subdirectory in the source tree. For
example: ``kea/src/lib/process/.libs/``.
::
strings path/libkea-process.so | sed -n 's/;;;; //p'
::
strings path/libkea-process.a | sed -n 's/;;;; //p'
The libcfgrpt.a library can also be used from the source tree with path:
``src/lib/process/cfgrpt/.libs/``.
::
strings path/libcfgrpt.a | sed -n 's/;;;; //p'
On startup, the server detects available network interfaces and
attempts to open UDP sockets on all interfaces listed in the
configuration file. Since the DHCPv6 server opens privileged ports, it

View File

@ -686,6 +686,26 @@ Starting and Stopping the NETCONF Agent
is a copy of the ``config.report`` file produced by ``./configure``;
it is embedded in the executable binary.
The ``config.report`` file may also be accessed directly, via the
following command. The binary ``path`` may be found in the install
directory or in the ``.libs`` subdirectory in the source tree. For
example: ``kea/src/lib/process/.libs/``.
::
strings path/libkea-process.so | sed -n 's/;;;; //p'
::
strings path/libkea-process.a | sed -n 's/;;;; //p'
The libcfgrpt.a library can also be used from the source tree with path:
``src/lib/process/cfgrpt/.libs/``.
::
strings path/libcfgrpt.a | sed -n 's/;;;; //p'
.. _operation-example:
A Step-by-Step NETCONF Agent Operation Example

View File

@ -49,6 +49,14 @@ The arguments are as follows:
service and control channel sockets are not opened, and hook
libraries are not loaded.
``-T config-file``
Checks the configuration file and reports the first error, if any.
It performs extra checks beside ``-t`` is doing, like establising database
connections (lease db, host db, CB db, forensic logging db), hook libraries
loading and configuration parsing, etc. It does not open unix or TCP/UDP
sockets, neither does it open or rotate files, as all these actions could
interfere with a running process on the same machine.
``-p server-port-number``
Specifies the server port number (1-65535) on which the server listens. This is
useful for testing purposes only.

View File

@ -49,6 +49,14 @@ The arguments are as follows:
service and control channel sockets are not opened, and hook
libraries are not loaded.
``-T config-file``
Checks the configuration file and reports the first error, if any.
It performs extra checks beside ``-t`` is doing, like establising database
connections (lease db, host db, CB db, forensic logging db), hook libraries
loading and configuration parsing, etc. It does not open unix or TCP/UDP
sockets, neither does it open or rotate files, as all these actions could
interfere with a running process on the same machine.
``-p server-port-number``
Specifies the server port number (1-65535) on which the server listens. This is
useful for testing purposes only.

View File

@ -1025,6 +1025,29 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
// exception free.
LibDHCP::commitRuntimeOptionDefs();
auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config);
if (notify_libraries) {
return (notify_libraries);
}
// Apply multi threading settings.
// @note These settings are applied/updated only if no errors occur while
// applying the new configuration.
// @todo This should be fixed.
try {
CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
} catch (const std::exception& ex) {
err << "Error applying multi threading settings: "
<< ex.what();
return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
}
return (answer);
}
isc::data::ConstElementPtr
ControlledDhcpv4Srv::finishConfigHookLibraries(isc::data::ConstElementPtr config) {
ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
// This hook point notifies hooks libraries that the configuration of the
// DHCPv4 server has completed. It provides the hook library with the pointer
// to the common IO service object, new server configuration in the JSON
@ -1053,27 +1076,11 @@ ControlledDhcpv4Srv::processConfig(isc::data::ConstElementPtr config) {
}
}
// Apply multi threading settings.
// @note These settings are applied/updated only if no errors occur while
// applying the new configuration.
// @todo This should be fixed.
try {
CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
} catch (const std::exception& ex) {
err << "Error applying multi threading settings: "
<< ex.what();
return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
}
return (answer);
return (ConstElementPtr());
}
isc::data::ConstElementPtr
ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) {
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
.arg(redactConfig(config)->str());
ControlledDhcpv4Srv* srv = ControlledDhcpv4Srv::getInstance();
if (!srv) {
@ -1082,6 +1089,9 @@ ControlledDhcpv4Srv::checkConfig(isc::data::ConstElementPtr config) {
return (no_srv);
}
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_RECEIVED)
.arg(srv->redactConfig(config)->str());
return (configureDhcp4Server(*srv, config, true));
}

View File

@ -105,21 +105,32 @@ public:
/// As pointer to this method is used a callback in ASIO used in
/// ModuleCCSession, it has to be static.
///
/// @param new_config textual representation of the new configuration
/// @param config textual representation of the new configuration
///
/// @return status of the config update
static isc::data::ConstElementPtr
processConfig(isc::data::ConstElementPtr new_config);
processConfig(isc::data::ConstElementPtr config);
/// @brief Configuration checker
///
/// This is a method for checking incoming configuration.
///
/// @param new_config JSON representation of the new configuration
/// @param config JSON representation of the new configuration
///
/// @return status of the config check
isc::data::ConstElementPtr
checkConfig(isc::data::ConstElementPtr new_config);
static isc::data::ConstElementPtr
checkConfig(isc::data::ConstElementPtr config);
/// @brief Configuration checker for hook libraries
///
/// This is a method for checking incoming configuration in the hooks
/// libraries. It calls dhcp4_srv_configured hook point for all hooks.
///
/// @param config JSON representation of the new configuration
///
/// @return status of the config check
static isc::data::ConstElementPtr
finishConfigHookLibraries(isc::data::ConstElementPtr config);
/// @brief Returns pointer to the sole instance of Dhcpv4Srv
///

View File

@ -11,6 +11,7 @@
#include <database/dbaccess_parser.h>
#include <database/backend_selector.h>
#include <database/server_selector.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp4/json_config_parser.h>
@ -738,7 +739,7 @@ processDhcp4Config(isc::data::ConstElementPtr config_set) {
isc::data::ConstElementPtr
configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
bool check_only) {
bool check_only, bool extra_checks) {
if (!config_set) {
ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
"Can't parse NULL config");
@ -748,21 +749,14 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
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;
int status_code = CONTROL_RESULT_SUCCESS;
isc::config::parseAnswer(status_code, answer);
if (status_code != CONTROL_RESULT_SUCCESS) {
rollback = true;
}
SrvConfigPtr srv_config;
if (!rollback) {
if (status_code == CONTROL_RESULT_SUCCESS) {
if (!check_only) {
string parameter_name;
ElementPtr mutable_cfg;
@ -803,20 +797,33 @@ 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;
status_code = CONTROL_RESULT_ERROR;
} 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;
status_code = CONTROL_RESULT_ERROR;
}
} else {
rollback = true;
if (extra_checks) {
// Re-open lease and host database with new parameters.
try {
// Get the staging configuration.
srv_config = CfgMgr::instance().getStagingCfg();
CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
string params = "universe=4 persist=false";
if (cfg_db->getExtendedInfoTablesEnabled()) {
params += " extended-info-tables=true";
}
cfg_db->setAppendedParameters(params);
cfg_db->createManagers();
} catch (const std::exception& ex) {
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
}
}
}
}
@ -824,12 +831,26 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
// 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) {
if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
try {
// Setup the command channel.
configureCommandChannel();
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
status_code = CONTROL_RESULT_ERROR;
}
}
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// No need to commit interface names as this is handled by the
// CfgMgr::commit() function.
@ -837,7 +858,21 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
D2ClientConfigPtr cfg;
cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
CfgMgr::instance().setD2ClientConfig(cfg);
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
status_code = CONTROL_RESULT_ERROR;
}
}
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// This occurs last as if it succeeds, there is no easy way to
// revert it. As a result, the failure to commit a subsequent
// change causes problems when trying to roll back.
@ -849,35 +884,32 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
}
}
// Moved from the commit block to add the config backend indication.
if (!rollback) {
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// If there are config backends, fetch and merge into staging config
server.getCBControl()->databaseConfigFetch(srv_config,
CBControlDHCPv4::FetchMode::FETCH_ALL);
if (extra_checks) {
server.getCBControl()->databaseConfigConnect(srv_config);
} else {
// If there are config backends, fetch and merge into staging config
server.getCBControl()->databaseConfigFetch(srv_config,
CBControlDHCPv4::FetchMode::FETCH_ALL);
}
} catch (const isc::Exception& ex) {
std::ostringstream err;
err << "during update from config backend database: " << ex.what();
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(err.str());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
std::ostringstream err;
@ -885,17 +917,23 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
<< "undefined configuration parsing error";
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(err.str());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
}
}
// Rollback changes as the configuration parsing failed.
if (rollback) {
if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
// Revert to original configuration of runtime option definitions
// in the libdhcp++.
LibDHCP::revertRuntimeOptionDefs();
if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
if (notify_libraries) {
return (notify_libraries);
}
}
return (answer);
}

View File

@ -8,15 +8,13 @@
#define DHCP4_CONFIG_PARSER_H
#include <cc/data.h>
#include <cc/stamped_value.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <exceptions/exceptions.h>
#include <stdint.h>
#include <string>
/// @todo: This header file and its .cc counterpart are very similar between
/// DHCPv4 and DHCPv6. They should be merged. A ticket #2355.
/// DHCPv4 and DHCPv6. They should be merged.
namespace isc {
namespace dhcp {
@ -41,9 +39,9 @@ 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.
/// 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:
@ -52,13 +50,16 @@ class Dhcpv4Srv;
/// 2 - commit failed (parsing was successful, but failed to store the
/// values in to server's configuration)
///
/// @param server the server object
/// @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
/// @param server DHCPv4 server object.
/// @param config_set a new configuration (JSON) for DHCPv4 server.
/// @param check_only whether this configuration is for testing only.
/// @param extra_checks load hooks and perform extra checks if this flag is true
/// and check_only is also true, otherwise perform simple check if
/// check_only is true.
/// @return answer that contains result of the reconfiguration.
isc::data::ConstElementPtr
configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
bool check_only = false);
bool check_only = false, bool extra_checks = false);
} // namespace dhcp
} // namespace isc

View File

@ -52,13 +52,15 @@ usage() {
<< endl;
cerr << endl;
cerr << "Usage: " << DHCP4_NAME
<< " -[v|V|W] [-d] [-{c|t} cfgfile] [-p number] [-P number]" << endl;
<< " -[v|V|W] [-d] [-{c|t|T} cfgfile] [-p number] [-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 << " -T file: check the configuration file doing hooks load and extra "
<< "checks and exit" << endl;
cerr << " -p number: specify non-standard server port number 1-65535 "
<< "(useful for testing only)" << endl;
cerr << " -P number: specify non-standard client port number 1-65535 "
@ -76,11 +78,12 @@ main(int argc, char* argv[]) {
int client_port_number = 0;
bool verbose_mode = false; // Should server be verbose?
bool check_mode = false; // Check syntax
bool load_hooks = false; // Check hooks config
// The standard config file
std::string config_file("");
while ((ch = getopt(argc, argv, "dvVWc:p:P:t:")) != -1) {
while ((ch = getopt(argc, argv, "dvVWc:p:P:t:T:")) != -1) {
switch (ch) {
case 'd':
verbose_mode = true;
@ -98,9 +101,16 @@ main(int argc, char* argv[]) {
cout << isc::detail::getConfigReport() << endl;
return (EXIT_SUCCESS);
case 'T':
load_hooks = true;
check_mode = true;
config_file = optarg;
break;
case 't':
check_mode = true;
// falls through
config_file = optarg;
break;
case 'c': // config file
config_file = optarg;
@ -184,9 +194,11 @@ main(int argc, char* argv[]) {
ControlledDhcpv4Srv server(0);
ConstElementPtr answer;
server.setProcName(DHCP4_NAME);
// 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);
answer = configureDhcp4Server(server, dhcp4, true, load_hooks);
int status_code = 0;
answer = isc::config::parseAnswer(status_code, answer);

View File

@ -20,6 +20,10 @@ static const int LIBRARY_NUMBER = 1;
// issues related to namespaces.
extern "C" {
int (*do_load)(isc::hooks::LibraryHandle& handle);
int (*do_unload)(isc::hooks::LibraryHandle& handle);
/// @brief This function is called to retrieve the multi-threading compatibility.
///
/// @return 1 which means compatible with multi-threading.

View File

@ -14,3 +14,11 @@ static const int LIBRARY_NUMBER = 2;
#include <config.h>
#include <dhcp4/tests/callout_library_common.h>
extern "C" {
int (*do_load)(isc::hooks::LibraryHandle& handle);
int (*do_unload)(isc::hooks::LibraryHandle& handle);
}

View File

@ -25,6 +25,23 @@ using namespace isc::hooks;
// issues related to namespaces.
extern "C" {
int
do_load_impl(LibraryHandle& handle) {
// Determine if this callout is configured to fail.
isc::dhcp::SrvConfigPtr config;
isc::data::ConstElementPtr const& parameters(handle.getParameters());
isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
std::string mode(mode_element ? mode_element->stringValue() : "");
if (mode == "fail-on-load") {
return (1);
}
return (0);
}
int (*do_load)(isc::hooks::LibraryHandle& handle) = do_load_impl;
int (*do_unload)(isc::hooks::LibraryHandle& handle);
/// @brief Callout which appends library number and provided arguments to
/// the marker file for dhcp4_srv_configured callout.
///

View File

@ -33,6 +33,10 @@
extern "C" {
extern int (*do_load)(isc::hooks::LibraryHandle& handle);
extern int (*do_unload)(isc::hooks::LibraryHandle& handle);
/// @brief Append digit to marker file
///
/// If the marker file does not exist, create it. Then append the single
@ -84,13 +88,23 @@ version() {
}
int
load(isc::hooks::LibraryHandle&) {
return (appendDigit(LOAD_MARKER_FILE));
load(isc::hooks::LibraryHandle& handle) {
int result = 0;
result = appendDigit(LOAD_MARKER_FILE);
if (result == 0 && do_load) {
result = do_load(handle);
}
return (result);
}
int
unload() {
return (appendDigit(UNLOAD_MARKER_FILE));
unload(isc::hooks::LibraryHandle& handle) {
int result = 0;
result = appendDigit(UNLOAD_MARKER_FILE);
if (result == 0 && do_unload) {
result = do_unload(handle);
}
return (result);
}
};

View File

@ -24,6 +24,8 @@ LOG_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/test.log"
LEASE_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/test_leases.csv"
# Path to the Kea LFC application
export KEA_LFC_EXECUTABLE="@abs_top_builddir@/src/bin/lfc/kea-lfc"
# Path to test hooks library
HOOK_PATH="@abs_top_builddir@/src/bin/dhcp4/tests/.libs/libco3.so"
# Kea configuration to be stored in the configuration file.
CONFIG="{
\"Dhcp4\":
@ -46,7 +48,7 @@ CONFIG="{
\"subnet\": \"10.0.0.0/8\",
\"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
} ],
\"dhcp-ddns\": {
\"dhcp-ddns\": {
\"enable-updates\": true,
\"qualifying-suffix\": \"\"
},
@ -63,6 +65,7 @@ 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
@ -168,6 +171,108 @@ CONFIG_INVALID="{
}
}"
# Invalid configuration (hook explicitly fails to load) to check that performing
# extra configuration checks detects the error.
INVALID_CONFIG_HOOKS_LOAD="{
\"Dhcp4\":
{
\"interfaces-config\": {
\"interfaces\": [ ]
},
\"multi-threading\": {
\"enable-multi-threading\": false
},
\"valid-lifetime\": 4000,
\"renew-timer\": 1000,
\"rebind-timer\": 2000,
\"lease-database\":
{
\"type\": \"memfile\",
\"name\": \"$LEASE_FILE\",
\"persist\": false,
\"lfc-interval\": 0
},
\"subnet4\": [
{
\"subnet\": \"10.0.0.0/8\",
\"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
} ],
\"dhcp-ddns\": {
\"enable-updates\": true,
\"qualifying-suffix\": \"\"
},
\"hooks-libraries\": [
{
\"library\": \"$HOOK_PATH\",
\"parameters\": {
\"mode\": \"fail-on-load\"
}
} ],
\"loggers\": [
{
\"name\": \"kea-dhcp4\",
\"output_options\": [
{
\"output\": \"$LOG_FILE\"
}
],
\"severity\": \"INFO\"
}
]
}
}"
# Invalid configuration (hook point returns error) to check that performing
# extra configuration checks detects the error.
INVALID_CONFIG_HOOKS_CALLOUT_FAIL="{
\"Dhcp4\":
{
\"interfaces-config\": {
\"interfaces\": [ ]
},
\"multi-threading\": {
\"enable-multi-threading\": false
},
\"valid-lifetime\": 4000,
\"renew-timer\": 1000,
\"rebind-timer\": 2000,
\"lease-database\":
{
\"type\": \"memfile\",
\"name\": \"$LEASE_FILE\",
\"persist\": false,
\"lfc-interval\": 0
},
\"subnet4\": [
{
\"subnet\": \"10.0.0.0/8\",
\"pools\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
} ],
\"dhcp-ddns\": {
\"enable-updates\": true,
\"qualifying-suffix\": \"\"
},
\"hooks-libraries\": [
{
\"library\": \"$HOOK_PATH\",
\"parameters\": {
\"mode\": \"fail-without-error\"
}
} ],
\"loggers\": [
{
\"name\": \"kea-dhcp4\",
\"output_options\": [
{
\"output\": \"$LOG_FILE\"
}
],
\"severity\": \"INFO\"
}
]
}
}"
# Set the location of the executable.
bin="kea-dhcp4"
bin_path="@abs_top_builddir@/src/bin/dhcp4"
@ -184,15 +289,22 @@ syntax_check_test() {
local test_name="${1}"
local config="${2}"
local expected_code="${3}"
local extra_check="${4}"
# 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 [ "${extra_check}" -eq 1 ]; then
printf "Running command %s.\n" "\"${bin_path}/${bin} -T ${CFG_FILE}\""
run_command \
"${bin_path}/${bin}" -T "${CFG_FILE}"
else
printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\""
run_command \
"${bin_path}/${bin}" -t "${CFG_FILE}"
fi
if [ "${EXIT_CODE}" -ne "${expected_code}" ]; then
printf 'ERROR: expected exit code %s, got %s\n' "${expected_code}" "${EXIT_CODE}"
clean_exit 1
@ -471,7 +583,9 @@ 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
syntax_check_test "dhcpv4.syntax_check_success" "${CONFIG}" 0 0
syntax_check_test "dhcpv4.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1 0
syntax_check_test "dhcpv4.syntax_check_bad_values" "${CONFIG_BAD_VALUES}" 1 0
syntax_check_test "dhcpv4.syntax_check_hooks_load_fail" "${INVALID_CONFIG_HOOKS_LOAD}" 1 1
syntax_check_test "dhcpv4.syntax_check_hooks_callout_fail" "${INVALID_CONFIG_HOOKS_CALLOUT_FAIL}" 1 1
password_redact_test "dhcpv4.password_redact_test" "$(kea_dhcp_config 4)" 0

View File

@ -880,7 +880,6 @@ ControlledDhcpv6Srv::processCommand(const string& command,
isc::data::ConstElementPtr
ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
// Single stream instance used in all error clauses
@ -1046,6 +1045,29 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
// exception free.
LibDHCP::commitRuntimeOptionDefs();
auto notify_libraries = ControlledDhcpv6Srv::finishConfigHookLibraries(config);
if (notify_libraries) {
return (notify_libraries);
}
// Apply multi threading settings.
// @note These settings are applied/updated only if no errors occur while
// applying the new configuration.
// @todo This should be fixed.
try {
CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
} catch (const std::exception& ex) {
err << "Error applying multi threading settings: "
<< ex.what();
return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
}
return (answer);
}
isc::data::ConstElementPtr
ControlledDhcpv6Srv::finishConfigHookLibraries(isc::data::ConstElementPtr config) {
ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
// This hook point notifies hooks libraries that the configuration of the
// DHCPv6 server has completed. It provides the hook library with the pointer
// to the common IO service object, new server configuration in the JSON
@ -1074,27 +1096,11 @@ ControlledDhcpv6Srv::processConfig(isc::data::ConstElementPtr config) {
}
}
// Apply multi threading settings.
// @note These settings are applied/updated only if no errors occur while
// applying the new configuration.
// @todo This should be fixed.
try {
CfgMultiThreading::apply(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading());
} catch (const std::exception& ex) {
err << "Error applying multi threading settings: "
<< ex.what();
return (isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str()));
}
return (answer);
return (ConstElementPtr());
}
isc::data::ConstElementPtr
ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_RECEIVED)
.arg(redactConfig(config)->str());
ControlledDhcpv6Srv* srv = ControlledDhcpv6Srv::getInstance();
if (!srv) {
@ -1103,6 +1109,9 @@ ControlledDhcpv6Srv::checkConfig(isc::data::ConstElementPtr config) {
return (no_srv);
}
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_COMMAND, DHCP6_CONFIG_RECEIVED)
.arg(srv->redactConfig(config)->str());
return (configureDhcp6Server(*srv, config, true));
}

View File

@ -105,21 +105,32 @@ public:
/// As pointer to this method is used a callback in ASIO used in
/// ModuleCCSession, it has to be static.
///
/// @param new_config textual representation of the new configuration
/// @param config textual representation of the new configuration
///
/// @return status of the config update
static isc::data::ConstElementPtr
processConfig(isc::data::ConstElementPtr new_config);
processConfig(isc::data::ConstElementPtr config);
/// @brief Configuration checker
///
/// This is a method for checking incoming configuration.
///
/// @param new_config JSON representation of the new configuration
/// @param config JSON representation of the new configuration
///
/// @return status of the config check
isc::data::ConstElementPtr
checkConfig(isc::data::ConstElementPtr new_config);
static isc::data::ConstElementPtr
checkConfig(isc::data::ConstElementPtr config);
/// @brief Configuration checker for hook libraries
///
/// This is a method for checking incoming configuration in the hooks
/// libraries. It calls dhcp4_srv_configured hook point for all hooks.
///
/// @param config JSON representation of the new configuration
///
/// @return status of the config check
static isc::data::ConstElementPtr
finishConfigHookLibraries(isc::data::ConstElementPtr config);
/// @brief Returns pointer to the sole instance of Dhcpv6Srv
///

View File

@ -11,9 +11,10 @@
#include <cc/command_interpreter.h>
#include <config/command_mgr.h>
#include <database/dbaccess_parser.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/dhcp6_log.h>
#include <dhcp6/dhcp6_srv.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp/libdhcp++.h>
#include <dhcp/iface_mgr.h>
#include <dhcpsrv/cb_ctl_dhcp4.h>
@ -867,7 +868,7 @@ processDhcp6Config(isc::data::ConstElementPtr config_set) {
isc::data::ConstElementPtr
configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
bool check_only) {
bool check_only, bool extra_checks) {
if (!config_set) {
ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
"Can't parse NULL config");
@ -877,21 +878,14 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
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;
int status_code = CONTROL_RESULT_SUCCESS;
isc::config::parseAnswer(status_code, answer);
if (status_code != CONTROL_RESULT_SUCCESS) {
rollback = true;
}
SrvConfigPtr srv_config;
if (!rollback) {
if (status_code == CONTROL_RESULT_SUCCESS) {
if (!check_only) {
string parameter_name;
ElementPtr mutable_cfg;
@ -932,20 +926,33 @@ 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;
status_code = CONTROL_RESULT_ERROR;
} 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;
status_code = CONTROL_RESULT_ERROR;
}
} else {
rollback = true;
if (extra_checks) {
// Re-open lease and host database with new parameters.
try {
// Get the staging configuration.
srv_config = CfgMgr::instance().getStagingCfg();
CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
string params = "universe=6 persist=false";
if (cfg_db->getExtendedInfoTablesEnabled()) {
params += " extended-info-tables=true";
}
cfg_db->setAppendedParameters(params);
cfg_db->createManagers();
} catch (const std::exception& ex) {
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
}
}
}
}
@ -953,12 +960,26 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
// 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) {
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// Setup the command channel.
configureCommandChannel();
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
status_code = CONTROL_RESULT_ERROR;
}
}
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// No need to commit interface names as this is handled by the
// CfgMgr::commit() function.
@ -966,7 +987,21 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
D2ClientConfigPtr cfg;
cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
CfgMgr::instance().setD2ClientConfig(cfg);
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
status_code = CONTROL_RESULT_ERROR;
}
}
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// This occurs last as if it succeeds, there is no easy way to
// revert it. As a result, the failure to commit a subsequent
// change causes problems when trying to roll back.
@ -978,35 +1013,32 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
}
}
// Moved from the commit block to add the config backend indication.
if (!rollback) {
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// If there are config backends, fetch and merge into staging config
server.getCBControl()->databaseConfigFetch(srv_config,
CBControlDHCPv6::FetchMode::FETCH_ALL);
if (extra_checks) {
server.getCBControl()->databaseConfigConnect(srv_config);
} else {
// If there are config backends, fetch and merge into staging config
server.getCBControl()->databaseConfigFetch(srv_config,
CBControlDHCPv6::FetchMode::FETCH_ALL);
}
} catch (const isc::Exception& ex) {
std::ostringstream err;
err << "during update from config backend database: " << ex.what();
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(err.str());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
std::ostringstream err;
@ -1014,17 +1046,23 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
<< "undefined configuration parsing error";
LOG_ERROR(dhcp6_logger, DHCP6_PARSER_COMMIT_FAIL).arg(err.str());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
// An error occurred, so make sure to restore the original data.
rollback = true;
status_code = CONTROL_RESULT_ERROR;
}
}
// Rollback changes as the configuration parsing failed.
if (rollback) {
if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
// Revert to original configuration of runtime option definitions
// in the libdhcp++.
LibDHCP::revertRuntimeOptionDefs();
if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
auto notify_libraries = ControlledDhcpv6Srv::finishConfigHookLibraries(config_set);
if (notify_libraries) {
return (notify_libraries);
}
}
return (answer);
}

View File

@ -13,12 +13,27 @@
#include <string>
/// @todo: This header file and its .cc counterpart are very similar between
/// DHCPv4 and DHCPv6. They should be merged.
namespace isc {
namespace dhcp {
class Dhcpv6Srv;
/// @brief Configures DHCPv6 server
/// @brief Configure DHCPv6 server (@c Dhcpv6Srv) with a set of configuration
/// values.
///
/// This function parses configuration information stored in @c config_set
/// and configures the @c server by applying the configuration to it.
/// It provides the strong exception guarantee as long as the underlying
/// derived class implementations of @c DhcpConfigParser meet the assumption,
/// that is, it ensures that either configuration is fully applied or the
/// state of the server is intact.
///
/// If a syntax or semantics level error happens during the configuration
/// (such as malformed configuration or invalid configuration parameter),
/// this function returns appropriate error code.
///
/// This function is called every time a new configuration is received. The
/// extra parameter is a reference to DHCPv6 server component. It is currently
@ -32,17 +47,19 @@ class Dhcpv6Srv;
/// reconfiguration statuses. It may return the following response codes:
/// 0 - configuration successful
/// 1 - malformed configuration (parsing failed)
/// 2 - commit failed (parsing was successful, but the values could not be
/// stored in the configuration).
/// 2 - commit failed (parsing was successful, but failed to store the
/// values in to server's configuration)
///
/// @param server DHCPv6 server object.
/// @param config_set a new configuration for DHCPv6 server.
/// @param check_only whether this configuration is for testing only
/// @param config_set a new configuration (JSON) for DHCPv6 server.
/// @param check_only whether this configuration is for testing only.
/// @param extra_checks load hooks and perform extra checks if this flag is true
/// and check_only is also true, otherwise perform simple check if
/// check_only is true.
/// @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,
bool check_only = false);
bool check_only = false, bool extra_checks = false);
} // namespace dhcp
} // namespace isc

View File

@ -52,13 +52,15 @@ usage() {
<< endl;
cerr << endl;
cerr << "Usage: " << DHCP6_NAME
<< " -[v|V|W] [-d] [-{c|t} cfgfile] [-p number] [-P number]" << endl;
<< " -[v|V|W] [-d] [-{c|t|T} cfgfile] [-p number] [-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 << " -T file: check the configuration file doing hooks load and extra "
<< "checks and exit" << endl;
cerr << " -p number: specify non-standard server port number 1-65535 "
<< "(useful for testing only)" << endl;
cerr << " -P number: specify non-standard client port number 1-65535 "
@ -76,11 +78,12 @@ main(int argc, char* argv[]) {
int client_port_number = 0;
bool verbose_mode = false; // Should server be verbose?
bool check_mode = false; // Check syntax
bool load_hooks = false; // Check hooks config
// The standard config file
std::string config_file("");
while ((ch = getopt(argc, argv, "dvVWc:p:P:t:")) != -1) {
while ((ch = getopt(argc, argv, "dvVWc:p:P:t:T:")) != -1) {
switch (ch) {
case 'd':
verbose_mode = true;
@ -98,9 +101,16 @@ main(int argc, char* argv[]) {
cout << isc::detail::getConfigReport() << endl;
return (EXIT_SUCCESS);
case 'T':
load_hooks = true;
check_mode = true;
config_file = optarg;
break;
case 't':
check_mode = true;
// falls through
config_file = optarg;
break;
case 'c': // config file
config_file = optarg;
@ -184,9 +194,11 @@ main(int argc, char* argv[]) {
ControlledDhcpv6Srv server(0);
ConstElementPtr answer;
server.setProcName(DHCP6_NAME);
// 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);
answer = configureDhcp6Server(server, dhcp6, true, load_hooks);
int status_code = 0;
answer = isc::config::parseAnswer(status_code, answer);

View File

@ -20,6 +20,10 @@ static const int LIBRARY_NUMBER = 1;
// issues related to namespaces.
extern "C" {
int (*do_load)(isc::hooks::LibraryHandle& handle);
int (*do_unload)(isc::hooks::LibraryHandle& handle);
/// @brief This function is called to retrieve the multi-threading compatibility.
///
/// @return 1 which means compatible with multi-threading.

View File

@ -14,3 +14,11 @@ static const int LIBRARY_NUMBER = 2;
#include <config.h>
#include <dhcp6/tests/callout_library_common.h>
extern "C" {
int (*do_load)(isc::hooks::LibraryHandle& handle);
int (*do_unload)(isc::hooks::LibraryHandle& handle);
}

View File

@ -24,6 +24,23 @@ using namespace isc::hooks;
// issues related to namespaces.
extern "C" {
int
do_load_impl(LibraryHandle& handle) {
// Determine if this callout is configured to fail.
isc::dhcp::SrvConfigPtr config;
isc::data::ConstElementPtr const& parameters(handle.getParameters());
isc::data::ConstElementPtr mode_element(parameters ? parameters->get("mode") : 0);
std::string mode(mode_element ? mode_element->stringValue() : "");
if (mode == "fail-on-load") {
return (1);
}
return (0);
}
int (*do_load)(isc::hooks::LibraryHandle& handle) = do_load_impl;
int (*do_unload)(isc::hooks::LibraryHandle& handle);
/// @brief Callout which appends library number and provided arguments to
/// the marker file for dhcp6_srv_configured callout.
///

View File

@ -33,6 +33,10 @@
extern "C" {
extern int (*do_load)(isc::hooks::LibraryHandle& handle);
extern int (*do_unload)(isc::hooks::LibraryHandle& handle);
/// @brief Append digit to marker file
///
/// If the marker file does not exist, create it. Then append the single
@ -84,13 +88,23 @@ version() {
}
int
load(isc::hooks::LibraryHandle&) {
return (appendDigit(LOAD_MARKER_FILE));
load(isc::hooks::LibraryHandle& handle) {
int result = 0;
result = appendDigit(LOAD_MARKER_FILE);
if (result == 0 && do_load) {
result = do_load(handle);
}
return (result);
}
int
unload() {
return (appendDigit(UNLOAD_MARKER_FILE));
unload(isc::hooks::LibraryHandle& handle) {
int result = 0;
result = appendDigit(UNLOAD_MARKER_FILE);
if (result == 0 && do_unload) {
result = do_unload(handle);
}
return (result);
}
};

View File

@ -24,10 +24,13 @@ LOG_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/test.log"
LEASE_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/test_leases.csv"
# Path to the Kea LFC application
export KEA_LFC_EXECUTABLE="@abs_top_builddir@/src/bin/lfc/kea-lfc"
# Path to test hooks library
HOOK_PATH="@abs_top_builddir@/src/bin/dhcp6/tests/.libs/libco3.so"
# Kea configuration to be stored in the configuration file.
CONFIG="{
\"Dhcp6\":
{ \"interfaces-config\": {
{
\"interfaces-config\": {
\"interfaces\": [ ]
},
\"server-id\": {
@ -67,6 +70,7 @@ 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
@ -102,6 +106,7 @@ CONFIG_BAD_SYNTAX="{
]
}
}"
# Invalid configuration (negative preferred-lifetime) to check that Kea
# gracefully handles reconfiguration errors.
CONFIG_INVALID="{
@ -142,7 +147,8 @@ CONFIG_INVALID="{
# it is defined in. Syntactically the config is correct, though.
CONFIG_BAD_VALUES="{
\"Dhcp6\":
{ \"interfaces-config\": {
{
\"interfaces-config\": {
\"interfaces\": [ ]
},
\"server-id\": {
@ -172,6 +178,117 @@ CONFIG_BAD_VALUES="{
}
}"
# Invalid configuration (hook explicitly fails to load) to check that performing
# extra configuration checks detects the error.
INVALID_CONFIG_HOOKS_LOAD="{
\"Dhcp6\":
{
\"interfaces-config\": {
\"interfaces\": [ ]
},
\"multi-threading\": {
\"enable-multi-threading\": false
},
\"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:1::/64\",
\"pools\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
} ],
\"dhcp-ddns\": {
\"enable-updates\": true,
\"qualifying-suffix\": \"\"
},
\"hooks-libraries\": [
{
\"library\": \"$HOOK_PATH\",
\"parameters\": {
\"mode\": \"fail-on-load\"
}
} ],
\"loggers\": [
{
\"name\": \"kea-dhcp6\",
\"output_options\": [
{
\"output\": \"$LOG_FILE\"
}
],
\"severity\": \"INFO\"
}
]
}
}"
# Invalid configuration (hook point returns error) to check that performing
# extra configuration checks detects the error.
INVALID_CONFIG_HOOKS_CALLOUT_FAIL="{
\"Dhcp6\":
{
\"interfaces-config\": {
\"interfaces\": [ ]
},
\"multi-threading\": {
\"enable-multi-threading\": false
},
\"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:1::/64\",
\"pools\": [ { \"pool\": \"2001:db8:1::10-2001:db8:1::100\" } ]
} ],
\"dhcp-ddns\": {
\"enable-updates\": true,
\"qualifying-suffix\": \"\"
},
\"hooks-libraries\": [
{
\"library\": \"$HOOK_PATH\",
\"parameters\": {
\"mode\": \"fail-without-error\"
}
} ],
\"loggers\": [
{
\"name\": \"kea-dhcp6\",
\"output_options\": [
{
\"output\": \"$LOG_FILE\"
}
],
\"severity\": \"INFO\"
}
]
}
}"
# Set the location of the executable.
bin="kea-dhcp6"
@ -189,15 +306,22 @@ syntax_check_test() {
local test_name="${1}"
local config="${2}"
local expected_code="${3}"
local extra_check="${4}"
# 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 [ "${extra_check}" -eq 1 ]; then
printf "Running command %s.\n" "\"${bin_path}/${bin} -T ${CFG_FILE}\""
run_command \
"${bin_path}/${bin}" -T "${CFG_FILE}"
else
printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\""
run_command \
"${bin_path}/${bin}" -t "${CFG_FILE}"
fi
if [ "${EXIT_CODE}" -ne "${expected_code}" ]; then
printf 'ERROR: expected exit code %s, got %s\n' "${expected_code}" "${EXIT_CODE}"
clean_exit 1
@ -479,7 +603,9 @@ 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
syntax_check_test "dhcpv6.syntax_check_success" "${CONFIG}" 0 0
syntax_check_test "dhcpv6.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1 0
syntax_check_test "dhcpv6.syntax_check_bad_values" "${CONFIG_BAD_VALUES}" 1 0
syntax_check_test "dhcpv6.syntax_check_hooks_load_fail" "${INVALID_CONFIG_HOOKS_LOAD}" 1 1
syntax_check_test "dhcpv6.syntax_check_hooks_callout_fail" "${INVALID_CONFIG_HOOKS_CALLOUT_FAIL}" 1 1
password_redact_test "dhcpv6.password_redact_test" "$(kea_dhcp_config 6)" 0

View File

@ -186,7 +186,7 @@ is internal server error and a bug report should be created.
% HA_DHCP6_START_SERVICE_FAILED failed to start DHCPv4 HA service in dhcp6_srv_configured callout: %1
This error message is issued when an attempt to start High Availability service
for the DHCPv4 server failed in the dhcp4_srv_configured callout. This
for the DHCPv6 server failed in the dhcp6_srv_configured callout. This
is internal server error and a bug report should be created.
% HA_DHCP_DISABLE_COMMUNICATIONS_FAILED failed to send request to disable DHCP service of %1: %2
@ -242,7 +242,7 @@ servers to resume the HA service.
% HA_INIT_OK loading High Availability hooks library successful
This informational message indicates that the High Availability hooks library
has been loaded successfully.
has been loaded successfully. Enjoy!
% HA_INVALID_PARTNER_STATE_COMMUNICATION_RECOVERY partner is in the communication-recovery state unexpectedly
This warning message is issued when a partner is in the communication-recovery

View File

@ -545,7 +545,7 @@ Debug message issued when triggered an action to retrieve type
% MYSQL_CB_INIT_OK loading MYSQL CB hooks library successful
This informational message indicates that the MySQL Configuration Backend hooks
library has been loaded successfully.
library has been loaded successfully. Enjoy!
% MYSQL_CB_NO_TLS TLS was required but is not used
This error message is issued when TLS for the connection was required but

View File

@ -545,7 +545,7 @@ Debug message issued when triggered an action to retrieve type
% PGSQL_CB_INIT_OK loading Postgres CB hooks library successful
This informational message indicates that the Postgres Configuration Backend hooks
library has been loaded successfully.
library has been loaded successfully. Enjoy!
% PGSQL_CB_NO_TLS_SUPPORT Attempt to configure TLS (unsupported for PostgreSQL): %1
This error message is printed when TLS support was required in the Kea

View File

@ -41,6 +41,12 @@ EXPECTED_VERSION="@PACKAGE_VERSION@"
export KEA_LFC_EXECUTABLE="@abs_top_builddir@/src/bin/lfc/kea-lfc"
export KEA_LOCKFILE_DIR="@abs_top_builddir@/test_lockfile_dir"
export KEA_PIDFILE_DIR="@abs_top_builddir@/test_pidfile_dir"
KEA_DHCP4_LOAD_MARKER_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/load_marker.txt"
KEA_DHCP4_UNLOAD_MARKER_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/unload_marker.txt"
KEA_DHCP4_SRV_CONFIG_MARKER_FILE="@abs_top_builddir@/src/bin/dhcp4/tests/srv_config_marker_file.txt"
KEA_DHCP6_LOAD_MARKER_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/load_marker.txt"
KEA_DHCP6_UNLOAD_MARKER_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/unload_marker.txt"
KEA_DHCP6_SRV_CONFIG_MARKER_FILE="@abs_top_builddir@/src/bin/dhcp6/tests/srv_config_marker_file.txt"
# A list of Kea processes, mainly used by the cleanup functions.
KEA_PROCS="kea-dhcp4 kea-dhcp6 kea-dhcp-ddns kea-ctrl-agent"
@ -656,7 +662,13 @@ cleanup() {
"${CFG_FILE-}" \
"${D2_CFG_FILE-}" \
"${DHCP4_CFG_FILE-}" \
"${KEA_DHCP4_LOAD_MARKER_FILE-}" \
"${KEA_DHCP4_UNLOAD_MARKER_FILE-}" \
"${KEA_DHCP4_SRV_CONFIG_MARKER_FILE-}" \
"${DHCP6_CFG_FILE-}" \
"${KEA_DHCP6_LOAD_MARKER_FILE-}" \
"${KEA_DHCP6_UNLOAD_MARKER_FILE-}" \
"${KEA_DHCP6_SRV_CONFIG_MARKER_FILE-}" \
"${KEACTRL_CFG_FILE-}" \
"${KEA_LOCKFILE_DIR-}" \
"${KEA_PIDFILE_DIR-}" \