case isc::dhcp::Parser4Context::INTERFACES_CONFIG:
case isc::dhcp::Parser4Context::LEASE_DATABASE:
case isc::dhcp::Parser4Context::HOSTS_DATABASE:
- case isc::dhcp::Parser4Context::HOOKS_LIBRARIES:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::POOLS:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
case isc::dhcp::Parser4Context::INTERFACES_CONFIG:
case isc::dhcp::Parser4Context::LEASE_DATABASE:
case isc::dhcp::Parser4Context::HOSTS_DATABASE:
- case isc::dhcp::Parser4Context::HOOKS_LIBRARIES:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::POOLS:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
hooks_params: hooks_param
| hooks_params COMMA hooks_param
+ | unknown_map_entry
;
hooks_param: library
| parameters
- | user_context
- | comment
- | unknown_map_entry
;
library: LIBRARY {
case isc::dhcp::Parser6Context::INTERFACES_CONFIG:
case isc::dhcp::Parser6Context::LEASE_DATABASE:
case isc::dhcp::Parser6Context::HOSTS_DATABASE:
- case isc::dhcp::Parser6Context::HOOKS_LIBRARIES:
case isc::dhcp::Parser6Context::SUBNET6:
case isc::dhcp::Parser6Context::SHARED_NETWORK:
case isc::dhcp::Parser6Context::OPTION_DEF:
case isc::dhcp::Parser6Context::INTERFACES_CONFIG:
case isc::dhcp::Parser6Context::LEASE_DATABASE:
case isc::dhcp::Parser6Context::HOSTS_DATABASE:
- case isc::dhcp::Parser6Context::HOOKS_LIBRARIES:
case isc::dhcp::Parser6Context::SUBNET6:
case isc::dhcp::Parser6Context::SHARED_NETWORK:
case isc::dhcp::Parser6Context::OPTION_DEF:
hooks_params: hooks_param
| hooks_params COMMA hooks_param
+ | unknown_map_entry
;
hooks_param: library
| parameters
- | user_context
- | comment
- | unknown_map_entry
;
library: LIBRARY {
return (s.str());
}
-ElementPtr
-CfgDbAccess::toElementDbAccessString(const std::string& dbaccess) {
- ElementPtr result = Element::createMap();
+void
+CfgDbAccess::toElementDbAccessString(const std::string& dbaccess,
+ ElementPtr map) {
// Code from DatabaseConnection::parse
if (dbaccess.empty()) {
- return (result);
+ return;
}
std::vector<std::string> tokens;
boost::split(tokens, dbaccess, boost::is_any_of(std::string("\t ")));
int64_t int_value;
try {
int_value = boost::lexical_cast<int64_t>(value);
- result->set(keyword, Element::create(int_value));
+ map->set(keyword, Element::create(int_value));
} catch (...) {
isc_throw(ToElementError, "invalid DB access "
<< "integer parameter: "
} else if ((keyword == "persist") ||
(keyword == "readonly")) {
if (value == "true") {
- result->set(keyword, Element::create(true));
+ map->set(keyword, Element::create(true));
} else if (value == "false") {
- result->set(keyword, Element::create(false));
+ map->set(keyword, Element::create(false));
} else {
isc_throw(ToElementError, "invalid DB access "
<< "boolean parameter: "
(keyword == "name") ||
(keyword == "contact_points") ||
(keyword == "keyspace")) {
- result->set(keyword, Element::create(value));
+ map->set(keyword, Element::create(value));
} else {
isc_throw(ToElementError, "unknown DB access parameter: "
<< keyword << "=" << value);
<< ", expected format is name=value");
}
}
+}
+
+ElementPtr
+CfgLeaseDbAccess::toElement() const {
+ ElementPtr result = Element::createMap();
+ // Add user context
+ contextToElement(result);
+ CfgDbAccess::toElementDbAccessString(lease_db_access_, result);
+ return (result);
+}
+
+ElementPtr
+CfgHostDbAccess::toElement() const {
+ ElementPtr result = Element::createMap();
+ // Add user context
+ contextToElement(result);
+ CfgDbAccess::toElementDbAccessString(host_db_access_, result);
return (result);
}
#define CFG_DBACCESS_H
#include <cc/cfg_to_element.h>
+#include <cc/user_context.h>
#include <boost/shared_ptr.hpp>
#include <string>
///
/// The database access strings use the same format as the strings
/// passed to the @ref isc::dhcp::LeaseMgrFactory::create function.
-class CfgDbAccess {
+class CfgDbAccess : public UserContext {
public:
/// @brief Constructor.
/// @brief Unparse an access string
///
/// @param dbaccess the database access string
- /// @return a pointer to configuration
- static
- isc::data::ElementPtr toElementDbAccessString(const std::string& dbaccess);
+ /// @param map the element map where the access string is unparse
+ static void
+ toElementDbAccessString(const std::string& dbaccess,
+ isc::data::ElementPtr map);
protected:
/// @ref isc::data::CfgToElement::toElement
///
/// @result a pointer to a configuration
- virtual isc::data::ElementPtr toElement() const {
- return (CfgDbAccess::toElementDbAccessString(lease_db_access_));
- }
+ virtual isc::data::ElementPtr toElement() const;
};
struct CfgHostDbAccess : public CfgDbAccess, public isc::data::CfgToElement {
/// @ref isc::data::CfgToElement::toElement
///
/// @result a pointer to a configuration
- virtual isc::data::ElementPtr toElement() const {
- return (CfgDbAccess::toElementDbAccessString(host_db_access_));
- }
+ virtual isc::data::ElementPtr toElement() const;
};
}
}
-
#endif // CFG_DBACCESS_H
ElementPtr
CfgDUID::toElement() const {
ElementPtr result = Element::createMap();
+ // Set user context
+ contextToElement(result);
// The type item is required
std::string duid_type = "LLT";
switch (type_) {
#include <dhcp/duid.h>
#include <cc/cfg_to_element.h>
+#include <cc/user_context.h>
#include <boost/shared_ptr.hpp>
#include <stdint.h>
#include <vector>
/// generate. It also allows for overriding entire default DUID or parts of
/// it via configuration file. This class holds the DUID configuration
/// specified in the server configuration file.
-class CfgDUID : public isc::data::CfgToElement {
+class CfgDUID : public UserContext, public isc::data::CfgToElement {
public:
/// @brief Constructor.
CfgIface::toElement() const {
ElementPtr result = Element::createMap();
+ // Set user context
+ contextToElement(result);
+
// Set interfaces
ElementPtr ifaces = Element::createList();
if (wildcard_used_) {
#include <asiolink/io_address.h>
#include <dhcp/iface_mgr.h>
#include <cc/cfg_to_element.h>
+#include <cc/user_context.h>
#include <boost/shared_ptr.hpp>
#include <map>
#include <set>
/// to which it is bound. It is allowed to select multiple addresses on the
/// particular interface explicitly, e.g. "eth0/192.168.8.1",
/// "eth0/192.168.8.2".
-class CfgIface : public isc::data::CfgToElement {
+class CfgIface : public UserContext, public isc::data::CfgToElement {
public:
/// @brief Socket type used by the DHCPv4 server.
ElementPtr
D2ClientConfig::toElement() const {
ElementPtr result = Element::createMap();
+ // Set user context
+ contextToElement(result);
// Set enable-updates
result->set("enable-updates", Element::create(enable_updates_));
// Set qualifying-suffix
#include <asiolink/io_address.h>
#include <cc/cfg_to_element.h>
+#include <cc/user_context.h>
#include <dhcp_ddns/ncr_io.h>
#include <exceptions/exceptions.h>
/// parameters associated with DHCP-DDNS and acting as a client of D2.
/// Instances of this class may be constructed through configuration parsing.
///
-class D2ClientConfig : public isc::data::CfgToElement {
+class D2ClientConfig : public UserContext, public isc::data::CfgToElement {
public:
/// @brief Default configuration constants.
/// @todo For now these are hard-coded as configuration layer cannot
// Prepare the map
ElementPtr map = Element::createMap();
+ // Set the user context
+ contextToElement(map);
// Set the identifier
Host::IdentifierType id_type = getIdentifierType();
if (id_type == Host::IDENT_HWADDR) {
#include <asiolink/io_address.h>
#include <cc/data.h>
+#include <cc/user_context.h>
#include <dhcp/classify.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
/// - disable IPv4 reservation without a need to set it to the 0.0.0.0 address
/// Note that the last three operations are mainly required for managing
/// host reservations which will be implemented later.
-class Host {
+class Host : public UserContext {
public:
/// @brief Type of the host identifier.
-// Copyright (C) 2014-2015 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
// Remove default destinations as we are going to replace them.
info.clearDestinations();
+ // Get user context
+ isc::data::ConstElementPtr user_context = entry->get("user-context");
+ if (user_context) {
+ info.setContext(user_context);
+ }
+
// Get a name
isc::data::ConstElementPtr name_ptr = entry->get("name");
if (!name_ptr) {
ElementPtr
LoggingInfo::toElement() const {
ElementPtr result = Element::createMap();
+ // Set user context
+ contextToElement(result);
// Set name
result->set("name", Element::create(name_));
// Set output_options if not empty
#include <log/logger_level.h>
#include <log/logger_specification.h>
#include <cc/cfg_to_element.h>
+#include <cc/user_context.h>
#include <stdint.h>
#include <vector>
///
/// This structure is used to keep log4cplus configuration parameters.
struct LoggingDestination : public isc::data::CfgToElement {
+public:
/// @brief defines logging destination output
///
/// "severity": "WARN",
/// "debuglevel": 99
/// },
-struct LoggingInfo : public isc::data::CfgToElement {
+class LoggingInfo : public UserContext, public isc::data::CfgToElement {
+public:
/// @brief logging name
std::string name_;
int64_t lfc_interval = 0;
int64_t timeout = 0;
int64_t port = 0;
+ ConstElementPtr user_context;
// 2. Update the copy with the passed keywords.
BOOST_FOREACH(ConfigPair param, database_config->mapValue()) {
try {
values_copy[param.first] =
boost::lexical_cast<std::string>(port);
+ } else if (param.first == "user-context") {
+ user_context = param.second;
+
} else {
values_copy[param.first] = param.second->stringValue();
}
cfg_db->setHostDbAccessString(getDbAccessString());
}
+ if (user_context) {
+ cfg_db->setContext(user_context);
+ }
}
// Create the database access string
<< client_config->getPosition() << ")");
}
+ // Add user context
+ ConstElementPtr user_context = client_config->get("user-context");
+ if (user_context) {
+ new_config->setContext(user_context);
+ }
+
return(new_config);
}
if (duid_configuration->contains(param)) {
cfg->setPersist(getBoolean(duid_configuration, param));
}
+
+ param = "user-context";
+ ConstElementPtr user_context = duid_configuration->get("user-context");
+ if (user_context) {
+ cfg->setContext(user_context);
+ }
} catch (const DhcpConfigError&) {
throw;
} catch (const std::exception& ex) {
}
}
+ if (element.first == "user-context") {
+ cfg->setContext(element.second);
+ continue;
+ }
+
// This should never happen as the input produced by the parser
// see (src/bin/dhcpX/dhcpX_parser.yy) should not produce any
// other parameter, so this case is only to catch bugs in
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::test;
+using namespace isc::data;
namespace {
// Additional parameters are not in lease_db_access_
runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg));
+ // Add and check user context
+ std::string user_context = "{ \"foo\": \"bar\" }";
+ cfg.setContext(Element::fromJSON(user_context));
+ expected = "{ \"user-context\": { \"foo\": \"bar\" }, \"type\": \"mysql\" }";
+ runToElementTest<CfgLeaseDbAccess>(expected, CfgLeaseDbAccess(cfg));
+
// If access string is empty, no parameters will be appended.
ASSERT_NO_THROW(cfg.setLeaseDbAccessString(""));
EXPECT_TRUE(cfg.getLeaseDbAccessString().empty());
// Additional parameters are not in host_db_access_
runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg));
+ // Add and check user context
+ std::string user_context = "{ \"foo\": \"bar\" }";
+ cfg.setContext(Element::fromJSON(user_context));
+ expected = "{ \"user-context\": { \"foo\": \"bar\" }, \"type\": \"mysql\" }";
+ runToElementTest<CfgHostDbAccess>(expected, CfgHostDbAccess(cfg));
+
// If access string is empty, no parameters will be appended.
ASSERT_NO_THROW(cfg.setHostDbAccessString(""));
EXPECT_TRUE(cfg.getHostDbAccessString().empty());
using namespace isc;
using namespace isc::dhcp;
using namespace isc::test;
+using namespace isc::data;
namespace {
EXPECT_EQ(0, cfg_duid.getTime());
EXPECT_EQ(0, cfg_duid.getEnterpriseId());
EXPECT_TRUE(cfg_duid.persist());
+ EXPECT_FALSE(cfg_duid.getContext());
std::string expected = "{ \"type\": \"LLT\",\n"
"\"identifier\": \"\",\n"
ASSERT_NO_THROW(cfg_duid.setTime(32100));
ASSERT_NO_THROW(cfg_duid.setEnterpriseId(10));
ASSERT_NO_THROW(cfg_duid.setPersist(false));
+ std::string user_context = "{ \"comment\": \"foo\" }";
+ ASSERT_NO_THROW(cfg_duid.setContext(Element::fromJSON(user_context)));
// Check that values have been set correctly.
EXPECT_EQ(DUID::DUID_EN, cfg_duid.getType());
EXPECT_EQ(32100, cfg_duid.getTime());
EXPECT_EQ(10, cfg_duid.getEnterpriseId());
EXPECT_FALSE(cfg_duid.persist());
+ ASSERT_TRUE(cfg_duid.getContext());
+ EXPECT_EQ(user_context, cfg_duid.getContext()->str());
- std::string expected = "{ \"type\": \"EN\",\n"
+ std::string expected = "{\n"
+ " \"comment\": \"foo\",\n"
+ " \"type\": \"EN\",\n"
" \"identifier\": \"ABCDEF\",\n"
" \"htype\": 100,\n"
" \"time\": 32100,\n"
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::test;
+using namespace isc::data;
namespace {
EXPECT_NO_THROW(cfg4.use(AF_INET, "*"));
EXPECT_NO_THROW(cfg4.use(AF_INET, "eth0"));
EXPECT_NO_THROW(cfg4.use(AF_INET, "eth1/192.0.2.3"));
+ std::string comment = "{ \"comment\": \"foo\" }";
+ EXPECT_NO_THROW(cfg4.setContext(Element::fromJSON(comment)));
// Check unparse
std::string expected =
- "{ \"interfaces\": [ \"*\", \"eth0\", \"eth1/192.0.2.3\" ], "
+ "{ \"comment\": \"foo\", "
+ "\"interfaces\": [ \"*\", \"eth0\", \"eth1/192.0.2.3\" ], "
"\"re-detect\": false }";
runToElementTest<CfgIface>(expected, cfg4);
EXPECT_NO_THROW(cfg6.use(AF_INET6, "*"));
EXPECT_NO_THROW(cfg6.use(AF_INET6, "eth1"));
EXPECT_NO_THROW(cfg6.use(AF_INET6, "eth0/2001:db8:1::1"));
+ comment = "{ \"comment\": \"bar\" }";
+ EXPECT_NO_THROW(cfg6.setContext(Element::fromJSON(comment)));
expected =
- "{ \"interfaces\": [ \"*\", \"eth1\", \"eth0/2001:db8:1::1\" ], "
+ "{ \"comment\": \"bar\", "
+ "\"interfaces\": [ \"*\", \"eth1\", \"eth0/2001:db8:1::1\" ], "
"\"re-detect\": false }";
runToElementTest<CfgIface>(expected, cfg6);
}
using namespace isc::dhcp;
using namespace isc::util;
using namespace isc::test;
+using namespace isc::data;
using namespace isc;
namespace {
ASSERT_TRUE(d2_client_config);
+ // Add user context
+ std::string user_context = "{ \"comment\": \"foo\" }";
+ EXPECT_FALSE(d2_client_config->getContext());
+ d2_client_config->setContext(Element::fromJSON(user_context));
+
// Verify that the accessors return the expected values.
EXPECT_EQ(d2_client_config->getEnableUpdates(), enable_updates);
EXPECT_EQ(d2_client_config->getGeneratedPrefix(), generated_prefix);
EXPECT_EQ(d2_client_config->getQualifyingSuffix(), qualifying_suffix);
+ ASSERT_TRUE(d2_client_config->getContext());
+ EXPECT_EQ(d2_client_config->getContext()->str(), user_context);
+
// Verify that toText called by << operator doesn't bomb.
ASSERT_NO_THROW(std::cout << "toText test:" << std::endl <<
*d2_client_config << std::endl);
// Verify what toElement returns.
std::string expected = "{\n"
+ "\"comment\": \"foo\",\n"
"\"enable-updates\": true,\n"
"\"server-ip\": \"127.0.0.1\",\n"
"\"server-port\": 477,\n"
" \"override-client-update\" : true, "
" \"replace-client-name\" : \"when-present\", "
" \"generated-prefix\" : \"test.prefix\", "
- " \"qualifying-suffix\" : \"test.suffix.\" "
+ " \"qualifying-suffix\" : \"test.suffix.\", "
+ " \"user-context\": { \"foo\": \"bar\" } "
" }"
"}";
EXPECT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, d2_client_config->getReplaceClientNameMode());
EXPECT_EQ("test.prefix", d2_client_config->getGeneratedPrefix());
EXPECT_EQ("test.suffix.", d2_client_config->getQualifyingSuffix());
+ ASSERT_TRUE(d2_client_config->getContext());
+ EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
// Verify that the configuration object unparses.
ConstElementPtr expected;
" \"override-client-update\" : false, "
" \"replace-client-name\" : \"never\", "
" \"generated-prefix\" : \"\", "
- " \"qualifying-suffix\" : \"\" "
+ " \"qualifying-suffix\" : \"\", "
+ " \"user-context\": { \"foo\": \"bar\" } "
" }"
"}";
EXPECT_EQ(D2ClientConfig::RCM_NEVER, d2_client_config->getReplaceClientNameMode());
EXPECT_EQ("", d2_client_config->getGeneratedPrefix());
EXPECT_EQ("", d2_client_config->getQualifyingSuffix());
+ ASSERT_TRUE(d2_client_config->getContext());
+ EXPECT_EQ("{ \"foo\": \"bar\" }", d2_client_config->getContext()->str());
ASSERT_NO_THROW(expected = Element::fromJSON(config_str2)->get("dhcp-ddns"));
ASSERT_TRUE(expected);
// Configuration with one interface.
std::string config =
- "{ \"interfaces\": [ \"eth0\" ], "
+ "{ \"user-context\": { \"foo\": \"bar\" }, "
+ " \"interfaces\": [ \"eth0\" ], "
" \"dhcp-socket-type\": \"udp\","
" \"outbound-interface\": \"use-routing\", "
" \"re-detect\": false }";
using namespace isc::dhcp;
using namespace isc::test;
+using namespace isc::data;
namespace {
ASSERT_EQ(1, info_non_verbose.destinations_.size());
EXPECT_EQ("stdout", info_non_verbose.destinations_[0].output_);
- std::string header = "{\n"
+ std::string header = "{\n";
+ std::string begin =
"\"name\": \"kea\",\n"
"\"output_options\": [ {\n"
" \"output\": \"stdout\",\n \"maxsize\": 10240000,\n"
"\"severity\": \"";
std::string dbglvl = "\",\n\"debuglevel\": ";
std::string trailer = "\n}\n";
- std::string expected = header + "INFO" + dbglvl + "0" + trailer;
+ std::string expected = header + begin + "INFO" + dbglvl + "0" + trailer;
+ runToElementTest<LoggingInfo>(expected, info_non_verbose);
+
+ // Add a user context
+ std::string comment = "\"comment\": \"foo\"";
+ std::string user_context = "{ " + comment + " }";
+ EXPECT_FALSE(info_non_verbose.getContext());
+ info_non_verbose.setContext(Element::fromJSON(user_context));
+ ASSERT_TRUE(info_non_verbose.getContext());
+ EXPECT_EQ(user_context, info_non_verbose.getContext()->str());
+ expected = header + comment + ",\n" + begin + "INFO" + dbglvl + "0" + trailer;
runToElementTest<LoggingInfo>(expected, info_non_verbose);
CfgMgr::instance().setVerbose(true);
EXPECT_EQ(10240000, info_verbose.destinations_[0].maxsize_);
EXPECT_EQ(1, info_verbose.destinations_[0].maxver_);
- expected = header + "DEBUG" + dbglvl + "99" + trailer;
+ expected = header + begin + "DEBUG" + dbglvl + "99" + trailer;
+ runToElementTest<LoggingInfo>(expected, info_verbose);
+
+ // User comment again
+ EXPECT_FALSE(info_verbose.getContext());
+ info_verbose.setContext(Element::fromJSON(user_context));
+ ASSERT_TRUE(info_verbose.getContext());
+ EXPECT_EQ(user_context, info_verbose.getContext()->str());
+ expected = header + comment + ",\n" + begin + "DEBUG" + dbglvl + "99" + trailer;
runToElementTest<LoggingInfo>(expected, info_verbose);
}
if (user_context->size() > 0) {
result->set("user-context", user_context);
}
- result->combine_set("comment", comment);
+ result->combine_set("comment", copy(comment, 0));
}
return (result);