return isc::dhcp::Dhcp4Parser::make_SUB_HOOKS_LIBRARY(driver.loc_);
case Parser4Context::PARSER_DHCP_DDNS:
return isc::dhcp::Dhcp4Parser::make_SUB_DHCP_DDNS(driver.loc_);
+ case Parser4Context::PARSER_LOGGING:
+ return isc::dhcp::Dhcp4Parser::make_SUB_LOGGING(driver.loc_);
}
}
%}
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::INTERFACES_CONFIG:
- case isc::dhcp::Parser4Context::LEASE_DATABASE:
- case isc::dhcp::Parser4Context::HOSTS_DATABASE:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::POOLS:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
switch(driver.ctx_) {
case isc::dhcp::Parser4Context::DHCP4:
case isc::dhcp::Parser4Context::INTERFACES_CONFIG:
- case isc::dhcp::Parser4Context::LEASE_DATABASE:
- case isc::dhcp::Parser4Context::HOSTS_DATABASE:
case isc::dhcp::Parser4Context::SUBNET4:
case isc::dhcp::Parser4Context::POOLS:
case isc::dhcp::Parser4Context::SHARED_NETWORK:
SUB_OPTION_DATA
SUB_HOOKS_LIBRARY
SUB_DHCP_DDNS
+ SUB_LOGGING
;
%token <std::string> STRING "constant string"
| 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
;
// ---- generic JSON parser ---------------------------------
| connect_timeout
| contact_points
| keyspace
- | user_context
- | comment
| unknown_map_entry
;
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.
PARSER_HOOKS_LIBRARY,
/// This will parse the input as dhcp-ddns.
- PARSER_DHCP_DDNS
+ PARSER_DHCP_DDNS,
+
+ /// This will parse the content of Logging.
+ PARSER_LOGGING
} ParserType;
/// @brief Default constructor.
#include <cc/command_interpreter.h>
#include <config/module_spec.h>
#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/json_config_parser.h>
#include <dhcp/option4_addrlst.h>
#include <dhcp/option_custom.h>
// Open port 0 means to not do anything at all. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
- srv_.reset(new Dhcpv4Srv(0));
+ srv_.reset(new ControlledDhcpv4Srv(0));
// Create fresh context.
resetConfiguration();
}
string config = "{\n"
"\"comment\": \"A DHCPv4 server\",\n"
+ "\"interfaces-config\": {\n"
+ " \"comment\": \"Use wildcard\",\n"
+ " \"interfaces\": [ \"*\" ] },\n"
"\"option-def\": [ {\n"
+ " \"comment\": \"An option definition\",\n"
" \"name\": \"foo\",\n"
" \"code\": 100,\n"
- " \"comment\": \"An option definition\",\n"
" \"type\": \"ipv4-address\",\n"
" \"space\": \"isc\"\n"
" } ],\n"
"\"option-data\": [ {\n"
- " \"name\": \"dhcp-message\",\n"
" \"comment\": \"Set option value\",\n"
+ " \"name\": \"dhcp-message\",\n"
" \"data\": \"ABCDEF0105\",\n"
" \"csv-format\": false\n"
" } ],\n"
"\"client-classes\": [\n"
" {\n"
- " \"name\": \"all\",\n"
" \"comment\": \"match all\",\n"
+ " \"name\": \"all\",\n"
" \"test\": \"'' == ''\"\n"
" },\n"
" {\n"
" \"name\": \"none\"\n"
" },\n"
" {\n"
- " \"name\": \"two\",\n"
" \"comment\": \"first comment\",\n"
- " \"comment\": \"second comment\"\n"
+ " \"comment\": \"second comment\",\n"
+ " \"name\": \"two\"\n"
" },\n"
" {\n"
- " \"name\": \"both\",\n"
" \"comment\": \"a comment\",\n"
+ " \"name\": \"both\",\n"
" \"user-context\": {\n"
" \"version\": 1\n"
" }\n"
" }\n"
" ],\n"
+ "\"control-socket\": {\n"
+ " \"comment\": \"REST API\",\n"
+ " \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n"
+ " \"user-context\": { \"comment\": \"Indirect comment\" }\n"
+ "},\n"
"\"shared-networks\": [ {\n"
- " \"name\": \"foo\"\n,"
" \"comment\": \"A shared network\"\n,"
+ " \"name\": \"foo\"\n,"
" \"subnet4\": [\n"
" { \n"
- " \"subnet\": \"192.0.1.0/24\",\n"
" \"comment\": \"A subnet\"\n,"
+ " \"subnet\": \"192.0.1.0/24\",\n"
+ " \"id\": 100,\n"
" \"pools\": [\n"
" {\n"
- " \"pool\": \"192.0.1.1-192.0.1.10\",\n"
- " \"comment\": \"A pool\"\n"
+ " \"comment\": \"A pool\",\n"
+ " \"pool\": \"192.0.1.1-192.0.1.10\"\n"
+ " }\n"
+ " ],\n"
+ " \"reservations\": [\n"
+ " {\n"
+ " \"comment\": \"A host reservation\",\n"
+ " \"hw-address\": \"AA:BB:CC:DD:EE:FF\",\n"
+ " \"hostname\": \"foo.example.com\",\n"
+ " \"option-data\": [ {\n"
+ " \"comment\": \"An option in a reservation\",\n"
+ " \"name\": \"domain-name\",\n"
+ " \"data\": \"example.com\"\n"
+ " } ]\n"
" }\n"
" ]\n"
" }\n"
" ]\n"
- " } ]\n"
- "} \n";
+ " } ],\n"
+ "\"dhcp-ddns\": {\n"
+ " \"comment\": \"No dynamic DNS\",\n"
+ " \"enable-updates\": false\n"
+ "}\n"
+ "}\n";
extractConfig(config);
configure(config, CONTROL_RESULT_SUCCESS, "");
ASSERT_TRUE(ctx->get("comment"));
EXPECT_EQ("\"A DHCPv4 server\"", ctx->get("comment")->str());
+ // There is a network interface configuration.
+ ConstCfgIfacePtr iface = CfgMgr::instance().getStagingCfg()->getCfgIface();
+ ASSERT_TRUE(iface);
+
+ // Check network interface configuration user context.
+ ConstElementPtr ctx_iface = iface->getContext();
+ ASSERT_TRUE(ctx_iface);
+ ASSERT_EQ(1, ctx_iface->size());
+ ASSERT_TRUE(ctx_iface->get("comment"));
+ EXPECT_EQ("\"Use wildcard\"", ctx_iface->get("comment")->str());
+
// There is a global option definition.
- OptionDefinitionPtr opt_def = LibDHCP::getRuntimeOptionDef("isc", 100);
+ const OptionDefinitionPtr& opt_def =
+ LibDHCP::getRuntimeOptionDef("isc", 100);
ASSERT_TRUE(opt_def);
EXPECT_EQ("foo", opt_def->getName());
EXPECT_EQ(100, opt_def->getCode());
EXPECT_EQ("\"An option definition\"", ctx_opt_def->get("comment")->str());
// There is an option descriptor aka option data.
- OptionDescriptor opt_desc =
+ const OptionDescriptor& opt_desc =
CfgMgr::instance().getStagingCfg()->getCfgOption()->
get(DHCP4_OPTION_SPACE, DHO_DHCP_MESSAGE);
ASSERT_TRUE(opt_desc.option_);
EXPECT_EQ("\"Set option value\"", ctx_opt_desc->get("comment")->str());
// And there are some client classes.
- ClientClassDictionaryPtr dict =
+ const ClientClassDictionaryPtr& dict =
CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ASSERT_TRUE(dict);
EXPECT_EQ(4, dict->getClasses()->size());
ASSERT_TRUE(ctx_class->get("version"));
EXPECT_EQ("1", ctx_class->get("version")->str());
+ // There is a control socket.
+ ConstElementPtr socket =
+ CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
+ ASSERT_TRUE(socket);
+ ASSERT_TRUE(socket->get("socket-type"));
+ EXPECT_EQ("\"unix\"", socket->get("socket-type")->str());
+ ASSERT_TRUE(socket->get("socket-name"));
+ EXPECT_EQ("\"/tmp/kea4-ctrl-socket\"", socket->get("socket-name")->str());
+
+ // Check control socket comment and user context.
+ ConstElementPtr ctx_socket = socket->get("comment");
+ ASSERT_TRUE(ctx_socket);
+ EXPECT_EQ("\"REST API\"", ctx_socket->str());
+ ctx_socket = socket->get("user-context");
+ ASSERT_EQ(1, ctx_socket->size());
+ ASSERT_TRUE(ctx_socket->get("comment"));
+ EXPECT_EQ("\"Indirect comment\"", ctx_socket->get("comment")->str());
+
// Now verify that the shared network was indeed configured.
- CfgSharedNetworks4Ptr cfg_net = CfgMgr::instance().getStagingCfg()
- ->getCfgSharedNetworks4();
+ const CfgSharedNetworks4Ptr& cfg_net =
+ CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks4();
ASSERT_TRUE(cfg_net);
const SharedNetwork4Collection* nets = cfg_net->getAll();
ASSERT_TRUE(nets);
ASSERT_EQ(1, nets->size());
SharedNetwork4Ptr net = nets->at(0);
ASSERT_TRUE(net);
+ EXPECT_EQ("foo", net->getName());
// Check shared network user context.
ConstElementPtr ctx_net = net->getContext();
EXPECT_EQ("\"A shared network\"", ctx_net->get("comment")->str());
// The shared network has a subnet.
- const Subnet4Collection * subs = net->getAllSubnets();
+ const Subnet4Collection* subs = net->getAllSubnets();
ASSERT_TRUE(subs);
ASSERT_EQ(1, subs->size());
Subnet4Ptr sub = subs->at(0);
ASSERT_TRUE(sub);
+ EXPECT_EQ(100, sub->getID());
+ EXPECT_EQ("192.0.1.0/24", sub->toText());
// Check subnet user context.
ConstElementPtr ctx_sub = sub->getContext();
ASSERT_EQ(1, ctx_pool->size());
ASSERT_TRUE(ctx_pool->get("comment"));
EXPECT_EQ("\"A pool\"", ctx_pool->get("comment")->str());
+
+ // The subnet has a host reservation.
+ uint8_t hw[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
+ HWAddrPtr hwaddr(new HWAddr(hw, sizeof(hw), HTYPE_ETHER));
+ ConstHostPtr host =
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->get4(100, hwaddr);
+ ASSERT_TRUE(host);
+ EXPECT_EQ(Host::IDENT_HWADDR, host->getIdentifierType());
+ EXPECT_EQ("aa:bb:cc:dd:ee:ff", host->getHWAddress()->toText(false));
+ EXPECT_FALSE(host->getDuid());
+ EXPECT_EQ(100, host->getIPv4SubnetID());
+ EXPECT_EQ(0, host->getIPv6SubnetID());
+ EXPECT_EQ("foo.example.com", host->getHostname());
+
+ // Check host user context.
+ ConstElementPtr ctx_host = host->getContext();
+ ASSERT_TRUE(ctx_host);
+ ASSERT_EQ(1, ctx_host->size());
+ ASSERT_TRUE(ctx_host->get("comment"));
+ EXPECT_EQ("\"A host reservation\"", ctx_host->get("comment")->str());
+
+ // The host reservation has an option data.
+ ConstCfgOptionPtr opts = host->getCfgOption4();
+ ASSERT_TRUE(opts);
+ EXPECT_FALSE(opts->empty());
+ const OptionDescriptor& host_desc =
+ opts->get(DHCP4_OPTION_SPACE, DHO_DOMAIN_NAME);
+ ASSERT_TRUE(host_desc.option_);
+ EXPECT_EQ(DHO_DOMAIN_NAME, host_desc.option_->getType());
+
+ // Check embedded option data user context.
+ ConstElementPtr ctx_host_desc = host_desc.getContext();
+ ASSERT_TRUE(ctx_host_desc);
+ ASSERT_EQ(1, ctx_host_desc->size());
+ ASSERT_TRUE(ctx_host_desc->get("comment"));
+ EXPECT_EQ("\"An option in a reservation\"",
+ ctx_host_desc->get("comment")->str());
+
+ // Finally dynamic DNS update configuration.
+ const D2ClientConfigPtr& d2 =
+ CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
+ ASSERT_TRUE(d2);
+ EXPECT_FALSE(d2->getEnableUpdates());
+
+ // Check dynamic DNS update configuration user context.
+ ConstElementPtr ctx_d2 = d2->getContext();
+ ASSERT_TRUE(ctx_d2);
+ ASSERT_EQ(1, ctx_d2->size());
+ ASSERT_TRUE(ctx_d2->get("comment"));
+ EXPECT_EQ("\"No dynamic DNS\"", ctx_d2->get("comment")->str());
+
+#if 0
+ // Loggers section supports comments too.
+
+ string logging = "{\n"
+ "\"loggers\": [ {\n"
+ " \"comment\": \"A logger\",\n"
+ " \"name\": \"kea-dhcp4\"\n"
+ "} ]\n";
+#endif
}
}
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp4/tests/get_config_unittest.h>
#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/json_config_parser.h>
#include <dhcpsrv/parsers/simple_parser4.h>
" }\n"
" }\n"
" ],\n"
+" \"control-socket\": {\n"
+" \"comment\": \"REST API\",\n"
+" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n"
+" \"socket-type\": \"unix\",\n"
+" \"user-context\": {\n"
+" \"comment\": \"Indirect comment\"\n"
+" }\n"
+" },\n"
+" \"dhcp-ddns\": {\n"
+" \"comment\": \"No dynamic DNS\",\n"
+" \"enable-updates\": false\n"
+" },\n"
+" \"interfaces-config\": {\n"
+" \"comment\": \"Use wildcard\",\n"
+" \"interfaces\": [ \"*\" ],\n"
+" \"re-detect\": false\n"
+" },\n"
" \"option-data\": [\n"
" {\n"
" \"comment\": \"Set option value\",\n"
" \"subnet4\": [\n"
" {\n"
" \"comment\": \"A subnet\",\n"
+" \"id\": 100,\n"
" \"pools\": [\n"
" {\n"
" \"comment\": \"A pool\",\n"
" \"pool\": \"192.0.1.1-192.0.1.10\"\n"
" }\n"
" ],\n"
+" \"reservations\": [\n"
+" {\n"
+" \"comment\": \"A host reservation\",\n"
+" \"hostname\": \"foo.example.com\",\n"
+" \"hw-address\": \"AA:BB:CC:DD:EE:FF\",\n"
+" \"option-data\": [\n"
+" {\n"
+" \"comment\": \"An option in a reservation\",\n"
+" \"data\": \"example.com\",\n"
+" \"name\": \"domain-name\"\n"
+" }\n"
+" ]\n"
+" }\n"
+" ],\n"
" \"subnet\": \"192.0.1.0/24\"\n"
" }\n"
" ]\n"
" \"server-hostname\": \"\"\n"
" }\n"
" ],\n"
+" \"control-socket\": {\n"
+" \"comment\": \"REST API\",\n"
+" \"socket-name\": \"/tmp/kea4-ctrl-socket\",\n"
+" \"socket-type\": \"unix\",\n"
+" \"user-context\": {\n"
+" \"comment\": \"Indirect comment\"\n"
+" }\n"
+" },\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
+" \"comment\": \"No dynamic DNS\",\n"
" \"always-include-fqdn\": false,\n"
" \"enable-updates\": false,\n"
" \"generated-prefix\": \"myhost\",\n"
" \"hooks-libraries\": [ ],\n"
" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\", \"circuit-id\", \"client-id\" ],\n"
" \"interfaces-config\": {\n"
-" \"interfaces\": [ ],\n"
+" \"comment\": \"Use wildcard\",\n"
+" \"interfaces\": [ \"*\" ],\n"
" \"re-detect\": false\n"
" },\n"
" \"lease-database\": {\n"
" \"4o6-interface-id\": \"\",\n"
" \"4o6-subnet\": \"\",\n"
" \"boot-file-name\": \"\",\n"
-" \"id\": 1,\n"
+" \"id\": 100,\n"
" \"match-client-id\": true,\n"
" \"next-server\": \"0.0.0.0\",\n"
" \"option-data\": [ ],\n"
// Open port 0 means to not do anything at all. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
- srv_.reset(new Dhcpv4Srv(0));
+ srv_.reset(new ControlledDhcpv4Srv(0));
// Create fresh context.
resetConfiguration();
}
CfgMgr::instance().setFamily(AF_INET);
}
- boost::scoped_ptr<Dhcpv4Srv> srv_; ///< DHCP4 server under test
+ boost::scoped_ptr<ControlledDhcpv4Srv> srv_; ///< DHCP4 server under test
int rcode_; ///< Return code from element parsing
ConstElementPtr comment_; ///< Reason for parse fail
};
#include <dhcp4/tests/dhcp4_test_utils.h>
#include <dhcp4/tests/get_config_unittest.h>
#include <dhcp4/dhcp4_srv.h>
+#include <dhcp4/ctrl_dhcp4_srv.h>
#include <dhcp4/json_config_parser.h>
#include <dhcpsrv/parsers/simple_parser4.h>
// Open port 0 means to not do anything at all. We don't want to
// deal with sockets here, just check if configuration handling
// is sane.
- srv_.reset(new Dhcpv4Srv(0));
+ srv_.reset(new ControlledDhcpv4Srv(0));
// Create fresh context.
resetConfiguration();
}
CfgMgr::instance().setFamily(AF_INET);
}
- boost::scoped_ptr<Dhcpv4Srv> srv_; ///< DHCP4 server under test
+ boost::scoped_ptr<ControlledDhcpv4Srv> srv_; ///< DHCP4 server under test
int rcode_; ///< Return code from element parsing
ConstElementPtr comment_; ///< Reason for parse fail
};
return isc::dhcp::Dhcp6Parser::make_SUB_HOOKS_LIBRARY(driver.loc_);
case Parser6Context::PARSER_DHCP_DDNS:
return isc::dhcp::Dhcp6Parser::make_SUB_DHCP_DDNS(driver.loc_);
+ case Parser6Context::PARSER_LOGGING:
+ return isc::dhcp::Dhcp6Parser::make_SUB_LOGGING(driver.loc_);
}
}
%}
switch(driver.ctx_) {
case isc::dhcp::Parser6Context::DHCP6:
case isc::dhcp::Parser6Context::INTERFACES_CONFIG:
- case isc::dhcp::Parser6Context::LEASE_DATABASE:
- case isc::dhcp::Parser6Context::HOSTS_DATABASE:
case isc::dhcp::Parser6Context::SUBNET6:
case isc::dhcp::Parser6Context::SHARED_NETWORK:
case isc::dhcp::Parser6Context::OPTION_DEF:
switch(driver.ctx_) {
case isc::dhcp::Parser6Context::DHCP6:
case isc::dhcp::Parser6Context::INTERFACES_CONFIG:
- case isc::dhcp::Parser6Context::LEASE_DATABASE:
- case isc::dhcp::Parser6Context::HOSTS_DATABASE:
case isc::dhcp::Parser6Context::SUBNET6:
case isc::dhcp::Parser6Context::SHARED_NETWORK:
case isc::dhcp::Parser6Context::OPTION_DEF:
SUB_OPTION_DATA
SUB_HOOKS_LIBRARY
SUB_DHCP_DDNS
+ SUB_LOGGING
;
%token <std::string> STRING "constant string"
| 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
;
// ---- generic JSON parser ---------------------------------
| connect_timeout
| contact_points
| keyspace
- | user_context
- | comment
| unknown_map_entry
;
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.
PARSER_HOOKS_LIBRARY,
/// This will parse the input as dhcp-ddns. (D2 client config)
- PARSER_DHCP_DDNS
+ PARSER_DHCP_DDNS,
+
+ /// This will parse the content of Logging.
+ PARSER_LOGGING
+
} ParserType;
/// @brief Default constructor.
#include <dhcp/tests/iface_mgr_test_config.h>
#include <dhcp6/json_config_parser.h>
#include <dhcp6/dhcp6_srv.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcpsrv/addr_utilities.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/cfg_expiration.h>
EXPECT_TRUE(pool);
}
- int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
- Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests
+ int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
+ ControlledDhcpv6Srv srv_; ///< Instance of the ControlledDhcp6Srv used during tests
ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
string valid_iface_; ///< Valid network interface name (present in system)
string bogus_iface_; ///< invalid network interface name (not in system)
string config = "{\n"
"\"comment\": \"A DHCPv6 server\",\n"
+ "\"server-id\": {\n"
+ " \"comment\": \"DHCPv6 specific\",\n"
+ " \"type\": \"LL\"\n"
+ "},\n"
+ "\"interfaces-config\": {\n"
+ " \"comment\": \"Use wildcard\",\n"
+ " \"interfaces\": [ \"*\" ] },\n"
"\"option-def\": [ {\n"
+ " \"comment\": \"An option definition\",\n"
" \"name\": \"foo\",\n"
" \"code\": 100,\n"
- " \"comment\": \"An option definition\",\n"
" \"type\": \"ipv6-address\",\n"
" \"space\": \"isc\"\n"
" } ],\n"
"\"option-data\": [ {\n"
- " \"name\": \"subscriber-id\",\n"
" \"comment\": \"Set option value\",\n"
+ " \"name\": \"subscriber-id\",\n"
" \"data\": \"ABCDEF0105\",\n"
" \"csv-format\": false\n"
" } ],\n"
"\"client-classes\": [\n"
" {\n"
- " \"name\": \"all\",\n"
" \"comment\": \"match all\",\n"
+ " \"name\": \"all\",\n"
" \"test\": \"'' == ''\"\n"
" },\n"
" {\n"
" \"name\": \"none\"\n"
" },\n"
" {\n"
- " \"name\": \"two\",\n"
" \"comment\": \"first comment\",\n"
- " \"comment\": \"second comment\"\n"
+ " \"comment\": \"second comment\",\n"
+ " \"name\": \"two\"\n"
" },\n"
" {\n"
- " \"name\": \"both\",\n"
" \"comment\": \"a comment\",\n"
+ " \"name\": \"both\",\n"
" \"user-context\": {\n"
" \"version\": 1\n"
" }\n"
" }\n"
" ],\n"
+ "\"control-socket\": {\n"
+ " \"comment\": \"REST API\",\n"
+ " \"socket-type\": \"unix\",\n"
+ " \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n"
+ " \"user-context\": { \"comment\": \"Indirect comment\" }\n"
+ "},\n"
"\"shared-networks\": [ {\n"
- " \"name\": \"foo\"\n,"
" \"comment\": \"A shared network\"\n,"
+ " \"name\": \"foo\"\n,"
" \"subnet6\": [\n"
" { \n"
- " \"subnet\": \"2001:db1::/48\",\n"
" \"comment\": \"A subnet\"\n,"
+ " \"subnet\": \"2001:db1::/48\",\n"
+ " \"id\": 100,\n"
" \"pools\": [\n"
" {\n"
- " \"pool\": \"2001:db1::/64\",\n"
- " \"comment\": \"A pool\"\n"
+ " \"comment\": \"A pool\",\n"
+ " \"pool\": \"2001:db1::/64\"\n"
" }\n"
" ],\n"
" \"pd-pools\": [\n"
" {\n"
+ " \"comment\": \"A prefix pool\",\n"
" \"prefix\": \"2001:db2::\",\n"
" \"prefix-len\": 48,\n"
- " \"delegated-len\": 64,\n"
- " \"comment\": \"A prefix pool\"\n"
+ " \"delegated-len\": 64\n"
+ " }\n"
+ " ],\n"
+ " \"reservations\": [\n"
+ " {\n"
+ " \"comment\": \"A host reservation\",\n"
+ " \"hw-address\": \"AA:BB:CC:DD:EE:FF\",\n"
+ " \"hostname\": \"foo.example.com\",\n"
+ " \"option-data\": [ {\n"
+ " \"comment\": \"An option in a reservation\",\n"
+ " \"name\": \"domain-search\",\n"
+ " \"data\": \"example.com\"\n"
+ " } ]\n"
" }\n"
" ]\n"
" }\n"
" ]\n"
- " } ]\n"
- "} \n";
+ " } ],\n"
+ "\"dhcp-ddns\": {\n"
+ " \"comment\": \"No dynamic DNS\",\n"
+ " \"enable-updates\": false\n"
+ "}\n"
+ "}\n";
extractConfig(config);
configure(config, CONTROL_RESULT_SUCCESS, "");
ASSERT_TRUE(ctx->get("comment"));
EXPECT_EQ("\"A DHCPv6 server\"", ctx->get("comment")->str());
+ // There is a server id.
+ ConstCfgDUIDPtr duid = CfgMgr::instance().getStagingCfg()->getCfgDUID();
+ ASSERT_TRUE(duid);
+ EXPECT_EQ(DUID::DUID_LL, duid->getType());
+
+ // Check server id user context.
+ ConstElementPtr ctx_duid = duid->getContext();
+ ASSERT_TRUE(ctx_duid);
+ ASSERT_EQ(1, ctx_duid->size());
+ ASSERT_TRUE(ctx_duid->get("comment"));
+ EXPECT_EQ("\"DHCPv6 specific\"", ctx_duid->get("comment")->str());
+
+ // There is a network interface configuration.
+ ConstCfgIfacePtr iface = CfgMgr::instance().getStagingCfg()->getCfgIface();
+ ASSERT_TRUE(iface);
+
+ // Check network interface configuration user context.
+ ConstElementPtr ctx_iface = iface->getContext();
+ ASSERT_TRUE(ctx_iface);
+ ASSERT_EQ(1, ctx_iface->size());
+ ASSERT_TRUE(ctx_iface->get("comment"));
+ EXPECT_EQ("\"Use wildcard\"", ctx_iface->get("comment")->str());
+
// There is a global option definition.
- OptionDefinitionPtr opt_def = LibDHCP::getRuntimeOptionDef("isc", 100);
+ const OptionDefinitionPtr& opt_def =
+ LibDHCP::getRuntimeOptionDef("isc", 100);
ASSERT_TRUE(opt_def);
EXPECT_EQ("foo", opt_def->getName());
EXPECT_EQ(100, opt_def->getCode());
EXPECT_EQ("\"An option definition\"", ctx_opt_def->get("comment")->str());
// There is an option descriptor aka option data.
- OptionDescriptor opt_desc =
+ const OptionDescriptor& opt_desc =
CfgMgr::instance().getStagingCfg()->getCfgOption()->
get(DHCP6_OPTION_SPACE, D6O_SUBSCRIBER_ID);
ASSERT_TRUE(opt_desc.option_);
EXPECT_EQ("\"Set option value\"", ctx_opt_desc->get("comment")->str());
// And there there are some client classes.
- ClientClassDictionaryPtr dict =
+ const ClientClassDictionaryPtr& dict =
CfgMgr::instance().getStagingCfg()->getClientClassDictionary();
ASSERT_TRUE(dict);
EXPECT_EQ(4, dict->getClasses()->size());
ASSERT_TRUE(ctx_class->get("version"));
EXPECT_EQ("1", ctx_class->get("version")->str());
+ // There is a control socket.
+ ConstElementPtr socket =
+ CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
+ ASSERT_TRUE(socket);
+ ASSERT_TRUE(socket->get("socket-type"));
+ EXPECT_EQ("\"unix\"", socket->get("socket-type")->str());
+ ASSERT_TRUE(socket->get("socket-name"));
+ EXPECT_EQ("\"/tmp/kea6-ctrl-socket\"", socket->get("socket-name")->str());
+
+ // Check control socket comment and user context.
+ ConstElementPtr ctx_socket = socket->get("comment");
+ ASSERT_TRUE(ctx_socket);
+ EXPECT_EQ("\"REST API\"", ctx_socket->str());
+ ctx_socket = socket->get("user-context");
+ ASSERT_EQ(1, ctx_socket->size());
+ ASSERT_TRUE(ctx_socket->get("comment"));
+ EXPECT_EQ("\"Indirect comment\"", ctx_socket->get("comment")->str());
+
// Now verify that the shared network was indeed configured.
- CfgSharedNetworks6Ptr cfg_net = CfgMgr::instance().getStagingCfg()
- ->getCfgSharedNetworks6();
+ const CfgSharedNetworks6Ptr& cfg_net =
+ CfgMgr::instance().getStagingCfg()->getCfgSharedNetworks6();
ASSERT_TRUE(cfg_net);
const SharedNetwork6Collection* nets = cfg_net->getAll();
ASSERT_TRUE(nets);
ASSERT_EQ(1, nets->size());
SharedNetwork6Ptr net = nets->at(0);
ASSERT_TRUE(net);
+ EXPECT_EQ("foo", net->getName());
// Check shared network user context.
ConstElementPtr ctx_net = net->getContext();
EXPECT_EQ("\"A shared network\"", ctx_net->get("comment")->str());
// The shared network has a subnet.
- const Subnet6Collection * subs = net->getAllSubnets();
+ const Subnet6Collection* subs = net->getAllSubnets();
ASSERT_TRUE(subs);
ASSERT_EQ(1, subs->size());
Subnet6Ptr sub = subs->at(0);
ASSERT_EQ(1, ctx_sub->size());
ASSERT_TRUE(ctx_sub->get("comment"));
EXPECT_EQ("\"A subnet\"", ctx_sub->get("comment")->str());
+ EXPECT_EQ(100, sub->getID());
+ EXPECT_EQ("2001:db1::/48", sub->toText());
// The subnet has a pool.
const PoolCollection& pools = sub->getPools(Lease::TYPE_NA);
ASSERT_EQ(1, ctx_pdpool->size());
ASSERT_TRUE(ctx_pdpool->get("comment"));
EXPECT_EQ("\"A prefix pool\"", ctx_pdpool->get("comment")->str());
+
+ // The subnet has a host reservation.
+ uint8_t hw[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
+ HWAddrPtr hwaddr(new HWAddr(hw, sizeof(hw), HTYPE_ETHER));
+ ConstHostPtr host =
+ CfgMgr::instance().getStagingCfg()->getCfgHosts()->get6(100, DuidPtr(), hwaddr);
+ ASSERT_TRUE(host);
+ EXPECT_EQ(Host::IDENT_HWADDR, host->getIdentifierType());
+ EXPECT_EQ("aa:bb:cc:dd:ee:ff", host->getHWAddress()->toText(false));
+ EXPECT_FALSE(host->getDuid());
+ EXPECT_EQ(0, host->getIPv4SubnetID());
+ EXPECT_EQ(100, host->getIPv6SubnetID());
+ EXPECT_EQ("foo.example.com", host->getHostname());
+
+ // Check host user context.
+ ConstElementPtr ctx_host = host->getContext();
+ ASSERT_TRUE(ctx_host);
+ ASSERT_EQ(1, ctx_host->size());
+ ASSERT_TRUE(ctx_host->get("comment"));
+ EXPECT_EQ("\"A host reservation\"", ctx_host->get("comment")->str());
+
+ // The host reservation has an option data.
+ ConstCfgOptionPtr opts = host->getCfgOption6();
+ ASSERT_TRUE(opts);
+ EXPECT_FALSE(opts->empty());
+ const OptionDescriptor& host_desc =
+ opts->get(DHCP6_OPTION_SPACE, D6O_DOMAIN_SEARCH);
+ ASSERT_TRUE(host_desc.option_);
+ EXPECT_EQ(D6O_DOMAIN_SEARCH, host_desc.option_->getType());
+
+ // Check embedded option data user context.
+ ConstElementPtr ctx_host_desc = host_desc.getContext();
+ ASSERT_TRUE(ctx_host_desc);
+ ASSERT_EQ(1, ctx_host_desc->size());
+ ASSERT_TRUE(ctx_host_desc->get("comment"));
+ EXPECT_EQ("\"An option in a reservation\"",
+ ctx_host_desc->get("comment")->str());
+
+ // Finally dynamic DNS update configuration.
+ const D2ClientConfigPtr& d2 =
+ CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
+ ASSERT_TRUE(d2);
+ EXPECT_FALSE(d2->getEnableUpdates());
+
+ // Check dynamic DNS update configuration user context.
+ ConstElementPtr ctx_d2 = d2->getContext();
+ ASSERT_TRUE(ctx_d2);
+ ASSERT_EQ(1, ctx_d2->size());
+ ASSERT_TRUE(ctx_d2->get("comment"));
+ EXPECT_EQ("\"No dynamic DNS\"", ctx_d2->get("comment")->str());
+
+#if 0
+ // Loggers section supports comments too.
+
+ string logging = "{\n"
+ "\"loggers\": [ {\n"
+ " \"comment\": \"A logger\",\n"
+ " \"name\": \"kea-dhcp6\"\n"
+ "} ]\n";
+#endif
}
};
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/get_config_unittest.h>
#include <dhcp6/dhcp6_srv.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/json_config_parser.h>
#include <dhcpsrv/parsers/simple_parser6.h>
" }\n"
" }\n"
" ],\n"
+" \"control-socket\": {\n"
+" \"comment\": \"REST API\",\n"
+" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n"
+" \"socket-type\": \"unix\",\n"
+" \"user-context\": {\n"
+" \"comment\": \"Indirect comment\"\n"
+" }\n"
+" },\n"
+" \"dhcp-ddns\": {\n"
+" \"comment\": \"No dynamic DNS\",\n"
+" \"enable-updates\": false\n"
+" },\n"
+" \"interfaces-config\": {\n"
+" \"comment\": \"Use wildcard\",\n"
+" \"interfaces\": [ \"*\" ],\n"
+" \"re-detect\": false\n"
+" },\n"
" \"option-data\": [\n"
" {\n"
" \"comment\": \"Set option value\",\n"
" \"type\": \"ipv6-address\"\n"
" }\n"
" ],\n"
+" \"server-id\": {\n"
+" \"comment\": \"DHCPv6 specific\",\n"
+" \"type\": \"LL\"\n"
+" },\n"
" \"shared-networks\": [\n"
" {\n"
" \"comment\": \"A shared network\",\n"
" \"subnet6\": [\n"
" {\n"
" \"comment\": \"A subnet\",\n"
+" \"id\": 100,\n"
" \"pd-pools\": [\n"
" {\n"
" \"comment\": \"A prefix pool\",\n"
" \"pool\": \"2001:db1::/64\"\n"
" }\n"
" ],\n"
+" \"reservations\": [\n"
+" {\n"
+" \"comment\": \"A host reservation\",\n"
+" \"hostname\": \"foo.example.com\",\n"
+" \"hw-address\": \"AA:BB:CC:DD:EE:FF\",\n"
+" \"option-data\": [\n"
+" {\n"
+" \"comment\": \"An option in a reservation\",\n"
+" \"data\": \"example.com\",\n"
+" \"name\": \"domain-search\"\n"
+" }\n"
+" ]\n"
+" }\n"
+" ],\n"
" \"subnet\": \"2001:db1::/48\"\n"
" }\n"
" ]\n"
" \"option-data\": [ ]\n"
" }\n"
" ],\n"
+" \"control-socket\": {\n"
+" \"comment\": \"REST API\",\n"
+" \"socket-name\": \"/tmp/kea6-ctrl-socket\",\n"
+" \"socket-type\": \"unix\",\n"
+" \"user-context\": {\n"
+" \"comment\": \"Indirect comment\"\n"
+" }\n"
+" },\n"
" \"decline-probation-period\": 86400,\n"
" \"dhcp-ddns\": {\n"
+" \"comment\": \"No dynamic DNS\",\n"
" \"always-include-fqdn\": false,\n"
" \"enable-updates\": false,\n"
" \"generated-prefix\": \"myhost\",\n"
" \"hooks-libraries\": [ ],\n"
" \"host-reservation-identifiers\": [ \"hw-address\", \"duid\" ],\n"
" \"interfaces-config\": {\n"
-" \"interfaces\": [ ],\n"
+" \"comment\": \"Use wildcard\",\n"
+" \"interfaces\": [ \"*\" ],\n"
" \"re-detect\": false\n"
" },\n"
" \"lease-database\": {\n"
" ],\n"
" \"relay-supplied-options\": [ \"65\" ],\n"
" \"server-id\": {\n"
+" \"comment\": \"DHCPv6 specific\",\n"
" \"enterprise-id\": 0,\n"
" \"htype\": 0,\n"
" \"identifier\": \"\",\n"
" \"persist\": true,\n"
" \"time\": 0,\n"
-" \"type\": \"LLT\"\n"
+" \"type\": \"LL\"\n"
" },\n"
" \"shared-networks\": [\n"
" {\n"
" \"subnet6\": [\n"
" {\n"
" \"comment\": \"A subnet\",\n"
-" \"id\": 1,\n"
+" \"id\": 100,\n"
" \"option-data\": [ ],\n"
" \"pd-pools\": [\n"
" {\n"
CfgMgr::instance().setFamily(AF_INET6);
}
- int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
- Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests
+ int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
+ ControlledDhcpv6Srv srv_; ///< Instance of the ControlledDhcp6Srv used during tests
ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
};
#include <dhcp6/tests/dhcp6_test_utils.h>
#include <dhcp6/tests/get_config_unittest.h>
#include <dhcp6/dhcp6_srv.h>
+#include <dhcp6/ctrl_dhcp6_srv.h>
#include <dhcp6/json_config_parser.h>
#include <dhcpsrv/parsers/simple_parser6.h>
CfgMgr::instance().setFamily(AF_INET6);
}
- int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
- Dhcpv6Srv srv_; ///< Instance of the Dhcp6Srv used during tests
+ int rcode_; ///< Return code (see @ref isc::config::parseAnswer)
+ ControlledDhcpv6Srv srv_; ///< Instance of the ControlledDhcp6Srv used during tests
ConstElementPtr comment_; ///< Comment (see @ref isc::config::parseAnswer)
};
return (s.str());
}
-void
-CfgDbAccess::toElementDbAccessString(const std::string& dbaccess,
- ElementPtr map) {
+ElementPtr
+CfgDbAccess::toElementDbAccessString(const std::string& dbaccess) {
+ ElementPtr result = Element::createMap();
// Code from DatabaseConnection::parse
if (dbaccess.empty()) {
- return;
+ return (result);
}
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);
- map->set(keyword, Element::create(int_value));
+ result->set(keyword, Element::create(int_value));
} catch (...) {
isc_throw(ToElementError, "invalid DB access "
<< "integer parameter: "
} else if ((keyword == "persist") ||
(keyword == "readonly")) {
if (value == "true") {
- map->set(keyword, Element::create(true));
+ result->set(keyword, Element::create(true));
} else if (value == "false") {
- map->set(keyword, Element::create(false));
+ result->set(keyword, Element::create(false));
} else {
isc_throw(ToElementError, "invalid DB access "
<< "boolean parameter: "
(keyword == "name") ||
(keyword == "contact_points") ||
(keyword == "keyspace")) {
- map->set(keyword, Element::create(value));
+ result->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 : public UserContext {
+class CfgDbAccess {
public:
/// @brief Constructor.
/// @brief Unparse an access string
///
/// @param dbaccess the database access string
- /// @param map the element map where the access string is unparse
- static void
- toElementDbAccessString(const std::string& dbaccess,
- isc::data::ElementPtr map);
+ /// @return a pointer to configuration
+ static
+ isc::data::ElementPtr toElementDbAccessString(const std::string& dbaccess);
protected:
/// @ref isc::data::CfgToElement::toElement
///
/// @result a pointer to a configuration
- virtual isc::data::ElementPtr toElement() const;
+ virtual isc::data::ElementPtr toElement() const {
+ return (CfgDbAccess::toElementDbAccessString(lease_db_access_));
+ }
};
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;
+ virtual isc::data::ElementPtr toElement() const {
+ return (CfgDbAccess::toElementDbAccessString(host_db_access_));
+ }
};
}
}
+
#endif // CFG_DBACCESS_H
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
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());
} else if (it->first == "user-context") {
// Do not traverse user-context entries
result->set("user-context", it->second);
+ } else if (it->first == "control-socket") {
+ // Do not traverse control-socke entries
+ result->set("control-socket", it->second);
} else {
// Not comment or user-context
try {
if (it->first == "comment") {
// Do not traverse comment entries
result->set("comment", it->second);
+ } else if (it->first == "control-socket") {
+ // Do not traverse control-socke entries
+ result->set("control-socket", it->second);
} else if (it->first == "user-context") {
if (it->second->contains("comment")) {
// Note there is a entry to move