Commit a07ea817 authored by Francis Dupont's avatar Francis Dupont

[#916] Checkpoint before regen

parent 6ca88096
......@@ -91,11 +91,11 @@ directory.
"loggers" object) moved inside the configuration objects (maps) for the
respective Kea modules. For example: the "Dhcp4" map contains the
"loggers" object specifying logging configuration for the DHCPv4
server. Backward compatibility is maintained until at least Kea 1.7.0
server. Backward compatibility is maintained until Kea 1.7.8
release; it will be possible to specify the "Logging" object at the top
configuration level and "loggers" objects at the module configuration
level. Ultimately, support for the top-level "Logging" object will be
removed.
level. Finally, support for the top-level "Logging" object was
removed in Kea 1.7.0.
The specification of several supported elements (e.g. "Dhcp4",
"Dhcp6") in a single configuration file can be confusing and works
......
......@@ -148,12 +148,9 @@ above this object is called ``Dhcp4``.
In the current Kea release it is possible to specify configurations
of multiple modules within a single configuration file, but this is
not recommended and support for it will be removed in a future
release. The only object, besides the one specifying module
configuration, which can be (and usually was) included in the same file
is ``Logging``. However, we don't include this object in the example
above for clarity; its content, the list of loggers, should now be
inside the ``Dhcp4`` object instead of the deprecated object.
not recommended and support for it was removed in 1.7.9 release,
including the ``Logging`` object: its previous content, the list
of loggers, must now inside be the ``Dhcp4`` object.
The Dhcp4 configuration starts with the ``"Dhcp4": {`` line and ends
with the corresponding closing brace (in the above example, the brace
......
......@@ -149,12 +149,9 @@ above this object is called ``Dhcp6``.
In the current Kea release it is possible to specify configurations
of multiple modules within a single configuration file, but this is
not recommended and support for it will be removed in a future
release. The only object, besides the one specifying module
configuration, which can be (and usually was) included in the same file
is ``Logging``. However, we don't include this object in the example
above for clarity; its content, the list of loggers, should now be
inside the ``Dhcp6`` object instead of this deprecated object.
not recommended and support for it was removed in 1.7.9 release,
including the ``Logging`` object: its previous content, the list
of loggers, must now inside be the ``Dhcp6`` object.
The Dhcp6 configuration starts with the ``"Dhcp6": {`` line and ends
with the corresponding closing brace (in the above example, the brace
......
......@@ -17,8 +17,8 @@ useful when debugging a problem.
The logging system in Kea is configured through the loggers entry in the
server section of your configuration file. In previous Kea releases this
entry was in an independent Logging section; this is still supported for
backward compatibility.
entry was in an independent Logging section; this was still supported
for backward compatibility until Kea 1.7.8 included.
Loggers
-------
......
......@@ -343,7 +343,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
// Command arguments are expected to be:
// { "Dhcp4": { ... } }
// The Logging component is supported by backward compatiblity.
if (!args) {
message = "Missing mandatory 'arguments' parameter.";
} else {
......@@ -355,6 +354,25 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
}
}
// Check unsupported objects.
if (message.empty()) {
for (auto obj : args->mapValue()) {
const string& obj_name = obj.first;
if (obj_name != "Dhcp4") {
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_UNSUPPORTED_OBJECT)
.arg(obj_name);
if (message.empty()) {
message = "Unsupported '" + obj_name + "' parameter";
} else {
message += " (and '" + obj_name + "')";
}
}
}
if (!message.empty()) {
message += ".";
}
}
if (!message.empty()) {
// Something is amiss with arguments, return a failure response.
ConstElementPtr result = isc::config::createAnswer(status_code,
......@@ -375,20 +393,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
// configuration attempts.
CfgMgr::instance().rollback();
// Check deprecated, obsolete or unknown (aka unsupported) objects.
list<string> unsupported;
for (auto obj : args->mapValue()) {
const string& obj_name = obj.first;
if ((obj_name == "Dhcp4") || (obj_name == "Logging")) {
continue;
}
unsupported.push_back(obj_name);
}
// Relocate Logging: if there is a global Logging object takes its
// loggers entry, move the entry to Dhcp4 and remove now empty Logging.
Daemon::relocateLogging(args, "Dhcp4");
// Parse the logger configuration explicitly into the staging config.
// Note this does not alter the current loggers, they remain in
// effect until we apply the logging config below. If no logging
......@@ -399,15 +403,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
// out what exactly is wrong with the new config in case of problems.
CfgMgr::instance().getStagingCfg()->applyLoggingCfg();
// Log unsupported objects.
if (!unsupported.empty()) {
for (auto name : unsupported) {
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_UNSUPPORTED_OBJECT).arg(name);
}
// Will return an error in a future version.
}
// Now we configure the server proper.
ConstElementPtr result = processConfig(dhcp4);
......@@ -452,6 +447,25 @@ ControlledDhcpv4Srv::commandConfigTestHandler(const string&,
}
}
// Check unsupported objects.
if (message.empty()) {
for (auto obj : args->mapValue()) {
const string& obj_name = obj.first;
if (obj_name != "Dhcp4") {
LOG_ERROR(dhcp4_logger, DHCP4_CONFIG_UNSUPPORTED_OBJECT)
.arg(obj_name);
if (message.empty()) {
message = "Unsupported '" + obj_name + "' parameter";
} else {
message += " (and '" + obj_name + "')";
}
}
}
if (!message.empty()) {
message += ".";
}
}
if (!message.empty()) {
// Something is amiss with arguments, return a failure response.
ConstElementPtr result = isc::config::createAnswer(status_code,
......@@ -459,13 +473,6 @@ ControlledDhcpv4Srv::commandConfigTestHandler(const string&,
return (result);
}
// Check obsolete objects.
// Relocate Logging. Note this allows to check the loggers configuration.
Daemon::relocateLogging(args, "Dhcp4");
// Log obsolete objects and return an error.
// stop thread pool (if running)
MultiThreadingCriticalSection cs;
......
......@@ -137,8 +137,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
return isc::dhcp::Dhcp4Parser::make_SUB_DHCP_DDNS(driver.loc_);
case Parser4Context::PARSER_CONFIG_CONTROL:
return isc::dhcp::Dhcp4Parser::make_SUB_CONFIG_CONTROL(driver.loc_);
case Parser4Context::PARSER_LOGGING:
return isc::dhcp::Dhcp4Parser::make_SUB_LOGGING(driver.loc_);
}
}
%}
......@@ -1036,19 +1034,9 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
}
\"Logging\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::CONFIG:
return isc::dhcp::Dhcp4Parser::make_LOGGING(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("Logging", driver.loc_);
}
}
\"loggers\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::LOGGING:
return isc::dhcp::Dhcp4Parser::make_LOGGERS(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("loggers", driver.loc_);
......@@ -1761,33 +1749,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
return isc::dhcp::Dhcp4Parser::make_STRING(tmp, driver.loc_);
}
\"Dhcp6\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::CONFIG:
return isc::dhcp::Dhcp4Parser::make_DHCP6(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("Dhcp6", driver.loc_);
}
}
\"DhcpDdns\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::CONFIG:
return isc::dhcp::Dhcp4Parser::make_DHCPDDNS(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("DhcpDdns", driver.loc_);
}
}
\"Control-agent\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::CONFIG:
return isc::dhcp::Dhcp4Parser::make_CONTROL_AGENT(driver.loc_);
default:
return isc::dhcp::Dhcp4Parser::make_STRING("Control-agent", driver.loc_);
}
}
\"4o6-interface\" {
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::SUBNET4:
......
......@@ -224,7 +224,6 @@ using namespace std;
HOSTNAME_CHAR_SET "hostname-char-set"
HOSTNAME_CHAR_REPLACEMENT "hostname-char-replacement"
LOGGING "Logging"
LOGGERS "loggers"
OUTPUT_OPTIONS "output_options"
OUTPUT "output"
......@@ -235,10 +234,6 @@ using namespace std;
MAXVER "maxver"
PATTERN "pattern"
DHCP6 "Dhcp6"
DHCPDDNS "DhcpDdns"
CONTROL_AGENT "Control-agent"
// Not real tokens, just a way to signal what the parser is expected to
// parse.
TOPLEVEL_JSON
......@@ -253,7 +248,6 @@ using namespace std;
SUB_OPTION_DATA
SUB_HOOKS_LIBRARY
SUB_DHCP_DDNS
SUB_LOGGING
SUB_CONFIG_CONTROL
;
......@@ -292,7 +286,6 @@ start: TOPLEVEL_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } sub_json
| SUB_OPTION_DATA { ctx.ctx_ = ctx.OPTION_DATA; } sub_option_data
| SUB_HOOKS_LIBRARY { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } sub_hooks_library
| SUB_DHCP_DDNS { ctx.ctx_ = ctx.DHCP_DDNS; } sub_dhcp_ddns
| SUB_LOGGING { ctx.ctx_ = ctx.LOGGING; } sub_logging
| SUB_CONFIG_CONTROL { ctx.ctx_ = ctx.CONFIG_CONTROL; } sub_config_control
;
......@@ -400,8 +393,7 @@ unknown_map_entry: STRING COLON {
};
// This defines the top-level { } that holds Control-agent, Dhcp6, Dhcp4,
// DhcpDdns or Logging objects.
// This defines the top-level { } that holds Dhcp4 only object.
syntax_map: LCURLY_BRACKET {
// This code is executed when we're about to start parsing
// the content of the map
......@@ -416,24 +408,20 @@ syntax_map: LCURLY_BRACKET {
ctx.require("Dhcp4", ctx.loc2pos(@1), ctx.loc2pos(@4));
};
// This represents top-level entries: Control-agent, Dhcp6, Dhcp4,
// DhcpDdns, Logging
// This represents top-level entries: Dhcp4
global_objects: global_object
| global_objects COMMA global_object
;
// This represents a single top level entry, e.g. Dhcp4 or DhcpDdns.
// This represents a single top level entry, e.g. Dhcp4.
global_object: dhcp4_object
| logging_object
| dhcp6_json_object
| dhcpddns_json_object
| control_agent_json_object
| unknown_map_entry
;
dhcp4_object: DHCP4 {
// This code is executed when we're about to start parsing
// the content of the map
// Prevent against duplicate.
ctx.unique("Dhcp4", ctx.loc2pos(@1));
ElementPtr m(new MapElement(ctx.loc2pos(@1)));
ctx.stack_.back()->set("Dhcp4", m);
ctx.stack_.push_back(m);
......@@ -2299,29 +2287,6 @@ dep_hostname_char_replacement: HOSTNAME_CHAR_REPLACEMENT {
};
// JSON entries for Dhcp4 and DhcpDdns
dhcp6_json_object: DHCP6 {
ctx.enter(ctx.NO_KEYWORD);
} COLON value {
ctx.stack_.back()->set("Dhcp6", $4);
ctx.leave();
};
dhcpddns_json_object: DHCPDDNS {
ctx.enter(ctx.NO_KEYWORD);
} COLON value {
ctx.stack_.back()->set("DhcpDdns", $4);
ctx.leave();
};
control_agent_json_object: CONTROL_AGENT {
ctx.enter(ctx.NO_KEYWORD);
} COLON value {
ctx.stack_.back()->set("Control-agent", $4);
ctx.leave();
};
// Config control information element
config_control: CONFIG_CONTROL {
......@@ -2369,41 +2334,8 @@ config_fetch_wait_time: CONFIG_FETCH_WAIT_TIME COLON INTEGER {
ctx.stack_.back()->set("config-fetch-wait-time", value);
};
// --- logging entry -----------------------------------------
// This defines the top level "Logging" object. It parses
// the following "Logging": { ... }. The ... is defined
// by logging_params
logging_object: LOGGING {
ElementPtr m(new MapElement(ctx.loc2pos(@1)));
ctx.stack_.back()->set("Logging", m);
ctx.stack_.push_back(m);
ctx.enter(ctx.LOGGING);
} COLON LCURLY_BRACKET logging_params RCURLY_BRACKET {
ctx.stack_.pop_back();
ctx.leave();
};
sub_logging: LCURLY_BRACKET {
// Parse the Logging map
ElementPtr m(new MapElement(ctx.loc2pos(@1)));
ctx.stack_.push_back(m);
} logging_params RCURLY_BRACKET {
// parsing completed
};
// This defines the list of allowed parameters that may appear
// in the top-level Logging object. It can either be a single
// parameter or several parameters separated by commas.
logging_params: logging_param
| logging_params COMMA logging_param
;
// There's currently only one parameter defined, which is "loggers".
logging_param: loggers;
// --- loggers entry -----------------------------------------
// "loggers", the only parameter currently defined in "Logging" object,
// is "loggers": [ ... ].
loggers: LOGGERS {
ElementPtr l(new ListElement(ctx.loc2pos(@1)));
ctx.stack_.back()->set("loggers", l);
......@@ -2420,7 +2352,7 @@ loggers_entries: logger_entry
| loggers_entries COMMA logger_entry
;
// This defines a single entry defined in loggers in Logging.
// This defines a single entry defined in loggers.
logger_entry: LCURLY_BRACKET {
ElementPtr l(new MapElement(ctx.loc2pos(@1)));
ctx.stack_.back()->add(l);
......
......@@ -111,6 +111,18 @@ Parser4Context::require(const std::string& name,
}
}
void
Parser4Context::unique(const std::string& name,
isc::data::Element::Position loc)
{
ConstElementPtr value = stack_.back()->get(name);
if (value) {
isc_throw(Dhcp4ParseError, loc << ": duplicate " << name
<< " entries in " << contextName()
<< " map (previous at " << value->getPosition() << ")");
}
}
void
Parser4Context::enter(const ParserContext& ctx)
{
......@@ -140,8 +152,6 @@ Parser4Context::contextName()
return ("toplevel");
case DHCP4:
return ("Dhcp4");
case LOGGING:
return ("Logging");
case INTERFACES_CONFIG:
return ("interfaces-config");
case DHCP_SOCKET_TYPE:
......
......@@ -90,9 +90,6 @@ public:
/// This will parse the input as config-control.
PARSER_CONFIG_CONTROL,
/// This will parse the content of Logging.
PARSER_LOGGING
} ParserType;
/// @brief Default constructor.
......@@ -192,28 +189,34 @@ public:
///
/// @param name name of the parameter to check
/// @param open_loc location of the opening curly bracket
/// @param close_loc ocation of the closing curly bracket
/// @param close_loc location of the closing curly bracket
/// @throw Dhcp4ParseError
void require(const std::string& name,
isc::data::Element::Position open_loc,
isc::data::Element::Position close_loc);
/// @brief Check if a parameter is already present
///
/// Check if a parameter is already present in the map at the top
/// of the stack and raise an error when it is.
///
/// @param name name of the parameter to check
/// @param loc location of the current parameter
/// @throw Dhcp4ParseError
void unique(const std::string& name,
isc::data::Element::Position loc);
/// @brief Defines syntactic contexts for lexical tie-ins
typedef enum {
///< This one is used in pure JSON mode.
NO_KEYWORD,
///< Used while parsing top level (that contains Dhcp4, Logging and others)
///< Used while parsing top level (that contains Dhcp4)
CONFIG,
///< Used while parsing content of Dhcp4.
DHCP4,
// not yet Dhcp6, DhcpDdns,
///< Used while parsing content of Logging
LOGGING,
/// Used while parsing Dhcp4/interfaces structures.
INTERFACES_CONFIG,
......
......@@ -768,15 +768,13 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
string control_socket_footer =
"\" \n} \n";
string logger_txt =
" \"Logging\": { \n"
" \"loggers\": [ { \n"
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output_options\": [{ \n"
" \"output\": \"/dev/null\" \n"
" }] \n"
" }] \n"
" } \n";
" }] \n";
std::ostringstream os;
......@@ -791,9 +789,8 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
<< control_socket_header
<< socket_path_
<< control_socket_footer
<< "}\n" // close dhcp4
<< ","
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command
......@@ -823,7 +820,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
<< socket_path_
<< control_socket_footer
<< "}\n" // close dhcp4
"}}";
<< "}}";
// Send the config-set command
sendUnixCommand(os.str(), response);
......@@ -945,15 +942,13 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configTest) {
string control_socket_footer =
"\" \n} \n";
string logger_txt =
" \"Logging\": { \n"
" \"loggers\": [ { \n"
" ,\"loggers\": [ { \n"
" \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n"
" \"output_options\": [{ \n"
" \"output\": \"/dev/null\" \n"
" }] \n"
" }] \n"
" } \n";
" }] \n";
std::ostringstream os;
......@@ -966,9 +961,8 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configTest) {
<< control_socket_header
<< socket_path_
<< control_socket_footer
<< "}\n" // close dhcp4
<< ","
<< logger_txt
<< "}\n" // close dhcp4
<< "}}";
// Send the config-set command
......@@ -995,7 +989,7 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configTest) {
<< socket_path_
<< control_socket_footer
<< "}\n" // close dhcp4
"}}";
<< "}}";
// Send the config-test command
sendUnixCommand(os.str(), response);
......
......@@ -39,11 +39,7 @@ CONFIG="{
\"dhcp-ddns\": {
\"enable-updates\": true,
\"qualifying-suffix\": \"\"
}
},
\"Logging\":
{
},
\"loggers\": [
{
\"name\": \"kea-dhcp4\",
......@@ -77,11 +73,7 @@ CONFIG_BAD_SYNTAX="{
{
\"subnet\": \"10.0.0.0/8\",
\"pool\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
} ]
},
\"Logging\":
{
} ],
\"loggers\": [
{
\"name\": \"kea-dhcp4\",
......@@ -116,11 +108,7 @@ CONFIG_BAD_VALUES="{
{
\"subnet\": \"10.0.0.0/8\",
\"pools\": [ { \"pool\": \"192.168.0.10-192.168.0.100\" } ]
} ]
},
\"Logging\":
{
} ],
\"loggers\": [
{
\"name\": \"kea-dhcp4\",
......@@ -155,11 +143,7 @@ CONFIG_INVALID="{
{
\"subnet\": \"10.0.0.0/8\",
\"pool\": [ { \"pool\": \"10.0.0.10-10.0.0.100\" } ]
} ]
},
\"Logging\":
{
} ],
\"loggers\": [
{
\"name\": \"kea-dhcp4\",
......
......@@ -465,31 +465,6 @@ TEST_F(JSONFileBackendTest, jsonFile) {
EXPECT_EQ(Lease::TYPE_V4, pools3.at(0)->getType());
}
// This test verifies that the configurations for various servers
// can coexist and that the DHCPv4 configuration parsers will simply
// ignore them.
TEST_F(JSONFileBackendTest, serverConfigurationsCoexistence) {
std::string config = "{ \"Dhcp4\": {"
"\"rebind-timer\": 2000, "
"\"renew-timer\": 1000, \n"
"\"valid-lifetime\": 4000 }, "
"\"Dhcp6\": { },"
"\"DhcpDdns\": { },"
"\"Control-agent\": { }"
"}";
writeFile(TEST_FILE, config);
// Now initialize the server
boost::scoped_ptr<ControlledDhcpv4Srv> srv;
ASSERT_NO_THROW(
srv.reset(new ControlledDhcpv4Srv(0))
);
// And configure it using the config file.
EXPECT_NO_THROW(srv->init(TEST_FILE));
}
// This test checks if configuration can be read from a JSON file
// using hash (#) line comments
TEST_F(JSONFileBackendTest, hashComments) {
......
......@@ -508,26 +508,28 @@ TEST(ParserTest, errors) {
"expecting }");
testError("{ 123 }\n",
Parser4Context::PARSER_DHCP4,
"<string>:1.3-5: syntax error, unexpected integer");
"<string>:1.3-5: syntax error, unexpected integer, "
"expecting Dhcp4");
testError("{ \"foo\" }\n",
Parser4Context::PARSER_JSON,
"<string>:1.9: syntax error, unexpected }, "
"expecting :");
testError("{ \"foo\" }\n",
Parser4Context::PARSER_DHCP4,
"<string>:1.9: syntax error, unexpected }, expecting :");
"<string>:1.3-7: syntax error, unexpected constant string, "
"expecting Dhcp4");
testError("{ \"foo\":null }\n",
Parser4Context::PARSER_DHCP4,
"<string>:1.3-7: got unexpected keyword "
"\"foo\" in toplevel map.");
"<string>:1.3-7: syntax error, unexpected constant string, "
"expecting Dhcp4");
testError("{ \"Logging\":null }\n",
Parser4Context::PARSER_DHCP4,
"<string>:1.3-11: syntax error, unexpected constant string, "
"expecting Dhcp4");
testError("{ \"Dhcp4\" }\n",
Parser4Context::PARSER_DHCP4,
"<string>:1.11: syntax error, unexpected }, "
"expecting :");
testError("{ \"Dhcp6\":[]\n",
Parser4Context::PARSER_DHCP4,
"<string>:2.1: syntax error, unexpected end of file, "
"expecting \",\" or }");
testError("{}{}\n",
Parser4Context::PARSER_JSON,
"<string>:1.3: syntax error, unexpected {, "
......@@ -606,6 +608,15 @@ TEST(ParserTest, errors) {
Parser4Context::PARSER_DHCP4,
"<string>:3.3-11: duplicate user-context/comment entries "
"(previous at <string>:2:19)");
// duplicate Dhcp4 entries
testError("{ \"Dhcp4\":{\n"
" \"comment\": \"first\" },\n"
" \"Dhcp4\":{\n"
" \"comment\": \"second\" }}\n",
Parser4Context::PARSER_DHCP4,
"<string>:3:3: duplicate Dhcp4 entries in toplevel map "
"(previous at <string>:1:3)");
}
// Check unicode escapes
......@@ -649,6 +660,6 @@ TEST(ParserTest, unicodeSlash) {
EXPECT_EQ("////", result->stringValue());
}
};
};
};
}
}
}
......@@ -358,6 +358,25 @@ ControlledDhcpv6Srv::commandConfigSetHandler(const string&,
}
}
// Check unsupported objects.
if (message.empty()) {
for (auto obj : args->mapValue()) {
const string& obj_name = obj.first;
if (obj_name != "Dhcp6") {
LOG_ERROR(dhcp6_logger, DHCP6_CONFIG_UNSUPPORTED_OBJECT)