2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 05:55:28 +00:00

[master] Merged trac5152 (check D2 and CA config)

This commit is contained in:
Francis Dupont
2017-03-16 16:27:28 +01:00
23 changed files with 1067 additions and 666 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -84,7 +84,8 @@ public:
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
virtual isc::data::ConstElementPtr
configure(isc::data::ConstElementPtr config_set, bool check_only);
configure(isc::data::ConstElementPtr config_set,
bool check_only = false);
/// @brief Processes the given command.
///

View File

@@ -52,7 +52,8 @@
<arg><option>-V</option></arg>
<arg><option>-W</option></arg>
<arg><option>-d</option></arg>
<arg><option>-s</option></arg>
<arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
<arg><option>-t<replaceable class="parameter">config-file</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -115,6 +116,16 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
<listitem><para>
Check the syntax of the configuration file and report the
first error if any. Note that not all parameters are
completely checked, in particular, service and client
sockets are not opened, and hook libraries are not loaded.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@@ -25,9 +25,18 @@ int main(int argc, char* argv[]) {
// 'false' value disables test mode.
controller->launch(argc, argv, false);
} catch (const VersionMessage& ex) {
std::cout << ex.what() << std::endl;
std::string msg(ex.what());
if (!msg.empty()) {
std::cout << msg << std::endl;
}
} catch (const InvalidUsage& ex) {
std::string msg(ex.what());
if (!msg.empty()) {
std::cerr << msg << std::endl;
}
ret = EXIT_FAILURE;
} catch (const isc::Exception& ex) {
std::cerr << "Service failed:" << ex.what() << std::endl;
std::cerr << "Service failed: " << ex.what() << std::endl;
ret = EXIT_FAILURE;
}

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
# Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -34,12 +34,55 @@ CONFIG="{
}
}"
# Invalid configuration (syntax error) to check that Kea can check syntax.
CONFIG_BAD_SYNTAX="{
\"Control-agent\":
{
\"http-port\": BOGUS
}
}"
# Invalid configuration (out of range port) to check that Kea can check syntax.
CONFIG_BAD_VALUE="{
\"Control-agent\":
{
\"http-port\": 80000
}
}"
bin="kea-ctrl-agent"
bin_path=@abs_top_builddir@/src/bin/agent
# Import common test library.
. @abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh
# This test verifies that syntax checking works properly. This function
# requires 3 parameters:
# testname
# config - string with a content of the config (will be written to a file)
# exp_code - expected exit code returned by kea (0 - success, 1 - failure)
syntax_check_test() {
local TESTNAME="${1}"
local CONFIG="${2}"
local EXP_CODE="${3}"
# Log the start of the test and print test name.
test_start $TESTNAME
# Remove dangling Kea instances and remove log files.
cleanup
# Create correct configuration file.
create_config "${CONFIG}"
# Check it
printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\""
${bin_path}/${bin} -t ${CFG_FILE}
exit_code=$?
if [ ${exit_code} -ne $EXP_CODE ]; then
printf "ERROR: expected exit code ${EXP_CODE}, got ${exit_code}\n"
clean_exit 1
fi
test_finish 0
}
# This test verifies that Control Agent is shut down gracefully when it
# receives a SIGINT or SIGTERM signal.
shutdown_test() {
@@ -102,4 +145,6 @@ shutdown_test() {
server_pid_file_test "${CONFIG}" DCTL_ALREADY_RUNNING
shutdown_test "ctrl-agent.sigterm_test" 15
shutdown_test "ctrl-agent.sigint_test" 2
syntax_check_test "ctrl-agent.syntax_check_success" "${CONFIG}" 0
syntax_check_test "ctrl-agent.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1
syntax_check_test "ctrl-agent.syntax_check_bad_values" "${CONFIG_BAD_VALUE}" 1

View File

@@ -37,7 +37,7 @@ to shutdown and has met the required criteria to exit.
This is a debug message issued when the DHCP-DDNS application command method
has been invoked.
% DHCP_DDNS_CONFIGURE configuration update received: %1
% DHCP_DDNS_CONFIGURE configuration %1 received: %2
This is a debug message issued when the DHCP-DDNS application configure method
has been invoked.

View File

@@ -192,17 +192,18 @@ D2Process::shutdown(isc::data::ConstElementPtr args) {
isc::data::ConstElementPtr
D2Process::configure(isc::data::ConstElementPtr config_set, bool check_only) {
LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC,
DHCP_DDNS_CONFIGURE).arg(config_set->str());
LOG_DEBUG(d2_logger, DBGLVL_TRACE_BASIC, DHCP_DDNS_CONFIGURE)
.arg(check_only ? "check" : "update")
.arg(config_set->str());
/// @todo: Implement this eventually.
isc::data::ConstElementPtr answer;
answer = getCfgMgr()->parseConfig(config_set, check_only);;
if (check_only) {
return (isc::config::createAnswer(0, "Configuration check is not supported by D2."));
return (answer);
}
int rcode = 0;
isc::data::ConstElementPtr comment;
isc::data::ConstElementPtr answer = getCfgMgr()->parseConfig(config_set);;
comment = isc::config::parseAnswer(rcode, answer);
if (rcode) {

View File

@@ -158,7 +158,8 @@ public:
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
virtual isc::data::ConstElementPtr
configure(isc::data::ConstElementPtr config_set, bool check_only);
configure(isc::data::ConstElementPtr config_set,
bool check_only = false);
/// @brief Processes the given command.
///

View File

@@ -2,7 +2,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
[<!ENTITY mdash "&#8212;">]>
<!--
- Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
- Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -52,8 +52,8 @@
<arg><option>-V</option></arg>
<arg><option>-W</option></arg>
<arg><option>-d</option></arg>
<!-- not yet <arg><option>-t</option></arg> -->
<arg><option>-c</option></arg>
<arg><option>-c<replaceable class="parameter">config-file</replaceable></option></arg>
<arg><option>-t<replaceable class="parameter">config-file</replaceable></option></arg>
</cmdsynopsis>
</refsynopsisdiv>
@@ -105,16 +105,6 @@
</para></listitem>
</varlistentry>
<!-- not yet
<varlistentry>
<term><option>-t</option></term>
<listitem><para>
Check the syntax of the configuration file and report the first
error if any.
</para></listitem>
</varlistentry>
-->
<varlistentry>
<term><option>-c</option></term>
<listitem><para>
@@ -123,6 +113,16 @@
</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-t</option></term>
<listitem><para>
Check the syntax of the configuration file and report the
first error if any. Note that not all parameters are
completely checked, in particular, service socket is
not opened.
</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -34,9 +34,18 @@ int main(int argc, char* argv[]) {
// 'false' value disables test mode.
controller->launch(argc, argv, false);
} catch (const VersionMessage& ex) {
std::cout << ex.what() << std::endl;
std::string msg(ex.what());
if (!msg.empty()) {
std::cout << msg << std::endl;
}
} catch (const InvalidUsage& ex) {
std::string msg(ex.what());
if (!msg.empty()) {
std::cerr << msg << std::endl;
}
ret = EXIT_FAILURE;
} catch (const isc::Exception& ex) {
std::cerr << "Service failed:" << ex.what() << std::endl;
std::cerr << "Service failed: " << ex.what() << std::endl;
ret = EXIT_FAILURE;
}

View File

@@ -168,7 +168,8 @@ public:
// The JSON parsed ok and we've added the defaults, pass the config
// into the Element parser and check for the expected outcome.
data::ConstElementPtr answer = cfg_mgr_->parseConfig(config_set_);
data::ConstElementPtr answer;
answer = cfg_mgr_->parseConfig(config_set_, false);
// Extract the result and error text from the answer.
int rcode = 0;
@@ -601,7 +602,7 @@ TEST_F(D2CfgMgrTest, fullConfig) {
// Verify that parsing the exact same configuration a second time
// does not cause a duplicate value errors.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
ASSERT_TRUE(checkAnswer(0));
}

View File

@@ -181,12 +181,22 @@ TEST_F(D2ControllerTest, configUpdateTests) {
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
// Verify that given a valid config we get a successful check result.
answer = checkConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
// Use an invalid configuration to verify parsing error return.
std::string config = "{ \"bogus\": 1000 } ";
config_set = isc::data::Element::fromJSON(config);
answer = updateConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
// Use an invalid configuration to verify checking error return.
answer = checkConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
}
/// @brief Command execution tests.

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
# Copyright (C) 2014-2017 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -36,7 +36,59 @@ CONFIG="{
}
}"
# Invalid configuration (invalid port) to check that D2
# Invalid configuration (syntax error) to check that Kea can check syntax.
CONFIG_BAD_SYNTAX="{
\"DhcpDdns\":
{
\"ip-address\": \"127.0.0.1\",
\"port\": BOGUS,
\"tsig-keys\": [],
\"forward-ddns\" : {},
\"reverse-ddns\" : {}
},
\"Logging\":
{
\"loggers\": [
{
\"name\": \"kea-dhcp-ddns\",
\"output_options\": [
{
\"output\": \"$LOG_FILE\"
}
],
\"severity\": \"INFO\"
}
]
}
}"
# Invalid configuration (out of range port) to check that Kea can check syntax.
CONFIG_BAD_VALUE="{
\"DhcpDdns\":
{
\"ip-address\": \"127.0.0.1\",
\"port\": 80000,
\"tsig-keys\": [],
\"forward-ddns\" : {},
\"reverse-ddns\" : {}
},
\"Logging\":
{
\"loggers\": [
{
\"name\": \"kea-dhcp-ddns\",
\"output_options\": [
{
\"output\": \"$LOG_FILE\"
}
],
\"severity\": \"INFO\"
}
]
}
}"
# Invalid value configuration (invalid port) to check that D2
# gracefully handles reconfiguration errors.
CONFIG_INVALID="{
\"DhcpDdns\":
@@ -70,6 +122,33 @@ bin_path=@abs_top_builddir@/src/bin/d2
# Import common test library.
. @abs_top_builddir@/src/lib/testutils/dhcp_test_lib.sh
# This test verifies that syntax checking works properly. This function
# requires 3 parameters:
# testname
# config - string with a content of the config (will be written to a file)
# exp_code - expected exit code returned by kea (0 - success, 1 - failure)
syntax_check_test() {
local TESTNAME="${1}"
local CONFIG="${2}"
local EXP_CODE="${3}"
# Log the start of the test and print test name.
test_start $TESTNAME
# Remove dangling Kea instances and remove log files.
cleanup
# Create correct configuration file.
create_config "${CONFIG}"
# Check it
printf "Running command %s.\n" "\"${bin_path}/${bin} -t ${CFG_FILE}\""
${bin_path}/${bin} -t ${CFG_FILE}
exit_code=$?
if [ ${exit_code} -ne $EXP_CODE ]; then
printf "ERROR: expected exit code ${EXP_CODE}, got ${exit_code}\n"
clean_exit 1
fi
test_finish 0
}
# This test verifies that D2 can be reconfigured with a SIGHUP signal.
dynamic_reconfiguration_test() {
# Log the start of the test and print test name.
@@ -233,3 +312,6 @@ shutdown_test "dhcp-ddns.sigterm_test" 15
shutdown_test "dhcp-ddns.sigint_test" 2
version_test "dhcp-ddns.version"
logger_vars_test "dhcp-ddns.variables"
syntax_check_test "dhcp-ddns.syntax_check_success" "${CONFIG}" 0
syntax_check_test "dhcp-ddns.syntax_check_bad_syntax" "${CONFIG_BAD_SYNTAX}" 1
syntax_check_test "dhcp-ddns.syntax_check_bad_values" "${CONFIG_BAD_VALUE}" 1

View File

@@ -152,7 +152,7 @@ public:
// try DHCPDDNS configure
ConstElementPtr status;
try {
status = srv_->parseConfig(d2);
status = srv_->parseConfig(d2, false);
} catch (const std::exception& ex) {
ADD_FAILURE() << "configure for " << operation
<< " failed with " << ex.what()

View File

@@ -122,7 +122,8 @@ DCfgMgrBase::setContext(DCfgContextBasePtr& context) {
}
isc::data::ConstElementPtr
DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set,
bool check_only) {
LOG_DEBUG(dctl_logger, DBGLVL_COMMAND,
DCTL_CONFIG_START).arg(config_set->str());
@@ -245,9 +246,15 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
}
// Everything was fine. Configuration set processed successfully.
LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
answer = isc::config::createAnswer(0, "Configuration committed.");
if (!check_only) {
LOG_INFO(dctl_logger, DCTL_CONFIG_COMPLETE).arg(getConfigSummary(0));
answer = isc::config::createAnswer(0, "Configuration committed.");
} else {
answer = isc::config::createAnswer(0, "Configuration seems sane.");
LOG_INFO(dctl_logger, DCTL_CONFIG_CHECK_COMPLETE)
.arg(getConfigSummary(0))
.arg(config::answerToText(answer));
}
} catch (const std::exception& ex) {
LOG_ERROR(dctl_logger, DCTL_PARSER_FAIL).arg(ex.what());
answer = isc::config::createAnswer(1, ex.what());
@@ -257,6 +264,12 @@ DCfgMgrBase::parseConfig(isc::data::ConstElementPtr config_set) {
return (answer);
}
if (check_only) {
// If this is a configuration check only, then don't actually apply
// the configuration and reverse to the previous one.
context_ = original_context;
}
return (answer);
}

View File

@@ -246,7 +246,7 @@ typedef std::vector<std::string> ElementIdList;
/// update context with parsed results
/// break on error
///
/// if an error occurred
/// if an error occurred or this is only a check
/// restore configuration context from backup
/// @endcode
///
@@ -281,7 +281,7 @@ typedef std::vector<std::string> ElementIdList;
/// 1. implementation calls simpleParseConfig from its configure method.
/// 2. simpleParseConfig makes a configuration context
/// 3. parse method from the derived class is called
/// 4. if the configuration was unsuccessful of this is only a check, the
/// 4. if the configuration was unsuccessful or this is only a check, the
/// old context is reinstantiated. If not, the configuration is kept.
///
/// See @ref isc::agent::CtrlAgentCfgMgr and @ref isc::agent::CtrlAgentProcess
@@ -303,12 +303,14 @@ public:
/// the parsing as described in the class brief.
///
/// @param config_set is a set of configuration elements to be parsed.
/// @param check_only true if the config is to be checked only, but not applied
///
/// @return an Element that contains the results of configuration composed
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
isc::data::ConstElementPtr parseConfig(isc::data::ConstElementPtr
config_set);
isc::data::ConstElementPtr
parseConfig(isc::data::ConstElementPtr config_set,
bool check_only = false);
/// @brief Acts as the receiver of new configurations.

View File

@@ -37,7 +37,7 @@ DControllerBasePtr DControllerBase::controller_;
// Note that the constructor instantiates the controller's primary IOService.
DControllerBase::DControllerBase(const char* app_name, const char* bin_name)
: app_name_(app_name), bin_name_(bin_name),
verbose_(false), spec_file_name_(""),
verbose_(false), check_only_(false), spec_file_name_(""),
io_service_(new isc::asiolink::IOService()),
io_signal_queue_() {
}
@@ -68,11 +68,17 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
parseArgs(argc, argv);
} catch (const InvalidUsage& ex) {
usage(ex.what());
throw; // rethrow it
// rethrow it with an empty message
isc_throw(InvalidUsage, "");
}
setProcName(bin_name_);
if (isCheckOnly()) {
checkConfigOnly();
return;
}
// It is important that we set a default logger name because this name
// will be used when the user doesn't provide the logging configuration
// in the Kea configuration file.
@@ -146,16 +152,71 @@ DControllerBase::launch(int argc, char* argv[], const bool test_mode) {
.arg(app_name_).arg(getpid()).arg(VERSION);
}
void
DControllerBase::checkConfigOnly() {
try {
// We need to initialize logging, in case any error
// messages are to be printed.
// This is just a test, so we don't care about lockfile.
setenv("KEA_LOCKFILE_DIR", "none", 0);
isc::dhcp::CfgMgr::instance().setDefaultLoggerName(bin_name_);
isc::dhcp::CfgMgr::instance().setVerbose(verbose_);
Daemon::loggerInit(bin_name_.c_str(), verbose_);
// Check the syntax first.
std::string config_file = getConfigFile();
if (config_file.empty()) {
// Basic sanity check: file name must not be empty.
isc_throw(InvalidUsage, "JSON configuration file not specified");
}
isc::data::ConstElementPtr whole_config = parseFile(config_file);
if (!whole_config) {
// No fallback to fromJSONFile
isc_throw(InvalidUsage, "No configuration found");
}
if (verbose_) {
std::cerr << "Syntax check OK" << std::endl;
}
// Check the logic next.
isc::data::ConstElementPtr module_config;
module_config = whole_config->get(getAppName());
if (!module_config) {
isc_throw(InvalidUsage, "Config file " << config_file <<
" does not include '" << getAppName() << "' entry");
}
// Get an application process object.
initProcess();
isc::data::ConstElementPtr answer;
answer = checkConfig(module_config);
int rcode = 0;
answer = isc::config::parseAnswer(rcode, answer);
if (rcode != 0) {
isc_throw(InvalidUsage, "Error encountered: "
<< answer->stringValue());
}
} catch (const VersionMessage&) {
throw;
} catch (const InvalidUsage&) {
throw;
} catch (const std::exception& ex) {
isc_throw(InvalidUsage, "Syntax check failed with: " << ex.what());
}
return;
}
void
DControllerBase::parseArgs(int argc, char* argv[])
{
// Iterate over the given command line options. If its a stock option
// ("s" or "v") handle it here. If its a valid custom option, then
// ("c" or "d") handle it here. If its a valid custom option, then
// invoke customOption.
int ch;
opterr = 0;
optind = 1;
std::string opts("dvVWc:" + getCustomOpts());
std::string opts("dvVWc:t:" + getCustomOpts());
while ((ch = getopt(argc, argv, opts.c_str())) != -1) {
switch (ch) {
case 'd':
@@ -182,12 +243,17 @@ DControllerBase::parseArgs(int argc, char* argv[])
break;
case 'c':
case 't':
// config file name
if (optarg == NULL) {
isc_throw(InvalidUsage, "configuration file name missing");
}
setConfigFile(optarg);
if (ch == 't') {
check_only_ = true;
}
break;
case '?': {
@@ -333,6 +399,12 @@ DControllerBase::updateConfig(isc::data::ConstElementPtr new_config) {
return (process_->configure(new_config, false));
}
// Instance method for checking new config
isc::data::ConstElementPtr
DControllerBase::checkConfig(isc::data::ConstElementPtr new_config) {
return (process_->configure(new_config, true));
}
// Instance method for executing commands
isc::data::ConstElementPtr
@@ -468,7 +540,9 @@ DControllerBase::usage(const std::string & text)
<< std::endl
<< " -d: optional, verbose output " << std::endl
<< " -c <config file name> : mandatory,"
<< " specifies name of configuration file " << std::endl;
<< " specify name of configuration file" << std::endl
<< " -t <config file name> : check the"
<< " configuration file and exit" << std::endl;
// add any derivation specific usage
std::cerr << getUsageText() << std::endl;

View File

@@ -24,6 +24,7 @@ namespace isc {
namespace process {
/// @brief Exception thrown when the command line is invalid.
/// Can be used to transmit negative messages too.
class InvalidUsage : public isc::Exception {
public:
InvalidUsage(const char* file, size_t line, const char* what) :
@@ -34,7 +35,7 @@ public:
/// Since command line argument parsing is done as part of
/// DControllerBase::launch(), it uses this exception to propagate
/// version information up to main(), when command line argument
/// -v or -V is given.
/// -v, -V or -W is given. Can be used to transmit positive messages too.
class VersionMessage : public isc::Exception {
public:
VersionMessage(const char* file, size_t line, const char* what) :
@@ -128,7 +129,7 @@ public:
/// arguments.
///
/// This function can be run in "test mode". It prevents initialization
/// of D2 module logger. This is used in unit tests which initialize logger
/// of module logger. This is used in unit tests which initialize logger
/// in their main function. Such a logger uses environmental variables to
/// control severity, verbosity etc.
///
@@ -161,6 +162,21 @@ public:
virtual isc::data::ConstElementPtr updateConfig(isc::data::ConstElementPtr
new_config);
/// @brief Instance method invoked by the configuration event handler and
/// which processes the actual configuration check. Provides behavioral
/// path for both integrated and stand-alone modes. The current
/// implementation will merge the configuration update into the existing
/// configuration and then invoke the application process' configure method
/// with a final rollback.
///
/// @param new_config is the new configuration
///
/// @return returns an Element that contains the results of configuration
/// update composed of an integer status value (0 means successful,
/// non-zero means failure), and a string explanation of the outcome.
virtual isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr
new_config);
/// @brief Reconfigures the process from a configuration file
///
/// By default the file is assumed to be a JSON text file whose contents
@@ -191,7 +207,7 @@ public:
///
/// It then extracts the set of configuration elements for the
/// module-name that matches the controller's app_name_ and passes that
/// set into @c updateConfig().
/// set into @c updateConfig() (or @c checkConfig()).
///
/// The file may contain an arbitrary number of other modules.
///
@@ -223,10 +239,10 @@ public:
/// @return an Element that contains the results of command composed
/// of an integer status value and a string explanation of the outcome.
/// The status value is one of the following:
/// D2::COMMAND_SUCCESS - Command executed successfully
/// D2::COMMAND_ERROR - Command is valid but suffered an operational
/// COMMAND_SUCCESS - Command executed successfully
/// COMMAND_ERROR - Command is valid but suffered an operational
/// failure.
/// D2::COMMAND_INVALID - Command is not recognized as valid be either
/// COMMAND_INVALID - Command is not recognized as valid be either
/// the controller or the application process.
virtual isc::data::ConstElementPtr executeCommand(const std::string&
command,
@@ -283,10 +299,10 @@ protected:
/// @return an Element that contains the results of command composed
/// of an integer status value and a string explanation of the outcome.
/// The status value is one of the following:
/// D2::COMMAND_SUCCESS - Command executed successfully
/// D2::COMMAND_ERROR - Command is valid but suffered an operational
/// COMMAND_SUCCESS - Command executed successfully
/// COMMAND_ERROR - Command is valid but suffered an operational
/// failure.
/// D2::COMMAND_INVALID - Command is not recognized as a valid custom
/// COMMAND_INVALID - Command is not recognized as a valid custom
/// controller command.
virtual isc::data::ConstElementPtr customControllerCommand(
const std::string& command, isc::data::ConstElementPtr args);
@@ -302,7 +318,7 @@ protected:
/// @brief Virtual method which returns a string containing the option
/// letters for any custom command line options supported by the derivation.
/// These are added to the stock options of "c" and "v" during command
/// These are added to the stock options of "c", "d", ..., during command
/// line interpretation.
///
/// @return returns a string containing the custom option letters.
@@ -310,6 +326,13 @@ protected:
return ("");
}
/// @brief Check the configuration
///
/// Called by @c launch() when @c check_only_ mode is enabled
/// @throw VersionMessage when successful but a message should be displayed
/// @throw InvalidUsage when an error was detected
void checkConfigOnly();
/// @brief Application-level signal processing method.
///
/// This method is the last step in processing a OS signal occurrence. It
@@ -342,6 +365,22 @@ protected:
verbose_ = value;
}
/// @brief Supplies whether or not check only mode is enabled.
///
/// @return returns true if check only is enabled.
bool isCheckOnly() const {
return (check_only_);
}
/// @brief Method for enabling or disabling check only mode.
///
/// @todo this method and @c setVerbose are currently not used.
///
/// @param value is the new value to assign the flag.
void setCheckOnly(bool value) {
check_only_ = value;
}
/// @brief Getter for fetching the controller's IOService
///
/// @return returns a pointer reference to the IOService.
@@ -385,14 +424,15 @@ protected:
/// list of options with those returned by getCustomOpts(), and uses
/// cstdlib's getopt to loop through the command line.
/// It handles stock options directly, and passes any custom options into
/// the customOption method. Currently there are only two stock options
/// -c for specifying the configuration file, and -v for verbose logging.
/// the customOption method. Currently there are only some stock options
/// -c/t for specifying the configuration file, -d for verbose logging,
/// and -v/V/W for version reports.
///
/// @param argc is the number of command line arguments supplied
/// @param argv is the array of string (char *) command line arguments
///
/// @throw InvalidUsage when there are usage errors.
/// @throw VersionMessage if the -v or -V arguments is given.
/// @throw VersionMessage if the -v, -V or -W arguments is given.
void parseArgs(int argc, char* argv[]);
@@ -536,6 +576,10 @@ private:
/// @brief Indicates if the verbose logging mode is enabled.
bool verbose_;
/// @brief Indicates if the check only mode for the configuration
/// is enabled (usually specified by the command line -t argument).
bool check_only_;
/// @brief The absolute file name of the JSON spec file.
std::string spec_file_name_;

View File

@@ -102,7 +102,7 @@ public:
///
/// @throw DProcessBaseError if an operational error is encountered.
virtual isc::data::ConstElementPtr
shutdown(isc::data::ConstElementPtr args) = 0;
shutdown(isc::data::ConstElementPtr args) = 0;
/// @brief Processes the given configuration.
///
@@ -118,7 +118,8 @@ public:
/// of an integer status value (0 means successful, non-zero means failure),
/// and a string explanation of the outcome.
virtual isc::data::ConstElementPtr
configure(isc::data::ConstElementPtr config_set, bool check_only) = 0;
configure(isc::data::ConstElementPtr config_set,
bool check_only = false) = 0;
/// @brief Processes the given command.
///

View File

@@ -115,13 +115,22 @@ TEST_F(DStubCfgMgrTest, basicParseTest) {
ASSERT_TRUE(fromJSON(config));
// Verify that we can parse a simple configuration.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
// Verify that we can check a simple configuration.
answer_ = cfg_mgr_->parseConfig(config_set_, true);
EXPECT_TRUE(checkAnswer(0));
// Verify that an unknown element error is caught and returns a failed
// parse result.
SimFailure::set(SimFailure::ftElementUnknown);
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
// Verify that an error is caught too when the config is checked for.
SimFailure::set(SimFailure::ftElementUnknown);
answer_ = cfg_mgr_->parseConfig(config_set_, true);
EXPECT_TRUE(checkAnswer(1));
}
@@ -181,7 +190,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
EXPECT_EQ(0, cfg_mgr_->getParseOrder().size());
// Parse the configuration, verify it parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
// Verify that the parsed order matches what we expected.
@@ -197,7 +206,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
EXPECT_EQ(1, cfg_mgr_->getParseOrder().size());
// Verify the configuration fails.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
// Verify that the configuration parses correctly, when the parse order
@@ -212,7 +221,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
cfg_mgr_->parsed_order_.clear();
// Verify the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
// Build expected order
@@ -238,7 +247,7 @@ TEST_F(DStubCfgMgrTest, parseOrderTest) {
EXPECT_EQ(4, cfg_mgr_->getParseOrder().size());
// Verify the configuration fails.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
}
@@ -262,7 +271,7 @@ TEST_F(DStubCfgMgrTest, simpleTypesTest) {
ASSERT_TRUE(fromJSON(config));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
ASSERT_TRUE(checkAnswer(0));
DStubContextPtr context = getStubContext();
ASSERT_TRUE(context);
@@ -301,7 +310,7 @@ TEST_F(DStubCfgMgrTest, simpleTypesTest) {
ASSERT_TRUE(fromJSON(config2));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
context = getStubContext();
ASSERT_TRUE(context);
@@ -352,7 +361,7 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
ASSERT_TRUE(fromJSON(config));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
DStubContextPtr context = getStubContext();
ASSERT_TRUE(context);
@@ -389,7 +398,7 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
// Force a failure on the last element
SimFailure::set(SimFailure::ftElementUnknown);
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(1));
context = getStubContext();
ASSERT_TRUE(context);
@@ -414,6 +423,76 @@ TEST_F(DStubCfgMgrTest, rollBackTest) {
EXPECT_TRUE(object);
}
/// @brief Tests that the configuration context is preserved during
/// check only parsing.
TEST_F(DStubCfgMgrTest, checkOnly) {
// Create a configuration with all of the parameters.
string config = "{ \"bool_test\": true , "
" \"uint32_test\": 77 , "
" \"string_test\": \"hmmm chewy\" , "
" \"map_test\" : {} , "
" \"list_test\": [] }";
ASSERT_TRUE(fromJSON(config));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_, false);
EXPECT_TRUE(checkAnswer(0));
DStubContextPtr context = getStubContext();
ASSERT_TRUE(context);
// Verify that all of parameters have the expected values.
bool actual_bool = false;
EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
EXPECT_EQ(true, actual_bool);
uint32_t actual_uint32 = 0;
EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
EXPECT_EQ(77, actual_uint32);
std::string actual_string = "";
EXPECT_NO_THROW(context->getParam("string_test", actual_string));
EXPECT_EQ("hmmm chewy", actual_string);
isc::data::ConstElementPtr object;
EXPECT_NO_THROW(context->getObjectParam("map_test", object));
EXPECT_TRUE(object);
EXPECT_NO_THROW(context->getObjectParam("list_test", object));
EXPECT_TRUE(object);
// Create a configuration which "updates" all of the parameter values.
string config2 = "{ \"bool_test\": false , "
" \"uint32_test\": 88 , "
" \"string_test\": \"ewww yuk!\" , "
" \"map_test2\" : {} , "
" \"list_test2\": [] }";
ASSERT_TRUE(fromJSON(config2));
answer_ = cfg_mgr_->parseConfig(config_set_, true);
EXPECT_TRUE(checkAnswer(0));
context = getStubContext();
ASSERT_TRUE(context);
// Verify that all of parameters have the original values.
actual_bool = false;
EXPECT_NO_THROW(context->getParam("bool_test", actual_bool));
EXPECT_EQ(true, actual_bool);
actual_uint32 = 0;
EXPECT_NO_THROW(context->getParam("uint32_test", actual_uint32));
EXPECT_EQ(77, actual_uint32);
actual_string = "";
EXPECT_NO_THROW(context->getParam("string_test", actual_string));
EXPECT_EQ("hmmm chewy", actual_string);
EXPECT_NO_THROW(context->getObjectParam("map_test", object));
EXPECT_TRUE(object);
EXPECT_NO_THROW(context->getObjectParam("list_test", object));
EXPECT_TRUE(object);
}
// Tests that configuration element position is returned by getParam variants.
TEST_F(DStubCfgMgrTest, paramPosition) {
// Create a configuration with one of each scalar types. We end them
@@ -424,7 +503,7 @@ TEST_F(DStubCfgMgrTest, paramPosition) {
ASSERT_TRUE(fromJSON(config));
// Verify that the configuration parses without error.
answer_ = cfg_mgr_->parseConfig(config_set_);
answer_ = cfg_mgr_->parseConfig(config_set_, false);
ASSERT_TRUE(checkAnswer(0));
DStubContextPtr context = getStubContext();
ASSERT_TRUE(context);

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -272,11 +272,22 @@ TEST_F(DStubControllerTest, configUpdateTests) {
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
// Verify that a valid config gets a successful check result.
answer = checkConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(0, rcode);
// Verify that an error in process configure method is handled.
SimFailure::set(SimFailure::ftProcessConfigure);
answer = updateConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
// Verify that an error is handled too when the config is checked for.
SimFailure::set(SimFailure::ftProcessConfigure);
answer = checkConfig(config_set);
isc::config::parseAnswer(rcode, answer);
EXPECT_EQ(1, rcode);
}
/// @brief Command execution tests.

View File

@@ -68,18 +68,13 @@ DStubProcess::shutdown(isc::data::ConstElementPtr /* args */) {
isc::data::ConstElementPtr
DStubProcess::configure(isc::data::ConstElementPtr config_set, bool check_only) {
if (check_only) {
return (isc::config::createAnswer(1,
"Configuration checking is not supported."));
}
if (SimFailure::shouldFailOn(SimFailure::ftProcessConfigure)) {
// Simulates a process configure failure.
return (isc::config::createAnswer(1,
"Simulated process configuration error."));
}
return (getCfgMgr()->parseConfig(config_set));
return (getCfgMgr()->parseConfig(config_set, check_only));
}
isc::data::ConstElementPtr

View File

@@ -492,6 +492,13 @@ public:
return (getController()->updateConfig(new_config));
}
/// @Wrapper to invoke the Controller's checkConfig method. Please
/// refer to DControllerBase::checkConfig for details.
isc::data::ConstElementPtr checkConfig(isc::data::ConstElementPtr
new_config) {
return (getController()->checkConfig(new_config));
}
/// @Wrapper to invoke the Controller's executeCommand method. Please
/// refer to DControllerBase::executeCommand for details.
isc::data::ConstElementPtr executeCommand(const std::string& command,