Commit a07ea817 authored by Francis Dupont's avatar Francis Dupont
Browse files

[#916] Checkpoint before regen

parent 6ca88096
...@@ -91,11 +91,11 @@ directory. ...@@ -91,11 +91,11 @@ directory.
"loggers" object) moved inside the configuration objects (maps) for the "loggers" object) moved inside the configuration objects (maps) for the
respective Kea modules. For example: the "Dhcp4" map contains the respective Kea modules. For example: the "Dhcp4" map contains the
"loggers" object specifying logging configuration for the DHCPv4 "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 release; it will be possible to specify the "Logging" object at the top
configuration level and "loggers" objects at the module configuration configuration level and "loggers" objects at the module configuration
level. Ultimately, support for the top-level "Logging" object will be level. Finally, support for the top-level "Logging" object was
removed. removed in Kea 1.7.0.
The specification of several supported elements (e.g. "Dhcp4", The specification of several supported elements (e.g. "Dhcp4",
"Dhcp6") in a single configuration file can be confusing and works "Dhcp6") in a single configuration file can be confusing and works
......
...@@ -148,12 +148,9 @@ above this object is called ``Dhcp4``. ...@@ -148,12 +148,9 @@ above this object is called ``Dhcp4``.
In the current Kea release it is possible to specify configurations In the current Kea release it is possible to specify configurations
of multiple modules within a single configuration file, but this is of multiple modules within a single configuration file, but this is
not recommended and support for it will be removed in a future not recommended and support for it was removed in 1.7.9 release,
release. The only object, besides the one specifying module including the ``Logging`` object: its previous content, the list
configuration, which can be (and usually was) included in the same file of loggers, must now inside be the ``Dhcp4`` object.
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.
The Dhcp4 configuration starts with the ``"Dhcp4": {`` line and ends The Dhcp4 configuration starts with the ``"Dhcp4": {`` line and ends
with the corresponding closing brace (in the above example, the brace with the corresponding closing brace (in the above example, the brace
......
...@@ -149,12 +149,9 @@ above this object is called ``Dhcp6``. ...@@ -149,12 +149,9 @@ above this object is called ``Dhcp6``.
In the current Kea release it is possible to specify configurations In the current Kea release it is possible to specify configurations
of multiple modules within a single configuration file, but this is of multiple modules within a single configuration file, but this is
not recommended and support for it will be removed in a future not recommended and support for it was removed in 1.7.9 release,
release. The only object, besides the one specifying module including the ``Logging`` object: its previous content, the list
configuration, which can be (and usually was) included in the same file of loggers, must now inside be the ``Dhcp6`` object.
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.
The Dhcp6 configuration starts with the ``"Dhcp6": {`` line and ends The Dhcp6 configuration starts with the ``"Dhcp6": {`` line and ends
with the corresponding closing brace (in the above example, the brace with the corresponding closing brace (in the above example, the brace
......
...@@ -17,8 +17,8 @@ useful when debugging a problem. ...@@ -17,8 +17,8 @@ useful when debugging a problem.
The logging system in Kea is configured through the loggers entry in the The logging system in Kea is configured through the loggers entry in the
server section of your configuration file. In previous Kea releases this server section of your configuration file. In previous Kea releases this
entry was in an independent Logging section; this is still supported for entry was in an independent Logging section; this was still supported
backward compatibility. for backward compatibility until Kea 1.7.8 included.
Loggers Loggers
------- -------
......
...@@ -343,7 +343,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&, ...@@ -343,7 +343,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
// Command arguments are expected to be: // Command arguments are expected to be:
// { "Dhcp4": { ... } } // { "Dhcp4": { ... } }
// The Logging component is supported by backward compatiblity.
if (!args) { if (!args) {
message = "Missing mandatory 'arguments' parameter."; message = "Missing mandatory 'arguments' parameter.";
} else { } else {
...@@ -355,6 +354,25 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&, ...@@ -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()) { if (!message.empty()) {
// Something is amiss with arguments, return a failure response. // Something is amiss with arguments, return a failure response.
ConstElementPtr result = isc::config::createAnswer(status_code, ConstElementPtr result = isc::config::createAnswer(status_code,
...@@ -375,20 +393,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&, ...@@ -375,20 +393,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
// configuration attempts. // configuration attempts.
CfgMgr::instance().rollback(); 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. // Parse the logger configuration explicitly into the staging config.
// Note this does not alter the current loggers, they remain in // Note this does not alter the current loggers, they remain in
// effect until we apply the logging config below. If no logging // effect until we apply the logging config below. If no logging
...@@ -399,15 +403,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&, ...@@ -399,15 +403,6 @@ ControlledDhcpv4Srv::commandConfigSetHandler(const string&,
// out what exactly is wrong with the new config in case of problems. // out what exactly is wrong with the new config in case of problems.
CfgMgr::instance().getStagingCfg()->applyLoggingCfg(); 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. // Now we configure the server proper.
ConstElementPtr result = processConfig(dhcp4); ConstElementPtr result = processConfig(dhcp4);
...@@ -452,6 +447,25 @@ ControlledDhcpv4Srv::commandConfigTestHandler(const string&, ...@@ -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()) { if (!message.empty()) {
// Something is amiss with arguments, return a failure response. // Something is amiss with arguments, return a failure response.
ConstElementPtr result = isc::config::createAnswer(status_code, ConstElementPtr result = isc::config::createAnswer(status_code,
...@@ -459,13 +473,6 @@ ControlledDhcpv4Srv::commandConfigTestHandler(const string&, ...@@ -459,13 +473,6 @@ ControlledDhcpv4Srv::commandConfigTestHandler(const string&,
return (result); 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) // stop thread pool (if running)
MultiThreadingCriticalSection cs; MultiThreadingCriticalSection cs;
......
...@@ -137,8 +137,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} ...@@ -137,8 +137,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
return isc::dhcp::Dhcp4Parser::make_SUB_DHCP_DDNS(driver.loc_); return isc::dhcp::Dhcp4Parser::make_SUB_DHCP_DDNS(driver.loc_);
case Parser4Context::PARSER_CONFIG_CONTROL: case Parser4Context::PARSER_CONFIG_CONTROL:
return isc::dhcp::Dhcp4Parser::make_SUB_CONFIG_CONTROL(driver.loc_); 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} ...@@ -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\" { \"loggers\" {
switch(driver.ctx_) { switch(driver.ctx_) {
case isc::dhcp::Parser4Context::DHCP4: case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::LOGGING:
return isc::dhcp::Dhcp4Parser::make_LOGGERS(driver.loc_); return isc::dhcp::Dhcp4Parser::make_LOGGERS(driver.loc_);
default: default:
return isc::dhcp::Dhcp4Parser::make_STRING("loggers", driver.loc_); return isc::dhcp::Dhcp4Parser::make_STRING("loggers", driver.loc_);
...@@ -1761,33 +1749,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence} ...@@ -1761,33 +1749,6 @@ ControlCharacterFill [^"\\]|\\{JSONEscapeSequence}
return isc::dhcp::Dhcp4Parser::make_STRING(tmp, driver.loc_); 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\" { \"4o6-interface\" {
switch(driver.ctx_) { switch(driver.ctx_) {
case isc::dhcp::Parser4Context::SUBNET4: case isc::dhcp::Parser4Context::SUBNET4:
......
...@@ -224,7 +224,6 @@ using namespace std; ...@@ -224,7 +224,6 @@ using namespace std;
HOSTNAME_CHAR_SET "hostname-char-set" HOSTNAME_CHAR_SET "hostname-char-set"
HOSTNAME_CHAR_REPLACEMENT "hostname-char-replacement" HOSTNAME_CHAR_REPLACEMENT "hostname-char-replacement"
LOGGING "Logging"
LOGGERS "loggers" LOGGERS "loggers"
OUTPUT_OPTIONS "output_options" OUTPUT_OPTIONS "output_options"
OUTPUT "output" OUTPUT "output"
...@@ -235,10 +234,6 @@ using namespace std; ...@@ -235,10 +234,6 @@ using namespace std;
MAXVER "maxver" MAXVER "maxver"
PATTERN "pattern" PATTERN "pattern"
DHCP6 "Dhcp6"
DHCPDDNS "DhcpDdns"
CONTROL_AGENT "Control-agent"
// Not real tokens, just a way to signal what the parser is expected to // Not real tokens, just a way to signal what the parser is expected to
// parse. // parse.
TOPLEVEL_JSON TOPLEVEL_JSON
...@@ -253,7 +248,6 @@ using namespace std; ...@@ -253,7 +248,6 @@ using namespace std;
SUB_OPTION_DATA SUB_OPTION_DATA
SUB_HOOKS_LIBRARY SUB_HOOKS_LIBRARY
SUB_DHCP_DDNS SUB_DHCP_DDNS
SUB_LOGGING
SUB_CONFIG_CONTROL SUB_CONFIG_CONTROL
; ;
...@@ -292,7 +286,6 @@ start: TOPLEVEL_JSON { ctx.ctx_ = ctx.NO_KEYWORD; } sub_json ...@@ -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_OPTION_DATA { ctx.ctx_ = ctx.OPTION_DATA; } sub_option_data
| SUB_HOOKS_LIBRARY { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } sub_hooks_library | SUB_HOOKS_LIBRARY { ctx.ctx_ = ctx.HOOKS_LIBRARIES; } sub_hooks_library
| SUB_DHCP_DDNS { ctx.ctx_ = ctx.DHCP_DDNS; } sub_dhcp_ddns | 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 | SUB_CONFIG_CONTROL { ctx.ctx_ = ctx.CONFIG_CONTROL; } sub_config_control
; ;
...@@ -400,8 +393,7 @@ unknown_map_entry: STRING COLON { ...@@ -400,8 +393,7 @@ unknown_map_entry: STRING COLON {
}; };
// This defines the top-level { } that holds Control-agent, Dhcp6, Dhcp4, // This defines the top-level { } that holds Dhcp4 only object.
// DhcpDdns or Logging objects.
syntax_map: LCURLY_BRACKET { syntax_map: LCURLY_BRACKET {
// This code is executed when we're about to start parsing // This code is executed when we're about to start parsing
// the content of the map // the content of the map
...@@ -416,24 +408,20 @@ syntax_map: LCURLY_BRACKET { ...@@ -416,24 +408,20 @@ syntax_map: LCURLY_BRACKET {
ctx.require("Dhcp4", ctx.loc2pos(@1), ctx.loc2pos(@4)); ctx.require("Dhcp4", ctx.loc2pos(@1), ctx.loc2pos(@4));
}; };
// This represents top-level entries: Control-agent, Dhcp6, Dhcp4, // This represents top-level entries: Dhcp4
// DhcpDdns, Logging
global_objects: global_object global_objects: global_object
| global_objects COMMA 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 global_object: dhcp4_object
| logging_object
| dhcp6_json_object
| dhcpddns_json_object
| control_agent_json_object
| unknown_map_entry
; ;
dhcp4_object: DHCP4 { dhcp4_object: DHCP4 {
// This code is executed when we're about to start parsing // This code is executed when we're about to start parsing
// the content of the map // the content of the map
// Prevent against duplicate.
ctx.unique("Dhcp4", ctx.loc2pos(@1));
ElementPtr m(new MapElement(ctx.loc2pos(@1))); ElementPtr m(new MapElement(ctx.loc2pos(@1)));
ctx.stack_.back()->set("Dhcp4", m); ctx.stack_.back()->set("Dhcp4", m);
ctx.stack_.push_back(m); ctx.stack_.push_back(m);
...@@ -2299,29 +2287,6 @@ dep_hostname_char_replacement: HOSTNAME_CHAR_REPLACEMENT { ...@@ -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 information element
config_control: CONFIG_CONTROL { config_control: CONFIG_CONTROL {
...@@ -2369,41 +2334,8 @@ config_fetch_wait_time: CONFIG_FETCH_WAIT_TIME COLON INTEGER { ...@@ -2369,41 +2334,8 @@ config_fetch_wait_time: CONFIG_FETCH_WAIT_TIME COLON INTEGER {
ctx.stack_.back()->set("config-fetch-wait-time", value); ctx.stack_.back()->set("config-fetch-wait-time", value);
}; };
// --- logging entry ----------------------------------------- // --- loggers 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", the only parameter currently defined in "Logging" object,
// is "loggers": [ ... ].
loggers: LOGGERS { loggers: LOGGERS {
ElementPtr l(new ListElement(ctx.loc2pos(@1))); ElementPtr l(new ListElement(ctx.loc2pos(@1)));
ctx.stack_.back()->set("loggers", l); ctx.stack_.back()->set("loggers", l);
...@@ -2420,7 +2352,7 @@ loggers_entries: logger_entry ...@@ -2420,7 +2352,7 @@ loggers_entries: logger_entry
| loggers_entries COMMA 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 { logger_entry: LCURLY_BRACKET {
ElementPtr l(new MapElement(ctx.loc2pos(@1))); ElementPtr l(new MapElement(ctx.loc2pos(@1)));
ctx.stack_.back()->add(l); ctx.stack_.back()->add(l);
......
...@@ -111,6 +111,18 @@ Parser4Context::require(const std::string& name, ...@@ -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 void
Parser4Context::enter(const ParserContext& ctx) Parser4Context::enter(const ParserContext& ctx)
{ {
...@@ -140,8 +152,6 @@ Parser4Context::contextName() ...@@ -140,8 +152,6 @@ Parser4Context::contextName()
return ("toplevel"); return ("toplevel");
case DHCP4: case DHCP4:
return ("Dhcp4"); return ("Dhcp4");
case LOGGING:
return ("Logging");
case INTERFACES_CONFIG: case INTERFACES_CONFIG:
return ("interfaces-config"); return ("interfaces-config");
case DHCP_SOCKET_TYPE: case DHCP_SOCKET_TYPE:
......
...@@ -90,9 +90,6 @@ public: ...@@ -90,9 +90,6 @@ public:
/// This will parse the input as config-control. /// This will parse the input as config-control.
PARSER_CONFIG_CONTROL, PARSER_CONFIG_CONTROL,
/// This will parse the content of Logging.
PARSER_LOGGING
} ParserType; } ParserType;
/// @brief Default constructor. /// @brief Default constructor.
...@@ -192,28 +189,34 @@ public: ...@@ -192,28 +189,34 @@ public:
/// ///
/// @param name name of the parameter to check /// @param name name of the parameter to check
/// @param open_loc location of the opening curly bracket /// @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 /// @throw Dhcp4ParseError
void require(const std::string& name, void require(const std::string& name,
isc::data::Element::Position open_loc, isc::data::Element::Position open_loc,
isc::data::Element::Position close_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 /// @brief Defines syntactic contexts for lexical tie-ins
typedef enum { typedef enum {
///< This one is used in pure JSON mode. ///< This one is used in pure JSON mode.
NO_KEYWORD, NO_KEYWORD,
///< Used while parsing top level (that contains Dhcp4, Logging and others) ///< Used while parsing top level (that contains Dhcp4)
CONFIG, CONFIG,
///< Used while parsing content of Dhcp4. ///< Used while parsing content of Dhcp4.
DHCP4, DHCP4,
// not yet Dhcp6, DhcpDdns,
///< Used while parsing content of Logging
LOGGING,
/// Used while parsing Dhcp4/interfaces structures. /// Used while parsing Dhcp4/interfaces structures.
INTERFACES_CONFIG, INTERFACES_CONFIG,
......
...@@ -768,15 +768,13 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) { ...@@ -768,15 +768,13 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configSet) {
string control_socket_footer = string control_socket_footer =
"\" \n} \n"; "\" \n} \n";
string logger_txt = string logger_txt =
" \"Logging\": { \n" " ,\"loggers\": [ { \n"
" \"loggers\": [ { \n"
" \"name\": \"kea\", \n" " \"name\": \"kea\", \n"
" \"severity\": \"FATAL\", \n" " \"severity\": \"FATAL\", \n"
" \"output_options\": [{ \n" " \"output_options\": [{ \n"
" \"output\": \"/dev/null\" \n" " <