mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-31 22:15:23 +00:00
[master] Merge branch 'trac5032' (mac-sources, control-socket, relay parsers)
# Conflicts: # src/lib/testutils/io_utils.cc # src/lib/testutils/io_utils.h
This commit is contained in:
@@ -44,6 +44,14 @@
|
|||||||
"lfc-interval": 3600
|
"lfc-interval": 3600
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// This defines a control socket. If defined, Kea will open a UNIX socket
|
||||||
|
// and will listen for incoming commands. See section 15 of the Kea User's
|
||||||
|
// Guide for list of supported commands.
|
||||||
|
"control-socket": {
|
||||||
|
"socket-type": "unix",
|
||||||
|
"socket-name": "/tmp/kea4-ctrl-socket"
|
||||||
|
},
|
||||||
|
|
||||||
// Addresses will be assigned with a lifetime of 4000 seconds.
|
// Addresses will be assigned with a lifetime of 4000 seconds.
|
||||||
// The client is told to start renewing after 1000 seconds. If the server
|
// The client is told to start renewing after 1000 seconds. If the server
|
||||||
// does not respond within 2000 seconds of the lease being granted, client
|
// does not respond within 2000 seconds of the lease being granted, client
|
||||||
@@ -83,7 +91,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
|
"pools": [ { "pool": "192.0.4.1 - 192.0.4.254" } ],
|
||||||
"subnet": "192.0.4.0/24"
|
"subnet": "192.0.4.0/24",
|
||||||
|
|
||||||
|
// Sometimes the relay may use an IPv4 address that does not match
|
||||||
|
// the subnet. This is discouraged, but there are valid cases when it
|
||||||
|
// makes sense. One case is when there is a shared subnet.
|
||||||
|
"relay": {
|
||||||
|
"ip-address": "192.168.1.1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@@ -1,77 +1,90 @@
|
|||||||
# This is an example configuration file for DHCPv6 server in Kea.
|
// This is an example configuration file for DHCPv6 server in Kea.
|
||||||
# It attempts to showcase some of the more advanced features.
|
// It attempts to showcase some of the more advanced features.
|
||||||
# Topology wise, it's a basic scenario with one IPv6 subnet configured.
|
// Topology wise, it's a basic scenario with one IPv6 subnet configured.
|
||||||
# It is assumed that one subnet (2001:db8:1::/64) is available directly
|
// It is assumed that one subnet (2001:db8:1::/64) is available directly
|
||||||
# over ethX interface.
|
// over ethX interface.
|
||||||
#
|
//
|
||||||
# The following features are currently showcased here:
|
// The following features are currently showcased here:
|
||||||
# 1. Configuration of MAC/hardware address sources in DHCPv6
|
// 1. Configuration of MAC/hardware address sources in DHCPv6
|
||||||
|
// 2. RSOO (Relay supplied options) - Some relays may insert options with the
|
||||||
|
// intention for the server to insert them into client directed messages.
|
||||||
|
// 3. Control socket. Kea can open a socket and listen for incoming
|
||||||
|
// commands.
|
||||||
|
|
||||||
{ "Dhcp6":
|
{ "Dhcp6":
|
||||||
|
|
||||||
{
|
{
|
||||||
# Kea is told to listen on ethX network interface only.
|
// Kea is told to listen on ethX network interface only.
|
||||||
"interfaces-config": {
|
"interfaces-config": {
|
||||||
"interfaces": [ "ethX" ]
|
"interfaces": [ "ethX" ]
|
||||||
},
|
},
|
||||||
|
|
||||||
# We need to specify the the database used to store leases. As of
|
// We need to specify the the database used to store leases. As of
|
||||||
# September 2016, four database backends are supported: MySQL,
|
// September 2016, four database backends are supported: MySQL,
|
||||||
# PostgreSQL, Cassandra, and the in-memory database, Memfile.
|
// PostgreSQL, Cassandra, and the in-memory database, Memfile.
|
||||||
# We will use memfile because it doesn't require any prior set up.
|
// We will use memfile because it doesn't require any prior set up.
|
||||||
"lease-database": {
|
"lease-database": {
|
||||||
"type": "memfile",
|
"type": "memfile",
|
||||||
"lfc-interval": 3600
|
"lfc-interval": 3600
|
||||||
},
|
},
|
||||||
|
|
||||||
# Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
|
// Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
|
||||||
# no single reliable method of getting MAC address information in DHCPv6.
|
// no single reliable method of getting MAC address information in DHCPv6.
|
||||||
# Kea supports several methods. Depending on your network set up, some
|
// Kea supports several methods. Depending on your network set up, some
|
||||||
# methods may be more preferable than others, hence the configuration
|
// methods may be more preferable than others, hence the configuration
|
||||||
# parameter. 'mac-sources' is a list of methods. Allowed parameters are:
|
// parameter. 'mac-sources' is a list of methods. Allowed parameters are:
|
||||||
# any, raw, duid, ipv6-link-local, client-link-addr-option, rfc6939 (which
|
// any, raw, duid, ipv6-link-local, client-link-addr-option, rfc6939 (which
|
||||||
# is an alias for client-link-addr-option), remote-id, rfc4649 (which is an
|
// is an alias for client-link-addr-option), remote-id, rfc4649 (which is an
|
||||||
# alias for remote-id, subscriber-id, rfc4580 (which is an alias for
|
// alias for remote-id, subscriber-id, rfc4580 (which is an alias for
|
||||||
# subscriber-id) and docsis.
|
// subscriber-id) and docsis.
|
||||||
#
|
//
|
||||||
# Note that the order matters. Methods are attempted one by one in the order
|
// Note that the order matters. Methods are attempted one by one in the order
|
||||||
# specified until hardware address is obtained. If you don't care which method
|
// specified until hardware address is obtained. If you don't care which method
|
||||||
# is used, using 'any' is marginally faster than enumerating them all.
|
// is used, using 'any' is marginally faster than enumerating them all.
|
||||||
#
|
//
|
||||||
# If mac-sources are not specified, a default value of 'any' is used.
|
// If mac-sources are not specified, a default value of 'any' is used.
|
||||||
"mac-sources": [ "client-link-addr-option", "duid", "ipv6-link-local" ],
|
"mac-sources": [ "client-link-addr-option", "duid", "ipv6-link-local" ],
|
||||||
|
|
||||||
# RFC6422 defines a mechanism called relay-supplied options option. The relay
|
// RFC6422 defines a mechanism called relay-supplied options option. The relay
|
||||||
# agent may insert certain options that the server will echo back to the
|
// agent may insert certain options that the server will echo back to the
|
||||||
# client, if certain criteria are met. One condition is that the option must
|
// client, if certain criteria are met. One condition is that the option must
|
||||||
# be RSOO-enabled (i.e. allowed to be echoed back). IANA maintains a list
|
// be RSOO-enabled (i.e. allowed to be echoed back). IANA maintains a list
|
||||||
# of those options here:
|
// of those options here:
|
||||||
# http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied
|
// http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#options-relay-supplied
|
||||||
# However, it is possible to allow the server to echo back additional options.
|
// However, it is possible to allow the server to echo back additional options.
|
||||||
# This entry marks options 110, 120 and 130 as RSOO-enabled.
|
// This entry marks options 110, 120 and 130 as RSOO-enabled.
|
||||||
"relay-supplied-options": [ "110", "120", "130" ],
|
"relay-supplied-options": [ "110", "120", "130" ],
|
||||||
|
|
||||||
# Addresses will be assigned with preferred and valid lifetimes
|
|
||||||
# being 3000 and 4000, respectively. Client is told to start
|
// This defines a control socket. If defined, Kea will open a UNIX socket
|
||||||
# renewing after 1000 seconds. If the server does not respond
|
// and will listen for incoming commands. See section 15 of the Kea User's
|
||||||
# after 2000 seconds since the lease was granted, client is supposed
|
// Guide for list of supported commands.
|
||||||
# to start REBIND procedure (emergency renewal that allows switching
|
"control-socket": {
|
||||||
# to a different server).
|
"socket-type": "unix",
|
||||||
|
"socket-name": "/tmp/kea6-ctrl-socket"
|
||||||
|
},
|
||||||
|
|
||||||
|
// Addresses will be assigned with preferred and valid lifetimes
|
||||||
|
// being 3000 and 4000, respectively. Client is told to start
|
||||||
|
// renewing after 1000 seconds. If the server does not respond
|
||||||
|
// after 2000 seconds since the lease was granted, client is supposed
|
||||||
|
// to start REBIND procedure (emergency renewal that allows switching
|
||||||
|
// to a different server).
|
||||||
"preferred-lifetime": 3000,
|
"preferred-lifetime": 3000,
|
||||||
"valid-lifetime": 4000,
|
"valid-lifetime": 4000,
|
||||||
"renew-timer": 1000,
|
"renew-timer": 1000,
|
||||||
"rebind-timer": 2000,
|
"rebind-timer": 2000,
|
||||||
|
|
||||||
# The following list defines subnets. Each subnet consists of at
|
// The following list defines subnets. Each subnet consists of at
|
||||||
# least subnet and pool entries.
|
// least subnet and pool entries.
|
||||||
"subnet6": [
|
"subnet6": [
|
||||||
{
|
{
|
||||||
"pools": [ { "pool": "2001:db8:1::/80" } ],
|
"pools": [ { "pool": "2001:db8:1::/80" } ],
|
||||||
|
|
||||||
# This defines PD (prefix delegation) pools. In this case
|
// This defines PD (prefix delegation) pools. In this case
|
||||||
# we have only one pool. That consists of /64 prefixes
|
// we have only one pool. That consists of /64 prefixes
|
||||||
# being delegated out of large /48 pool. Each delegated
|
// being delegated out of large /48 pool. Each delegated
|
||||||
# prefix will contain an excluded-prefix option.
|
// prefix will contain an excluded-prefix option.
|
||||||
"pd-pools": [
|
"pd-pools": [
|
||||||
{
|
{
|
||||||
"prefix": "2001:db8:abcd::",
|
"prefix": "2001:db8:abcd::",
|
||||||
@@ -82,13 +95,21 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subnet": "2001:db8:1::/64",
|
"subnet": "2001:db8:1::/64",
|
||||||
"interface": "ethX"
|
"interface": "ethX",
|
||||||
|
|
||||||
|
// Sometimes the relay may use an odd IPv6 address that's not matching
|
||||||
|
// the subnet. This is discouraged, but there are valid cases when it
|
||||||
|
// makes sense. One case is when the relay has only link-local address
|
||||||
|
// and another is when there is a shared subnet scenario.
|
||||||
|
"relay": {
|
||||||
|
"ip-address": "3000::1"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
# The following configures logging. It assumes that messages with at least
|
// The following configures logging. It assumes that messages with at least
|
||||||
# informational level (info, warn, error and fatal) should be logged to stdout.
|
// informational level (info, warn, error and fatal) should be logged to stdout.
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"loggers": [
|
"loggers": [
|
||||||
{
|
{
|
||||||
|
@@ -3203,6 +3203,9 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
|
|||||||
</screen>
|
</screen>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>If "relay" is specified, the "ip-address" parameter within
|
||||||
|
it is mandatory.</para>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="dhcp4-srv-example-client-class-relay">
|
<section id="dhcp4-srv-example-client-class-relay">
|
||||||
|
@@ -3410,6 +3410,9 @@ If not specified, the default value is:
|
|||||||
</screen>
|
</screen>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>If "relay" is specified, the "ip-address" parameter within
|
||||||
|
it is mandatory.</para>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="dhcp6-client-class-relay">
|
<section id="dhcp6-client-class-relay">
|
||||||
@@ -3500,7 +3503,8 @@ If not specified, the default value is:
|
|||||||
|
|
||||||
When not specified, a special value of "any" is used, which
|
When not specified, a special value of "any" is used, which
|
||||||
instructs the server to attempt to use all the methods in sequence and use
|
instructs the server to attempt to use all the methods in sequence and use
|
||||||
value returned by the first one that succeeds.</para>
|
value returned by the first one that succeeds. If specified, it
|
||||||
|
has to have at least one value.</para>
|
||||||
|
|
||||||
<para>Supported methods are:
|
<para>Supported methods are:
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
|
@@ -189,8 +189,7 @@ protected:
|
|||||||
parser = new StringParser(config_id, string_values_);
|
parser = new StringParser(config_id, string_values_);
|
||||||
} else if (config_id.compare("pools") == 0) {
|
} else if (config_id.compare("pools") == 0) {
|
||||||
parser = new Pools4ListParser(config_id, pools_);
|
parser = new Pools4ListParser(config_id, pools_);
|
||||||
} else if (config_id.compare("relay") == 0) {
|
// relay has been converted to SimpleParser already.
|
||||||
parser = new RelayInfoParser(config_id, relay_info_, Option::V4);
|
|
||||||
// option-data has been converted to SimpleParser already.
|
// option-data has been converted to SimpleParser already.
|
||||||
} else if (config_id.compare("match-client-id") == 0) {
|
} else if (config_id.compare("match-client-id") == 0) {
|
||||||
parser = new BooleanParser(config_id, boolean_values_);
|
parser = new BooleanParser(config_id, boolean_values_);
|
||||||
@@ -440,8 +439,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
|
|||||||
parser = new D2ClientConfigParser(config_id);
|
parser = new D2ClientConfigParser(config_id);
|
||||||
} else if (config_id.compare("match-client-id") == 0) {
|
} else if (config_id.compare("match-client-id") == 0) {
|
||||||
parser = new BooleanParser(config_id, globalContext()->boolean_values_);
|
parser = new BooleanParser(config_id, globalContext()->boolean_values_);
|
||||||
} else if (config_id.compare("control-socket") == 0) {
|
// control-socket has been converted to SimpleParser already.
|
||||||
parser = new ControlSocketParser(config_id);
|
|
||||||
} else if (config_id.compare("expired-leases-processing") == 0) {
|
} else if (config_id.compare("expired-leases-processing") == 0) {
|
||||||
parser = new ExpirationConfigParser();
|
parser = new ExpirationConfigParser();
|
||||||
} else if (config_id.compare("client-classes") == 0) {
|
} else if (config_id.compare("client-classes") == 0) {
|
||||||
@@ -637,6 +635,13 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_pair.first == "control-socket") {
|
||||||
|
ControlSocketParser parser;
|
||||||
|
SrvConfigPtr srv_cfg = CfgMgr::instance().getStagingCfg();
|
||||||
|
parser.parse(*srv_cfg, config_pair.second);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first,
|
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first,
|
||||||
config_pair.second));
|
config_pair.second));
|
||||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
|
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -7,14 +7,14 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <cc/data.h>
|
#include <cc/data.h>
|
||||||
#include <dhcp4/parser_context.h>
|
#include <dhcp4/parser_context.h>
|
||||||
#include <fstream>
|
#include <testutils/io_utils.h>
|
||||||
#include <cstdio>
|
|
||||||
#include <exceptions/exceptions.h>
|
|
||||||
|
|
||||||
using namespace isc::data;
|
using namespace isc::data;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace {
|
namespace isc {
|
||||||
|
namespace dhcp {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
/// @brief compares two JSON trees
|
/// @brief compares two JSON trees
|
||||||
///
|
///
|
||||||
@@ -128,6 +128,8 @@ TEST(ParserTest, keywordDhcp4) {
|
|||||||
testParser(txt, Parser4Context::PARSER_DHCP4);
|
testParser(txt, Parser4Context::PARSER_DHCP4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests if bash (#) comments are supported. That's the only comment type that
|
||||||
|
// was supported by the old parser.
|
||||||
TEST(ParserTest, bashComments) {
|
TEST(ParserTest, bashComments) {
|
||||||
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
@@ -146,7 +148,8 @@ TEST(ParserTest, bashComments) {
|
|||||||
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParserTest, cComments) {
|
// Tests if C++ (//) comments can start anywhere, not just in the first line.
|
||||||
|
TEST(ParserTest, cppComments) {
|
||||||
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
"},\n"
|
"},\n"
|
||||||
@@ -161,6 +164,7 @@ TEST(ParserTest, cComments) {
|
|||||||
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests if bash (#) comments can start anywhere, not just in the first line.
|
||||||
TEST(ParserTest, bashCommentsInline) {
|
TEST(ParserTest, bashCommentsInline) {
|
||||||
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
@@ -176,6 +180,7 @@ TEST(ParserTest, bashCommentsInline) {
|
|||||||
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests if multi-line C style comments are handled correctly.
|
||||||
TEST(ParserTest, multilineComments) {
|
TEST(ParserTest, multilineComments) {
|
||||||
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
@@ -193,89 +198,13 @@ TEST(ParserTest, multilineComments) {
|
|||||||
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief removes comments from a JSON file
|
|
||||||
///
|
|
||||||
/// This is rather naive implementation, but it's probably sufficient for
|
|
||||||
/// testing. It won't be able to pick any trickier cases, like # or //
|
|
||||||
/// appearing in strings, nested C++ comments etc.
|
|
||||||
///
|
|
||||||
/// @param input_file file to be stripped of comments
|
|
||||||
/// @return a new file that has comments stripped from it
|
|
||||||
std::string decommentJSONfile(const std::string& input_file) {
|
|
||||||
ifstream f(input_file);
|
|
||||||
if (!f.is_open()) {
|
|
||||||
isc_throw(isc::BadValue, "can't open input file for reading: " + input_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
string outfile;
|
|
||||||
size_t last_slash = input_file.find_last_of("/");
|
|
||||||
if (last_slash != string::npos) {
|
|
||||||
outfile = input_file.substr(last_slash + 1);
|
|
||||||
} else {
|
|
||||||
outfile = input_file;
|
|
||||||
}
|
|
||||||
outfile += "-decommented";
|
|
||||||
|
|
||||||
ofstream out(outfile);
|
|
||||||
if (!out.is_open()) {
|
|
||||||
isc_throw(isc::BadValue, "can't open output file for writing: " + input_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool in_comment = false;
|
|
||||||
string line;
|
|
||||||
while (std::getline(f, line)) {
|
|
||||||
// First, let's get rid of the # comments
|
|
||||||
size_t hash_pos = line.find("#");
|
|
||||||
if (hash_pos != string::npos) {
|
|
||||||
line = line.substr(0, hash_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second, let's get rid of the // comments
|
|
||||||
size_t dblslash_pos = line.find("//");
|
|
||||||
if (dblslash_pos != string::npos) {
|
|
||||||
line = line.substr(0, dblslash_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now the tricky part: c comments.
|
|
||||||
size_t begin_pos = line.find("/*");
|
|
||||||
size_t end_pos = line.find("*/");
|
|
||||||
if (in_comment && end_pos == string::npos) {
|
|
||||||
// we continue through multiline comment
|
|
||||||
line = "";
|
|
||||||
} else {
|
|
||||||
|
|
||||||
if (begin_pos != string::npos) {
|
|
||||||
in_comment = true;
|
|
||||||
if (end_pos != string::npos) {
|
|
||||||
// sigle line comment. Let's get rid of the content in between
|
|
||||||
line = line.replace(begin_pos, end_pos + 2, end_pos + 2 - begin_pos, ' ');
|
|
||||||
in_comment = false;
|
|
||||||
} else {
|
|
||||||
line = line.substr(0, begin_pos);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (in_comment && end_pos != string::npos) {
|
|
||||||
line = line.replace(0, end_pos +2 , end_pos + 2, ' ');
|
|
||||||
in_comment = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, write the line to the output file.
|
|
||||||
out << line << endl;
|
|
||||||
}
|
|
||||||
f.close();
|
|
||||||
out.close();
|
|
||||||
|
|
||||||
return (outfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Loads specified example config file
|
/// @brief Loads specified example config file
|
||||||
///
|
///
|
||||||
/// This test loads specified example file twice: first, using the legacy
|
/// This test loads specified example file twice: first, using the legacy
|
||||||
/// JSON file and then second time using bison parser. Two created Element
|
/// JSON file and then second time using bison parser. Two created Element
|
||||||
/// trees are then compared. The input is decommented before it is passed
|
/// trees are then compared. The input is decommented before it is passed
|
||||||
/// to legacy parser (as its support for comments is very limited).
|
/// to legacy parser (as legacy support for comments is very limited).
|
||||||
///
|
///
|
||||||
/// @param fname name of the file to be loaded
|
/// @param fname name of the file to be loaded
|
||||||
void testFile(const std::string& fname) {
|
void testFile(const std::string& fname) {
|
||||||
@@ -284,8 +213,7 @@ void testFile(const std::string& fname) {
|
|||||||
|
|
||||||
string decommented = decommentJSONfile(fname);
|
string decommented = decommentJSONfile(fname);
|
||||||
|
|
||||||
cout << "Attempting to load file " << fname << " (" << decommented
|
cout << "Parsing file " << fname << " (" << decommented << ")" << endl;
|
||||||
<< ")" << endl;
|
|
||||||
|
|
||||||
EXPECT_NO_THROW(reference_json = Element::fromJSONFile(decommented, true));
|
EXPECT_NO_THROW(reference_json = Element::fromJSONFile(decommented, true));
|
||||||
|
|
||||||
@@ -329,6 +257,11 @@ TEST(ParserTest, file) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Tests error conditions in Dhcp4Parser
|
||||||
|
///
|
||||||
|
/// @param txt text to be parsed
|
||||||
|
/// @param parser_type type of the parser to be used in the test
|
||||||
|
/// @param msg expected content of the exception
|
||||||
void testError(const std::string& txt,
|
void testError(const std::string& txt,
|
||||||
Parser4Context::ParserType parser_type,
|
Parser4Context::ParserType parser_type,
|
||||||
const std::string& msg)
|
const std::string& msg)
|
||||||
@@ -347,7 +280,7 @@ void testError(const std::string& txt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check errors
|
// Verify that error conditions are handled correctly.
|
||||||
TEST(ParserTest, errors) {
|
TEST(ParserTest, errors) {
|
||||||
// no input
|
// no input
|
||||||
testError("", Parser4Context::PARSER_JSON,
|
testError("", Parser4Context::PARSER_JSON,
|
||||||
@@ -591,7 +524,7 @@ TEST(ParserTest, unicodeEscapes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test checks that all representations of a slash is recognized properly.
|
// This test checks that all representations of a slash are recognized properly.
|
||||||
TEST(ParserTest, unicodeSlash) {
|
TEST(ParserTest, unicodeSlash) {
|
||||||
// check the 4 possible encodings of solidus '/'
|
// check the 4 possible encodings of solidus '/'
|
||||||
ConstElementPtr result;
|
ConstElementPtr result;
|
||||||
@@ -609,3 +542,5 @@ TEST(ParserTest, unicodeSlash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@@ -424,8 +424,7 @@ protected:
|
|||||||
parser = new StringParser(config_id, string_values_);
|
parser = new StringParser(config_id, string_values_);
|
||||||
} else if (config_id.compare("pools") == 0) {
|
} else if (config_id.compare("pools") == 0) {
|
||||||
parser = new Pools6ListParser(config_id, pools_);
|
parser = new Pools6ListParser(config_id, pools_);
|
||||||
} else if (config_id.compare("relay") == 0) {
|
// relay has been converted to SimpleParser.
|
||||||
parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
|
|
||||||
} else if (config_id.compare("pd-pools") == 0) {
|
} else if (config_id.compare("pd-pools") == 0) {
|
||||||
parser = new PdPoolListParser(config_id, pools_);
|
parser = new PdPoolListParser(config_id, pools_);
|
||||||
// option-data was here, but it is now converted to SimpleParser
|
// option-data was here, but it is now converted to SimpleParser
|
||||||
@@ -718,13 +717,10 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
|
|||||||
parser = new HooksLibrariesParser(config_id);
|
parser = new HooksLibrariesParser(config_id);
|
||||||
} else if (config_id.compare("dhcp-ddns") == 0) {
|
} else if (config_id.compare("dhcp-ddns") == 0) {
|
||||||
parser = new D2ClientConfigParser(config_id);
|
parser = new D2ClientConfigParser(config_id);
|
||||||
} else if (config_id.compare("mac-sources") == 0) {
|
// mac-source has been converted to SimpleParser.
|
||||||
parser = new MACSourcesListConfigParser(config_id,
|
|
||||||
globalContext());
|
|
||||||
} else if (config_id.compare("relay-supplied-options") == 0) {
|
} else if (config_id.compare("relay-supplied-options") == 0) {
|
||||||
parser = new RSOOListConfigParser(config_id);
|
parser = new RSOOListConfigParser(config_id);
|
||||||
} else if (config_id.compare("control-socket") == 0) {
|
// control-socket has been converted to SimpleParser.
|
||||||
parser = new ControlSocketParser(config_id);
|
|
||||||
} else if (config_id.compare("expired-leases-processing") == 0) {
|
} else if (config_id.compare("expired-leases-processing") == 0) {
|
||||||
parser = new ExpirationConfigParser();
|
parser = new ExpirationConfigParser();
|
||||||
} else if (config_id.compare("client-classes") == 0) {
|
} else if (config_id.compare("client-classes") == 0) {
|
||||||
@@ -911,6 +907,20 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config_pair.first == "mac-sources") {
|
||||||
|
MACSourcesListConfigParser parser;
|
||||||
|
CfgMACSource& mac_source = CfgMgr::instance().getStagingCfg()->getMACSources();
|
||||||
|
parser.parse(mac_source, config_pair.second);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_pair.first == "control-socket") {
|
||||||
|
ControlSocketParser parser;
|
||||||
|
SrvConfigPtr srv_config = CfgMgr::instance().getStagingCfg();
|
||||||
|
parser.parse(*srv_config, config_pair.second);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
|
ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
|
||||||
config_pair.second));
|
config_pair.second));
|
||||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
|
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -251,6 +251,9 @@ public:
|
|||||||
ASSERT_TRUE(status);
|
ASSERT_TRUE(status);
|
||||||
comment_ = parseAnswer(rcode_, status);
|
comment_ = parseAnswer(rcode_, status);
|
||||||
EXPECT_EQ(expected_code, rcode_);
|
EXPECT_EQ(expected_code, rcode_);
|
||||||
|
if (expected_code != rcode_) {
|
||||||
|
cout << "The comment returned was: [" << comment_->stringValue() << "]" << endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Returns an interface configuration used by the most of the
|
/// @brief Returns an interface configuration used by the most of the
|
||||||
@@ -762,7 +765,7 @@ TEST_F(Dhcp6ParserTest, emptySubnet) {
|
|||||||
|
|
||||||
ConstElementPtr status;
|
ConstElementPtr status;
|
||||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||||
|
|
||||||
// returned value should be 0 (success)
|
// returned value should be 0 (success)
|
||||||
checkResult(status, 0);
|
checkResult(status, 0);
|
||||||
}
|
}
|
||||||
@@ -4292,16 +4295,15 @@ TEST_F(Dhcp6ParserTest, reservationBogus) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The goal of this test is to verify that configuration can include
|
/// The goal of this test is to verify that configuration can include
|
||||||
/// MAC/Hardware sources. This test also checks if the aliases are
|
/// MAC/Hardware sources. This case uses RFC numbers to reference methods.
|
||||||
/// handled properly (rfc6939 = client-addr-relay, rfc4649 = remote-id,
|
/// Also checks if the aliases are handled properly (rfc6939 = client-addr-relay,
|
||||||
/// rfc4580 = subscriber-id).
|
/// rfc4649 = remote-id, rfc4580 = subscriber-id).
|
||||||
TEST_F(Dhcp6ParserTest, macSources) {
|
TEST_F(Dhcp6ParserTest, macSources1) {
|
||||||
|
|
||||||
ConstElementPtr json;
|
ConstElementPtr json;
|
||||||
ASSERT_NO_THROW(json =
|
ASSERT_NO_THROW(json =
|
||||||
parseDHCP6("{ " + genIfaceConfig() + ","
|
parseDHCP6("{ " + genIfaceConfig() + ","
|
||||||
"\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\","
|
"\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\" ],"
|
||||||
"\"client-link-addr-option\", \"remote-id\", \"subscriber-id\"],"
|
|
||||||
"\"preferred-lifetime\": 3000,"
|
"\"preferred-lifetime\": 3000,"
|
||||||
"\"rebind-timer\": 2000, "
|
"\"rebind-timer\": 2000, "
|
||||||
"\"renew-timer\": 1000, "
|
"\"renew-timer\": 1000, "
|
||||||
@@ -4310,28 +4312,49 @@ TEST_F(Dhcp6ParserTest, macSources) {
|
|||||||
|
|
||||||
ConstElementPtr status;
|
ConstElementPtr status;
|
||||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||||
|
|
||||||
// returned value should be 0 (success)
|
|
||||||
checkResult(status, 0);
|
checkResult(status, 0);
|
||||||
|
|
||||||
CfgMACSources mac_sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
|
CfgMACSources sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
|
||||||
ASSERT_EQ(6, mac_sources.size());
|
|
||||||
// Let's check the aliases. They should be recognized to their base methods.
|
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, mac_sources[0]);
|
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac_sources[1]);
|
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, mac_sources[2]);
|
|
||||||
|
|
||||||
// Let's check if the actual methods are recognized properly.
|
ASSERT_EQ(3, sources.size());
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, mac_sources[3]);
|
// Let's check the aliases. They should be recognized to their base methods.
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac_sources[4]);
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, sources[0]);
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, mac_sources[5]);
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, sources[1]);
|
||||||
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, sources[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The goal of this test is to verify that MAC sources configuration can be
|
/// The goal of this test is to verify that configuration can include
|
||||||
/// empty.
|
/// MAC/Hardware sources. This uses specific method names.
|
||||||
/// Note the Dhcp6 parser requires the list to NOT be empty?!
|
TEST_F(Dhcp6ParserTest, macSources2) {
|
||||||
TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
|
|
||||||
|
|
||||||
|
ConstElementPtr json;
|
||||||
|
ASSERT_NO_THROW(json =
|
||||||
|
parseDHCP6("{ " + genIfaceConfig() + ","
|
||||||
|
"\"mac-sources\": [ \"client-link-addr-option\", \"remote-id\", "
|
||||||
|
" \"subscriber-id\"],"
|
||||||
|
"\"preferred-lifetime\": 3000,"
|
||||||
|
"\"rebind-timer\": 2000, "
|
||||||
|
"\"renew-timer\": 1000, "
|
||||||
|
"\"subnet6\": [ ], "
|
||||||
|
"\"valid-lifetime\": 4000 }"));
|
||||||
|
|
||||||
|
ConstElementPtr status;
|
||||||
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||||
|
checkResult(status, 0);
|
||||||
|
|
||||||
|
CfgMACSources sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
|
||||||
|
|
||||||
|
ASSERT_EQ(3, sources.size());
|
||||||
|
// Let's check the aliases. They should be recognized to their base methods.
|
||||||
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, sources[0]);
|
||||||
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, sources[1]);
|
||||||
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, sources[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// The goal of this test is to verify that empty MAC sources configuration
|
||||||
|
/// is rejected. If specified, this has to have at least one value.
|
||||||
|
TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
|
||||||
ConstElementPtr status;
|
ConstElementPtr status;
|
||||||
|
|
||||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
|
EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
|
||||||
@@ -4343,11 +4366,9 @@ TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
|
|||||||
"\"subnet6\": [ ], "
|
"\"subnet6\": [ ], "
|
||||||
"\"valid-lifetime\": 4000 }")));
|
"\"valid-lifetime\": 4000 }")));
|
||||||
|
|
||||||
// returned value should be 0 (success)
|
// returned value should be 1 (failure), because the mac-sources must not
|
||||||
checkResult(status, 0);
|
// be empty when specified.
|
||||||
|
checkResult(status, 1);
|
||||||
CfgMACSources mac_sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
|
|
||||||
EXPECT_EQ(0, mac_sources.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The goal of this test is to verify that MAC sources configuration can
|
/// The goal of this test is to verify that MAC sources configuration can
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2016-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -7,28 +7,42 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <cc/data.h>
|
#include <cc/data.h>
|
||||||
#include <dhcp6/parser_context.h>
|
#include <dhcp6/parser_context.h>
|
||||||
|
#include <testutils/io_utils.h>
|
||||||
|
|
||||||
using namespace isc::data;
|
using namespace isc::data;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace {
|
namespace isc {
|
||||||
|
namespace dhcp {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
void compareJSON(ConstElementPtr a, ConstElementPtr b, bool print = true) {
|
/// @brief compares two JSON trees
|
||||||
|
///
|
||||||
|
/// If differences are discovered, gtest failure is reported (using EXPECT_EQ)
|
||||||
|
///
|
||||||
|
/// @param a first to be compared
|
||||||
|
/// @param b second to be compared
|
||||||
|
void compareJSON(ConstElementPtr a, ConstElementPtr b) {
|
||||||
ASSERT_TRUE(a);
|
ASSERT_TRUE(a);
|
||||||
ASSERT_TRUE(b);
|
ASSERT_TRUE(b);
|
||||||
if (print) {
|
|
||||||
// std::cout << "JSON A: -----" << endl << a->str() << std::endl;
|
|
||||||
// std::cout << "JSON B: -----" << endl << b->str() << std::endl;
|
|
||||||
// cout << "---------" << endl << endl;
|
|
||||||
}
|
|
||||||
EXPECT_EQ(a->str(), b->str());
|
EXPECT_EQ(a->str(), b->str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void testParser(const std::string& txt, Parser6Context::ParserType parser_type) {
|
/// @brief Tests if the input string can be parsed with specific parser
|
||||||
ElementPtr reference_json;
|
///
|
||||||
|
/// The input text will be passed to bison parser of specified type.
|
||||||
|
/// Then the same input text is passed to legacy JSON parser and outputs
|
||||||
|
/// from both parsers are compared. The legacy comparison can be disabled,
|
||||||
|
/// if the feature tested is not supported by the old parser (e.g.
|
||||||
|
/// new comment styles)
|
||||||
|
///
|
||||||
|
/// @param txt text to be compared
|
||||||
|
/// @param parser_type bison parser type to be instantiated
|
||||||
|
/// @param compare whether to compare the output with legacy JSON parser
|
||||||
|
void testParser(const std::string& txt, Parser6Context::ParserType parser_type,
|
||||||
|
bool compare = true) {
|
||||||
ConstElementPtr test_json;
|
ConstElementPtr test_json;
|
||||||
|
|
||||||
ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
|
|
||||||
ASSERT_NO_THROW({
|
ASSERT_NO_THROW({
|
||||||
try {
|
try {
|
||||||
Parser6Context ctx;
|
Parser6Context ctx;
|
||||||
@@ -40,29 +54,16 @@ void testParser(const std::string& txt, Parser6Context::ParserType parser_type)
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!compare) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Now compare if both representations are the same.
|
// Now compare if both representations are the same.
|
||||||
|
ElementPtr reference_json;
|
||||||
|
ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
|
||||||
compareJSON(reference_json, test_json);
|
compareJSON(reference_json, test_json);
|
||||||
}
|
}
|
||||||
|
|
||||||
void testParser2(const std::string& txt, Parser6Context::ParserType parser_type) {
|
|
||||||
ConstElementPtr test_json;
|
|
||||||
|
|
||||||
ASSERT_NO_THROW({
|
|
||||||
try {
|
|
||||||
Parser6Context ctx;
|
|
||||||
test_json = ctx.parseString(txt, parser_type);
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
cout << "EXCEPTION: " << e.what() << endl;
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/// @todo: Implement actual validation here. since the original
|
|
||||||
/// Element::fromJSON does not support several comment types, we don't
|
|
||||||
/// have anything to compare with.
|
|
||||||
/// std::cout << "Original text:" << txt << endl;
|
|
||||||
/// std::cout << "Parsed text :" << test_json->str() << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(ParserTest, mapInMap) {
|
TEST(ParserTest, mapInMap) {
|
||||||
string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }";
|
string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }";
|
||||||
testParser(txt, Parser6Context::PARSER_JSON);
|
testParser(txt, Parser6Context::PARSER_JSON);
|
||||||
@@ -125,9 +126,11 @@ TEST(ParserTest, keywordDhcp6) {
|
|||||||
" \"subnet\": \"2001:db8:1::/48\", "
|
" \"subnet\": \"2001:db8:1::/48\", "
|
||||||
" \"interface\": \"test\" } ],\n"
|
" \"interface\": \"test\" } ],\n"
|
||||||
"\"valid-lifetime\": 4000 } }";
|
"\"valid-lifetime\": 4000 } }";
|
||||||
testParser2(txt, Parser6Context::PARSER_DHCP6);
|
testParser(txt, Parser6Context::PARSER_DHCP6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests if bash (#) comments are supported. That's the only comment type that
|
||||||
|
// was supported by the old parser.
|
||||||
TEST(ParserTest, bashComments) {
|
TEST(ParserTest, bashComments) {
|
||||||
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
@@ -144,10 +147,11 @@ TEST(ParserTest, bashComments) {
|
|||||||
" \"interface\": \"eth0\""
|
" \"interface\": \"eth0\""
|
||||||
" } ],"
|
" } ],"
|
||||||
"\"valid-lifetime\": 4000 } }";
|
"\"valid-lifetime\": 4000 } }";
|
||||||
testParser2(txt, Parser6Context::PARSER_DHCP6);
|
testParser(txt, Parser6Context::PARSER_DHCP6);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ParserTest, cComments) {
|
// Tests if C++ (//) comments can start anywhere, not just in the first line.
|
||||||
|
TEST(ParserTest, cppComments) {
|
||||||
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
"},\n"
|
"},\n"
|
||||||
@@ -160,9 +164,10 @@ TEST(ParserTest, cComments) {
|
|||||||
" \"interface\": \"eth0\""
|
" \"interface\": \"eth0\""
|
||||||
" } ],"
|
" } ],"
|
||||||
"\"valid-lifetime\": 4000 } }";
|
"\"valid-lifetime\": 4000 } }";
|
||||||
testParser2(txt, Parser6Context::PARSER_DHCP6);
|
testParser(txt, Parser6Context::PARSER_DHCP6, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests if bash (#) comments can start anywhere, not just in the first line.
|
||||||
TEST(ParserTest, bashCommentsInline) {
|
TEST(ParserTest, bashCommentsInline) {
|
||||||
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
@@ -176,9 +181,10 @@ TEST(ParserTest, bashCommentsInline) {
|
|||||||
" \"interface\": \"eth0\""
|
" \"interface\": \"eth0\""
|
||||||
" } ],"
|
" } ],"
|
||||||
"\"valid-lifetime\": 4000 } }";
|
"\"valid-lifetime\": 4000 } }";
|
||||||
testParser2(txt, Parser6Context::PARSER_DHCP6);
|
testParser(txt, Parser6Context::PARSER_DHCP6, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests if multi-line C style comments are handled correctly.
|
||||||
TEST(ParserTest, multilineComments) {
|
TEST(ParserTest, multilineComments) {
|
||||||
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||||
" \"interfaces\": [ \"*\" ]"
|
" \"interfaces\": [ \"*\" ]"
|
||||||
@@ -193,17 +199,29 @@ TEST(ParserTest, multilineComments) {
|
|||||||
" \"interface\": \"eth0\""
|
" \"interface\": \"eth0\""
|
||||||
" } ],"
|
" } ],"
|
||||||
"\"valid-lifetime\": 4000 } }";
|
"\"valid-lifetime\": 4000 } }";
|
||||||
testParser2(txt, Parser6Context::PARSER_DHCP6);
|
testParser(txt, Parser6Context::PARSER_DHCP6, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Loads specified example config file
|
||||||
void testFile(const std::string& fname, bool print) {
|
///
|
||||||
|
/// This test loads specified example file twice: first, using the legacy
|
||||||
|
/// JSON file and then second time using bison parser. Two created Element
|
||||||
|
/// trees are then compared. The input is decommented before it is passed
|
||||||
|
/// to legacy parser (as legacy support for comments is very limited).
|
||||||
|
///
|
||||||
|
/// @param fname name of the file to be loaded
|
||||||
|
void testFile(const std::string& fname) {
|
||||||
ElementPtr reference_json;
|
ElementPtr reference_json;
|
||||||
ConstElementPtr test_json;
|
ConstElementPtr test_json;
|
||||||
|
|
||||||
cout << "Attempting to load file " << fname << endl;
|
string decommented = decommentJSONfile(fname);
|
||||||
|
|
||||||
EXPECT_NO_THROW(reference_json = Element::fromJSONFile(fname, true));
|
cout << "Parsing file " << fname << "(" << decommented << ")" << endl;
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(reference_json = Element::fromJSONFile(decommented, true));
|
||||||
|
|
||||||
|
// remove the temporary file
|
||||||
|
EXPECT_NO_THROW(::remove(decommented.c_str()));
|
||||||
|
|
||||||
EXPECT_NO_THROW(
|
EXPECT_NO_THROW(
|
||||||
try {
|
try {
|
||||||
@@ -217,9 +235,7 @@ void testFile(const std::string& fname, bool print) {
|
|||||||
ASSERT_TRUE(reference_json);
|
ASSERT_TRUE(reference_json);
|
||||||
ASSERT_TRUE(test_json);
|
ASSERT_TRUE(test_json);
|
||||||
|
|
||||||
compareJSON(reference_json, test_json, print);
|
compareJSON(reference_json, test_json);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This test loads all available existing files. Each config is loaded
|
// This test loads all available existing files. Each config is loaded
|
||||||
@@ -243,10 +259,15 @@ TEST(ParserTest, file) {
|
|||||||
configs.push_back("stateless.json");
|
configs.push_back("stateless.json");
|
||||||
|
|
||||||
for (int i = 0; i<configs.size(); i++) {
|
for (int i = 0; i<configs.size(); i++) {
|
||||||
testFile(string(CFG_EXAMPLES) + "/" + configs[i], false);
|
testFile(string(CFG_EXAMPLES) + "/" + configs[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Tests error conditions in Dhcp6Parser
|
||||||
|
///
|
||||||
|
/// @param txt text to be parsed
|
||||||
|
/// @param parser_type type of the parser to be used in the test
|
||||||
|
/// @param msg expected content of the exception
|
||||||
void testError(const std::string& txt,
|
void testError(const std::string& txt,
|
||||||
Parser6Context::ParserType parser_type,
|
Parser6Context::ParserType parser_type,
|
||||||
const std::string& msg)
|
const std::string& msg)
|
||||||
@@ -265,7 +286,7 @@ void testError(const std::string& txt,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check errors
|
// Verify that error conditions are handled correctly.
|
||||||
TEST(ParserTest, errors) {
|
TEST(ParserTest, errors) {
|
||||||
// no input
|
// no input
|
||||||
testError("", Parser6Context::PARSER_JSON,
|
testError("", Parser6Context::PARSER_JSON,
|
||||||
@@ -507,9 +528,13 @@ TEST(ParserTest, unicodeEscapes) {
|
|||||||
ASSERT_EQ(Element::string, result->getType());
|
ASSERT_EQ(Element::string, result->getType());
|
||||||
EXPECT_EQ(ins, result->stringValue());
|
EXPECT_EQ(ins, result->stringValue());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test checks that all representations of a slash is recognized properly.
|
||||||
|
TEST(ParserTest, unicodeSlash) {
|
||||||
// check the 4 possible encodings of solidus '/'
|
// check the 4 possible encodings of solidus '/'
|
||||||
json = "\"/\\/\\u002f\\u002F\"";
|
ConstElementPtr result;
|
||||||
|
string json = "\"/\\/\\u002f\\u002F\"";
|
||||||
ASSERT_NO_THROW(
|
ASSERT_NO_THROW(
|
||||||
try {
|
try {
|
||||||
Parser6Context ctx;
|
Parser6Context ctx;
|
||||||
@@ -520,6 +545,8 @@ TEST(ParserTest, unicodeEscapes) {
|
|||||||
});
|
});
|
||||||
ASSERT_EQ(Element::string, result->getType());
|
ASSERT_EQ(Element::string, result->getType());
|
||||||
EXPECT_EQ("////", result->stringValue());
|
EXPECT_EQ("////", result->stringValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -47,6 +47,16 @@ uint32_t CfgMACSource::MACSourceFromText(const std::string& name) {
|
|||||||
isc_throw(BadValue, "Can't convert '" << name << "' to any known MAC source.");
|
isc_throw(BadValue, "Can't convert '" << name << "' to any known MAC source.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CfgMACSource::add(uint32_t source) {
|
||||||
|
for (CfgMACSources::const_iterator it = mac_sources_.begin();
|
||||||
|
it != mac_sources_.end(); ++it) {
|
||||||
|
if (*it == source) {
|
||||||
|
isc_throw(InvalidParameter, "mac-source paramter " << source
|
||||||
|
<< "' specified twice.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mac_sources_.push_back(source);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -52,10 +52,8 @@ class CfgMACSource {
|
|||||||
/// @param source MAC source (see constants in Pkt::HWADDR_SOURCE_*)
|
/// @param source MAC source (see constants in Pkt::HWADDR_SOURCE_*)
|
||||||
///
|
///
|
||||||
/// Specified source is being added to the mac_sources_ array.
|
/// Specified source is being added to the mac_sources_ array.
|
||||||
/// @todo implement add(string) version of this method.
|
/// @throw InvalidParameter if such a source is already defined.
|
||||||
void add(uint32_t source) {
|
void add(uint32_t source);
|
||||||
mac_sources_.push_back(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Provides access to the configure MAC/Hardware address sources.
|
/// @brief Provides access to the configure MAC/Hardware address sources.
|
||||||
///
|
///
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -177,62 +177,48 @@ template <> void ValueParser<std::string>::build(ConstElementPtr value) {
|
|||||||
|
|
||||||
// ******************** MACSourcesListConfigParser *************************
|
// ******************** MACSourcesListConfigParser *************************
|
||||||
|
|
||||||
MACSourcesListConfigParser::
|
|
||||||
MACSourcesListConfigParser(const std::string& param_name,
|
|
||||||
ParserContextPtr global_context)
|
|
||||||
: param_name_(param_name), global_context_(global_context) {
|
|
||||||
if (param_name_ != "mac-sources") {
|
|
||||||
isc_throw(BadValue, "Internal error. MAC sources configuration "
|
|
||||||
"parser called for the wrong parameter: " << param_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MACSourcesListConfigParser::build(ConstElementPtr value) {
|
MACSourcesListConfigParser::parse(CfgMACSource& mac_sources, ConstElementPtr value) {
|
||||||
CfgIface cfg_iface;
|
CfgIface cfg_iface;
|
||||||
uint32_t source = 0;
|
uint32_t source = 0;
|
||||||
|
size_t cnt = 0;
|
||||||
|
|
||||||
// By default, there's only one source defined: ANY.
|
// By default, there's only one source defined: ANY.
|
||||||
// If user specified anything, we need to get rid of that default.
|
// If user specified anything, we need to get rid of that default.
|
||||||
CfgMgr::instance().getStagingCfg()->getMACSources().clear();
|
mac_sources.clear();
|
||||||
|
|
||||||
BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
|
BOOST_FOREACH(ConstElementPtr source_elem, value->listValue()) {
|
||||||
std::string source_str = source_elem->stringValue();
|
std::string source_str = source_elem->stringValue();
|
||||||
try {
|
try {
|
||||||
source = CfgMACSource::MACSourceFromText(source_str);
|
source = CfgMACSource::MACSourceFromText(source_str);
|
||||||
CfgMgr::instance().getStagingCfg()->getMACSources().add(source);
|
mac_sources.add(source);
|
||||||
|
++cnt;
|
||||||
|
} catch (const InvalidParameter& ex) {
|
||||||
|
isc_throw(DhcpConfigError, "The mac-sources value '" << source_str
|
||||||
|
<< "' was specified twice (" << value->getPosition() << ")");
|
||||||
} catch (const std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
isc_throw(DhcpConfigError, "Failed to convert '"
|
isc_throw(DhcpConfigError, "Failed to convert '"
|
||||||
<< source_str << "' to any recognized MAC source:"
|
<< source_str << "' to any recognized MAC source:"
|
||||||
<< ex.what() << " (" << value->getPosition() << ")");
|
<< ex.what() << " (" << value->getPosition() << ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
if (!cnt) {
|
||||||
MACSourcesListConfigParser::commit() {
|
isc_throw(DhcpConfigError, "If specified, MAC Sources cannot be empty");
|
||||||
// Nothing to do.
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************** ControlSocketParser *************************
|
// ******************** ControlSocketParser *************************
|
||||||
ControlSocketParser::ControlSocketParser(const std::string& param_name) {
|
void ControlSocketParser::parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value) {
|
||||||
if (param_name != "control-socket") {
|
if (!value) {
|
||||||
isc_throw(BadValue, "Internal error. Control socket parser called "
|
isc_throw(DhcpConfigError, "Logic error: specified control-socket is null");
|
||||||
" for wrong parameter:" << param_name);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void ControlSocketParser::build(isc::data::ConstElementPtr value) {
|
|
||||||
if (value->getType() != Element::map) {
|
if (value->getType() != Element::map) {
|
||||||
isc_throw(BadValue, "Specified control-socket is expected to be a map"
|
isc_throw(DhcpConfigError, "Specified control-socket is expected to be a map"
|
||||||
", i.e. a structure defined within { }");
|
", i.e. a structure defined within { }");
|
||||||
}
|
}
|
||||||
CfgMgr::instance().getStagingCfg()->setControlSocketInfo(value);
|
srv_cfg.setControlSocketInfo(value);
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Does nothing.
|
|
||||||
void ControlSocketParser::commit() {
|
|
||||||
// Nothing to do.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ******************** HooksLibrariesParser *************************
|
// ******************** HooksLibrariesParser *************************
|
||||||
@@ -804,66 +790,66 @@ OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_l
|
|||||||
}
|
}
|
||||||
|
|
||||||
//****************************** RelayInfoParser ********************************
|
//****************************** RelayInfoParser ********************************
|
||||||
RelayInfoParser::RelayInfoParser(const std::string&,
|
RelayInfoParser::RelayInfoParser(const Option::Universe& family)
|
||||||
const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
|
: family_(family) {
|
||||||
const Option::Universe& family)
|
|
||||||
:storage_(relay_info), local_(isc::asiolink::IOAddress(
|
|
||||||
family == Option::V4 ? "0.0.0.0" : "::")),
|
|
||||||
string_values_(new StringStorage()), family_(family) {
|
|
||||||
if (!relay_info) {
|
|
||||||
isc_throw(isc::dhcp::DhcpConfigError, "parser logic error: "
|
|
||||||
<< "relay-info storage may not be NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
RelayInfoParser::build(ConstElementPtr relay_info) {
|
RelayInfoParser::parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
|
||||||
|
ConstElementPtr relay_info) {
|
||||||
|
// Let's start with some sanity checks.
|
||||||
|
if (!relay_info || !cfg) {
|
||||||
|
isc_throw(DhcpConfigError, "Logic error: RelayInfoParser::parse() called "
|
||||||
|
"with at least one NULL parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relay_info->getType() != Element::map) {
|
||||||
|
isc_throw(DhcpConfigError, "Configuration error: RelayInfoParser::parse() "
|
||||||
|
"called with non-map parameter");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now create the default value.
|
||||||
|
isc::asiolink::IOAddress ip(family_ == Option::V4 ? IOAddress::IPV4_ZERO_ADDRESS()
|
||||||
|
: IOAddress::IPV6_ZERO_ADDRESS());
|
||||||
|
|
||||||
|
// Now iterate over all parameters. Currently there's only one supported
|
||||||
|
// parameter, so it should be an easy thing to check.
|
||||||
|
bool ip_address_specified = false;
|
||||||
BOOST_FOREACH(ConfigPair param, relay_info->mapValue()) {
|
BOOST_FOREACH(ConfigPair param, relay_info->mapValue()) {
|
||||||
ParserPtr parser(createConfigParser(param.first));
|
if (param.first == "ip-address") {
|
||||||
parser->build(param.second);
|
ip_address_specified = true;
|
||||||
parser->commit();
|
|
||||||
|
try {
|
||||||
|
ip = asiolink::IOAddress(param.second->stringValue());
|
||||||
|
} catch (...) {
|
||||||
|
isc_throw(DhcpConfigError, "Failed to parse ip-address "
|
||||||
|
"value: " << param.second
|
||||||
|
<< " (" << param.second->getPosition() << ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the address family matches.
|
||||||
|
if ( (ip.isV4() && family_ != Option::V4) ||
|
||||||
|
(ip.isV6() && family_ != Option::V6) ) {
|
||||||
|
isc_throw(DhcpConfigError, "ip-address field " << ip.toText()
|
||||||
|
<< " does not have IP address of expected family type: "
|
||||||
|
<< (family_ == Option::V4 ? "IPv4" : "IPv6")
|
||||||
|
<< " (" << param.second->getPosition() << ")");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isc_throw(NotImplemented,
|
||||||
|
"parser error: RelayInfoParser parameter not supported: "
|
||||||
|
<< param.second);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the IP address
|
if (!ip_address_specified) {
|
||||||
boost::scoped_ptr<asiolink::IOAddress> ip;
|
isc_throw(DhcpConfigError, "'relay' specified, but mandatory 'ip-address' "
|
||||||
try {
|
"paramter in it is missing");
|
||||||
ip.reset(new asiolink::IOAddress(string_values_->getParam("ip-address")));
|
|
||||||
} catch (...) {
|
|
||||||
isc_throw(DhcpConfigError, "Failed to parse ip-address "
|
|
||||||
"value: " << string_values_->getParam("ip-address")
|
|
||||||
<< " (" << string_values_->getPosition("ip-address") << ")");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (ip->isV4() && family_ != Option::V4) ||
|
// Ok, we're done with parsing. Let's store the result in the structure
|
||||||
(ip->isV6() && family_ != Option::V6) ) {
|
// we were given as configuration storage.
|
||||||
isc_throw(DhcpConfigError, "ip-address field " << ip->toText()
|
*cfg = isc::dhcp::Subnet::RelayInfo(ip);
|
||||||
<< " does not have IP address of expected family type: "
|
|
||||||
<< (family_ == Option::V4 ? "IPv4" : "IPv6")
|
|
||||||
<< " (" << string_values_->getPosition("ip-address") << ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
local_.addr_ = *ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
isc::dhcp::ParserPtr
|
|
||||||
RelayInfoParser::createConfigParser(const std::string& parameter) {
|
|
||||||
DhcpConfigParser* parser = NULL;
|
|
||||||
if (parameter.compare("ip-address") == 0) {
|
|
||||||
parser = new StringParser(parameter, string_values_);
|
|
||||||
} else {
|
|
||||||
isc_throw(NotImplemented,
|
|
||||||
"parser error: RelayInfoParser parameter not supported: "
|
|
||||||
<< parameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (isc::dhcp::ParserPtr(parser));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RelayInfoParser::commit() {
|
|
||||||
*storage_ = local_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//****************************** PoolsListParser ********************************
|
//****************************** PoolsListParser ********************************
|
||||||
@@ -1069,6 +1055,12 @@ SubnetConfigParser::build(ConstElementPtr subnet) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (param.first == "relay") {
|
||||||
|
RelayInfoParser parser(global_context_->universe_);
|
||||||
|
parser.parse(relay_info_, param.second);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ParserPtr parser;
|
ParserPtr parser;
|
||||||
// When unsupported parameter is specified, the function called
|
// When unsupported parameter is specified, the function called
|
||||||
// below will thrown an exception. We have to catch this exception
|
// below will thrown an exception. We have to catch this exception
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2013-2016 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2013-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
#include <dhcpsrv/cfg_option.h>
|
#include <dhcpsrv/cfg_option.h>
|
||||||
#include <dhcpsrv/subnet.h>
|
#include <dhcpsrv/subnet.h>
|
||||||
#include <dhcpsrv/cfg_option_def.h>
|
#include <dhcpsrv/cfg_option_def.h>
|
||||||
|
#include <dhcpsrv/cfg_mac_source.h>
|
||||||
|
#include <dhcpsrv/srv_config.h>
|
||||||
#include <dhcpsrv/parsers/dhcp_config_parser.h>
|
#include <dhcpsrv/parsers/dhcp_config_parser.h>
|
||||||
#include <cc/simple_parser.h>
|
#include <cc/simple_parser.h>
|
||||||
#include <hooks/libinfo.h>
|
#include <hooks/libinfo.h>
|
||||||
@@ -372,56 +374,33 @@ private:
|
|||||||
/// It contains a list of MAC/hardware acquisition source, i.e. methods how
|
/// It contains a list of MAC/hardware acquisition source, i.e. methods how
|
||||||
/// MAC address can possibly by obtained in DHCPv6. For a currently supported
|
/// MAC address can possibly by obtained in DHCPv6. For a currently supported
|
||||||
/// methods, see @ref isc::dhcp::Pkt::getMAC.
|
/// methods, see @ref isc::dhcp::Pkt::getMAC.
|
||||||
class MACSourcesListConfigParser : public DhcpConfigParser {
|
class MACSourcesListConfigParser : public isc::data::SimpleParser {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// @brief constructor
|
|
||||||
///
|
|
||||||
/// As this is a dedicated parser, it must be used to parse
|
|
||||||
/// "mac-sources" parameter only. All other types will throw exception.
|
|
||||||
///
|
|
||||||
/// @param param_name name of the configuration parameter being parsed
|
|
||||||
/// @param global_context Global parser context.
|
|
||||||
/// @throw BadValue if supplied parameter name is not "mac-sources"
|
|
||||||
MACSourcesListConfigParser(const std::string& param_name,
|
|
||||||
ParserContextPtr global_context);
|
|
||||||
|
|
||||||
/// @brief parses parameters value
|
/// @brief parses parameters value
|
||||||
///
|
///
|
||||||
/// Parses configuration entry (list of sources) and adds each element
|
/// Parses configuration entry (list of sources) and adds each element
|
||||||
/// to the sources list.
|
/// to the sources list.
|
||||||
///
|
///
|
||||||
/// @param value pointer to the content of parsed values
|
/// @param value pointer to the content of parsed values
|
||||||
virtual void build(isc::data::ConstElementPtr value);
|
void parse(CfgMACSource& mac_sources, isc::data::ConstElementPtr value);
|
||||||
|
|
||||||
/// @brief Does nothing.
|
|
||||||
virtual void commit();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Parsed parameter name
|
|
||||||
std::string param_name_;
|
|
||||||
|
|
||||||
/// Global parser context.
|
|
||||||
ParserContextPtr global_context_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Parser for the control-socket structure
|
/// @brief Parser for the control-socket structure
|
||||||
///
|
///
|
||||||
/// It does not parse anything, simply stores the element in
|
/// It does not parse anything, simply stores the element in
|
||||||
/// the staging config.
|
/// the staging config.
|
||||||
class ControlSocketParser : public DhcpConfigParser {
|
class ControlSocketParser : public isc::data::SimpleParser {
|
||||||
public:
|
public:
|
||||||
|
/// @brief "Parses" control-socket structure
|
||||||
ControlSocketParser(const std::string& param_name);
|
|
||||||
|
|
||||||
/// @brief Stores contents of the control-socket map in the staging config.
|
|
||||||
///
|
///
|
||||||
|
/// Since the SrvConfig structure takes the socket definition
|
||||||
|
/// as ConstElementPtr, there's really nothing to parse here.
|
||||||
|
/// It only does basic sanity checks and throws DhcpConfigError
|
||||||
|
/// if the value is null or is not a map.
|
||||||
|
///
|
||||||
|
/// @param srv_cfg parsed values will be stored here
|
||||||
/// @param value pointer to the content of parsed values
|
/// @param value pointer to the content of parsed values
|
||||||
virtual void build(isc::data::ConstElementPtr value);
|
void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
|
||||||
|
|
||||||
/// @brief Does nothing.
|
|
||||||
virtual void commit();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @brief Parser for hooks library list
|
/// @brief Parser for hooks library list
|
||||||
@@ -849,48 +828,24 @@ protected:
|
|||||||
/// is expected that the number of parameters will increase over time.
|
/// is expected that the number of parameters will increase over time.
|
||||||
///
|
///
|
||||||
/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[x]/relay parameters.
|
/// It is useful for parsing Dhcp<4/6>/subnet<4/6>[x]/relay parameters.
|
||||||
class RelayInfoParser : public DhcpConfigParser {
|
class RelayInfoParser : public isc::data::SimpleParser {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// @brief constructor
|
/// @brief constructor
|
||||||
/// @param unused first argument is ignored, all Parser constructors
|
|
||||||
/// accept string as first argument.
|
|
||||||
/// @param relay_info is the storage in which to store the parsed
|
|
||||||
/// @param family specifies protocol family (IPv4 or IPv6)
|
/// @param family specifies protocol family (IPv4 or IPv6)
|
||||||
RelayInfoParser(const std::string& unused,
|
RelayInfoParser(const isc::dhcp::Option::Universe& family);
|
||||||
const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
|
|
||||||
const isc::dhcp::Option::Universe& family);
|
|
||||||
|
|
||||||
/// @brief parses the actual relay parameters
|
/// @brief parses the actual relay parameters
|
||||||
/// @param relay_info JSON structure holding relay parameters to parse
|
|
||||||
virtual void build(isc::data::ConstElementPtr relay_info);
|
|
||||||
|
|
||||||
/// @brief stores parsed info in relay_info
|
|
||||||
virtual void commit();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
/// @brief Creates a parser for the given "relay" member element id.
|
|
||||||
///
|
///
|
||||||
/// The elements currently supported are:
|
/// The elements currently supported are:
|
||||||
/// -# ip-address
|
/// -# ip-address
|
||||||
///
|
///
|
||||||
/// @param parser is the "item_name" for a specific member element of
|
/// @param cfg configuration will be stored here
|
||||||
/// the "relay" specification.
|
/// @param relay_info JSON structure holding relay parameters to parse
|
||||||
///
|
void parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
|
||||||
/// @return returns a pointer to newly created parser.
|
isc::data::ConstElementPtr relay_info);
|
||||||
isc::dhcp::ParserPtr
|
|
||||||
createConfigParser(const std::string& parser);
|
|
||||||
|
|
||||||
/// Parsed data will be stored there on commit()
|
|
||||||
isc::dhcp::Subnet::RelayInfoPtr storage_;
|
|
||||||
|
|
||||||
/// Local storage information (for temporary values)
|
|
||||||
isc::dhcp::Subnet::RelayInfo local_;
|
|
||||||
|
|
||||||
/// Storage for subnet-specific string values.
|
|
||||||
StringStoragePtr string_values_;
|
|
||||||
|
|
||||||
|
protected:
|
||||||
/// Protocol family (IPv4 or IPv6)
|
/// Protocol family (IPv4 or IPv6)
|
||||||
Option::Universe family_;
|
Option::Universe family_;
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (C) 2012-2016 Internet Systems Consortium, Inc. ("ISC")
|
// Copyright (C) 2012-2017 Internet Systems Consortium, Inc. ("ISC")
|
||||||
//
|
//
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
// 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
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@@ -226,48 +226,95 @@ TEST_F(DhcpParserTest, uint32ParserTest) {
|
|||||||
EXPECT_EQ(test_value, actual_value);
|
EXPECT_EQ(test_value, actual_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Check MACSourcesListConfigParser basic functionality
|
/// Verifies the code that parses mac sources and adds them to CfgMgr
|
||||||
///
|
TEST_F(DhcpParserTest, MacSources) {
|
||||||
/// Verifies that the parser:
|
|
||||||
/// 1. Does not allow empty for storage.
|
|
||||||
/// 2. Does not allow name other than "mac-sources"
|
|
||||||
/// 3. Parses list of mac sources and adds them to CfgMgr
|
|
||||||
TEST_F(DhcpParserTest, MacSourcesListConfigParserTest) {
|
|
||||||
|
|
||||||
const std::string valid_name = "mac-sources";
|
|
||||||
const std::string bogus_name = "bogus-name";
|
|
||||||
|
|
||||||
ParserContextPtr parser_context(new ParserContext(Option::V6));
|
|
||||||
|
|
||||||
// Verify that parser constructor fails if parameter name isn't "mac-sources"
|
|
||||||
EXPECT_THROW(MACSourcesListConfigParser(bogus_name, parser_context),
|
|
||||||
isc::BadValue);
|
|
||||||
|
|
||||||
// That's an equivalent of the following snippet:
|
// That's an equivalent of the following snippet:
|
||||||
// "mac-sources: [ \"duid\", \"ipv6\" ]";
|
// "mac-sources: [ \"duid\", \"ipv6\" ]";
|
||||||
ElementPtr config = Element::createList();
|
ElementPtr values = Element::createList();
|
||||||
config->add(Element::create("duid"));
|
values->add(Element::create("duid"));
|
||||||
config->add(Element::create("ipv6-link-local"));
|
values->add(Element::create("ipv6-link-local"));
|
||||||
|
|
||||||
boost::scoped_ptr<MACSourcesListConfigParser>
|
// Let's grab server configuration from CfgMgr
|
||||||
parser(new MACSourcesListConfigParser(valid_name, parser_context));
|
|
||||||
|
|
||||||
// This should parse the configuration and add eth0 and eth1 to the list
|
|
||||||
// of interfaces that server should listen on.
|
|
||||||
EXPECT_NO_THROW(parser->build(config));
|
|
||||||
EXPECT_NO_THROW(parser->commit());
|
|
||||||
|
|
||||||
// Use CfgMgr instance to check if eth0 and eth1 was added, and that
|
|
||||||
// eth2 was not added.
|
|
||||||
SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
|
SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
|
||||||
ASSERT_TRUE(cfg);
|
ASSERT_TRUE(cfg);
|
||||||
CfgMACSources configured_sources = cfg->getMACSources().get();
|
CfgMACSource& sources = cfg->getMACSources();
|
||||||
|
|
||||||
|
// This should parse the configuration and check that it doesn't throw.
|
||||||
|
MACSourcesListConfigParser parser;
|
||||||
|
EXPECT_NO_THROW(parser.parse(sources, values));
|
||||||
|
|
||||||
|
// Finally, check the sources that were configured
|
||||||
|
CfgMACSources configured_sources = cfg->getMACSources().get();
|
||||||
ASSERT_EQ(2, configured_sources.size());
|
ASSERT_EQ(2, configured_sources.size());
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, configured_sources[0]);
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, configured_sources[0]);
|
||||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, configured_sources[1]);
|
EXPECT_EQ(HWAddr::HWADDR_SOURCE_IPV6_LINK_LOCAL, configured_sources[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Check MACSourcesListConfigParser rejecting empty list
|
||||||
|
///
|
||||||
|
/// Verifies that the code rejects an empty mac-sources list.
|
||||||
|
TEST_F(DhcpParserTest, MacSourcesEmpty) {
|
||||||
|
|
||||||
|
// That's an equivalent of the following snippet:
|
||||||
|
// "mac-sources: [ \"duid\", \"ipv6\" ]";
|
||||||
|
ElementPtr values = Element::createList();
|
||||||
|
|
||||||
|
// Let's grab server configuration from CfgMgr
|
||||||
|
SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
|
||||||
|
ASSERT_TRUE(cfg);
|
||||||
|
CfgMACSource& sources = cfg->getMACSources();
|
||||||
|
|
||||||
|
// This should throw, because if specified, at least one MAC source
|
||||||
|
// has to be specified.
|
||||||
|
MACSourcesListConfigParser parser;
|
||||||
|
EXPECT_THROW(parser.parse(sources, values), DhcpConfigError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check MACSourcesListConfigParser rejecting empty list
|
||||||
|
///
|
||||||
|
/// Verifies that the code rejects fake mac source.
|
||||||
|
TEST_F(DhcpParserTest, MacSourcesBogus) {
|
||||||
|
|
||||||
|
// That's an equivalent of the following snippet:
|
||||||
|
// "mac-sources: [ \"duid\", \"ipv6\" ]";
|
||||||
|
ElementPtr values = Element::createList();
|
||||||
|
values->add(Element::create("from-ebay"));
|
||||||
|
values->add(Element::create("just-guess-it"));
|
||||||
|
|
||||||
|
// Let's grab server configuration from CfgMgr
|
||||||
|
SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
|
||||||
|
ASSERT_TRUE(cfg);
|
||||||
|
CfgMACSource& sources = cfg->getMACSources();
|
||||||
|
|
||||||
|
// This should throw, because these are not valid sources.
|
||||||
|
MACSourcesListConfigParser parser;
|
||||||
|
EXPECT_THROW(parser.parse(sources, values), DhcpConfigError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies the code that properly catches duplicate entries
|
||||||
|
/// in mac-sources definition.
|
||||||
|
TEST_F(DhcpParserTest, MacSourcesDuplicate) {
|
||||||
|
|
||||||
|
// That's an equivalent of the following snippet:
|
||||||
|
// "mac-sources: [ \"duid\", \"ipv6\" ]";
|
||||||
|
ElementPtr values = Element::createList();
|
||||||
|
values->add(Element::create("ipv6-link-local"));
|
||||||
|
values->add(Element::create("duid"));
|
||||||
|
values->add(Element::create("duid"));
|
||||||
|
values->add(Element::create("duid"));
|
||||||
|
|
||||||
|
// Let's grab server configuration from CfgMgr
|
||||||
|
SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
|
||||||
|
ASSERT_TRUE(cfg);
|
||||||
|
CfgMACSource& sources = cfg->getMACSources();
|
||||||
|
|
||||||
|
// This should parse the configuration and check that it throws.
|
||||||
|
MACSourcesListConfigParser parser;
|
||||||
|
EXPECT_THROW(parser.parse(sources, values), DhcpConfigError);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @brief Test Fixture class which provides basic structure for testing
|
/// @brief Test Fixture class which provides basic structure for testing
|
||||||
/// configuration parsing. This is essentially the same structure provided
|
/// configuration parsing. This is essentially the same structure provided
|
||||||
/// by dhcp servers.
|
/// by dhcp servers.
|
||||||
@@ -2416,6 +2463,19 @@ TEST_F(ParseConfigTest, validRelayInfo4) {
|
|||||||
" }";
|
" }";
|
||||||
ElementPtr json = Element::fromJSON(config_str);
|
ElementPtr json = Element::fromJSON(config_str);
|
||||||
|
|
||||||
|
// We need to set the default ip-address to something.
|
||||||
|
Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("0.0.0.0")));
|
||||||
|
|
||||||
|
RelayInfoParser parser(Option::V4);
|
||||||
|
|
||||||
|
// Subnet4 parser will pass 0.0.0.0 to the RelayInfoParser
|
||||||
|
EXPECT_NO_THROW(parser.parse(result, json));
|
||||||
|
EXPECT_EQ("192.0.2.1", result->addr_.toText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Checks that a bogus relay info structure for IPv4 is rejected.
|
||||||
|
TEST_F(ParseConfigTest, bogusRelayInfo4) {
|
||||||
|
|
||||||
// Invalid config (wrong family type of the ip-address field)
|
// Invalid config (wrong family type of the ip-address field)
|
||||||
std::string config_str_bogus1 =
|
std::string config_str_bogus1 =
|
||||||
" {"
|
" {"
|
||||||
@@ -2430,24 +2490,25 @@ TEST_F(ParseConfigTest, validRelayInfo4) {
|
|||||||
" }";
|
" }";
|
||||||
ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
|
ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
|
||||||
|
|
||||||
|
// Invalid config (ip-address is mandatory)
|
||||||
|
std::string config_str_bogus3 =
|
||||||
|
" {"
|
||||||
|
" }";
|
||||||
|
ElementPtr json_bogus3 = Element::fromJSON(config_str_bogus3);
|
||||||
|
|
||||||
// We need to set the default ip-address to something.
|
// We need to set the default ip-address to something.
|
||||||
Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("0.0.0.0")));
|
Subnet::RelayInfoPtr result(new Subnet::RelayInfo(IOAddress::IPV4_ZERO_ADDRESS()));
|
||||||
|
|
||||||
boost::shared_ptr<RelayInfoParser> parser;
|
RelayInfoParser parser(Option::V4);
|
||||||
|
|
||||||
// Subnet4 parser will pass 0.0.0.0 to the RelayInfoParser
|
// wrong family type
|
||||||
EXPECT_NO_THROW(parser.reset(new RelayInfoParser("ignored", result,
|
EXPECT_THROW(parser.parse(result, json_bogus1), DhcpConfigError);
|
||||||
Option::V4)));
|
|
||||||
EXPECT_NO_THROW(parser->build(json));
|
|
||||||
EXPECT_NO_THROW(parser->commit());
|
|
||||||
|
|
||||||
EXPECT_EQ("192.0.2.1", result->addr_.toText());
|
// Too large byte values in pseudo-IPv4 addr
|
||||||
|
EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
|
||||||
|
|
||||||
// Let's check negative scenario (wrong family type)
|
// Mandatory ip-address is missing. What a pity.
|
||||||
EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
|
EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
|
||||||
|
|
||||||
// Let's check negative scenario (too large byte values in pseudo-IPv4 addr)
|
|
||||||
EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Checks that a valid relay info structure for IPv6 can be handled
|
/// @brief Checks that a valid relay info structure for IPv6 can be handled
|
||||||
@@ -2460,6 +2521,18 @@ TEST_F(ParseConfigTest, validRelayInfo6) {
|
|||||||
" }";
|
" }";
|
||||||
ElementPtr json = Element::fromJSON(config_str);
|
ElementPtr json = Element::fromJSON(config_str);
|
||||||
|
|
||||||
|
// We need to set the default ip-address to something.
|
||||||
|
Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
|
||||||
|
|
||||||
|
RelayInfoParser parser(Option::V6);
|
||||||
|
// Subnet4 parser will pass :: to the RelayInfoParser
|
||||||
|
EXPECT_NO_THROW(parser.parse(result, json));
|
||||||
|
EXPECT_EQ("2001:db8::1", result->addr_.toText());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Checks that a valid relay info structure for IPv6 can be handled
|
||||||
|
TEST_F(ParseConfigTest, bogusRelayInfo6) {
|
||||||
|
|
||||||
// Invalid config (wrong family type of the ip-address field
|
// Invalid config (wrong family type of the ip-address field
|
||||||
std::string config_str_bogus1 =
|
std::string config_str_bogus1 =
|
||||||
" {"
|
" {"
|
||||||
@@ -2474,23 +2547,25 @@ TEST_F(ParseConfigTest, validRelayInfo6) {
|
|||||||
" }";
|
" }";
|
||||||
ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
|
ElementPtr json_bogus2 = Element::fromJSON(config_str_bogus2);
|
||||||
|
|
||||||
|
// Missing mandatory ip-address field.
|
||||||
|
std::string config_str_bogus3 =
|
||||||
|
" {"
|
||||||
|
" }";
|
||||||
|
ElementPtr json_bogus3 = Element::fromJSON(config_str_bogus3);
|
||||||
|
|
||||||
// We need to set the default ip-address to something.
|
// We need to set the default ip-address to something.
|
||||||
Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
|
Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
|
||||||
|
|
||||||
boost::shared_ptr<RelayInfoParser> parser;
|
RelayInfoParser parser(Option::V6);
|
||||||
// Subnet4 parser will pass :: to the RelayInfoParser
|
|
||||||
EXPECT_NO_THROW(parser.reset(new RelayInfoParser("ignored", result,
|
|
||||||
Option::V6)));
|
|
||||||
EXPECT_NO_THROW(parser->build(json));
|
|
||||||
EXPECT_NO_THROW(parser->commit());
|
|
||||||
|
|
||||||
EXPECT_EQ("2001:db8::1", result->addr_.toText());
|
// Negative scenario (wrong family type)
|
||||||
|
EXPECT_THROW(parser.parse(result, json_bogus1), DhcpConfigError);
|
||||||
|
|
||||||
// Let's check negative scenario (wrong family type)
|
// Looks like IPv6 address, but has too many colons
|
||||||
EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
|
EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
|
||||||
|
|
||||||
// Unparseable text that looks like IPv6 address, but has too many colons
|
// Mandatory ip-address is missing. What a pity.
|
||||||
EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
|
EXPECT_THROW(parser.parse(result, json_bogus3), DhcpConfigError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// There's no test for ControlSocketParser, as it is tested in the DHCPv4 code
|
// There's no test for ControlSocketParser, as it is tested in the DHCPv4 code
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
#include <exceptions/exceptions.h>
|
||||||
#include <testutils/io_utils.h>
|
#include <testutils/io_utils.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
@@ -36,7 +37,78 @@ std::string readFile(const std::string& file_path) {
|
|||||||
return (output.str());
|
return (output.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string decommentJSONfile(const std::string& input_file) {
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
ifstream f(input_file);
|
||||||
|
if (!f.is_open()) {
|
||||||
|
isc_throw(isc::BadValue, "can't open input file for reading: " + input_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
string outfile;
|
||||||
|
size_t last_slash = input_file.find_last_of("/");
|
||||||
|
if (last_slash != string::npos) {
|
||||||
|
outfile = input_file.substr(last_slash + 1);
|
||||||
|
} else {
|
||||||
|
outfile = input_file;
|
||||||
|
}
|
||||||
|
outfile += "-decommented";
|
||||||
|
|
||||||
|
ofstream out(outfile);
|
||||||
|
if (!out.is_open()) {
|
||||||
|
isc_throw(isc::BadValue, "can't open output file for writing: " + input_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_comment = false;
|
||||||
|
string line;
|
||||||
|
while (std::getline(f, line)) {
|
||||||
|
// First, let's get rid of the # comments
|
||||||
|
size_t hash_pos = line.find("#");
|
||||||
|
if (hash_pos != string::npos) {
|
||||||
|
line = line.substr(0, hash_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second, let's get rid of the // comments
|
||||||
|
size_t dblslash_pos = line.find("//");
|
||||||
|
if (dblslash_pos != string::npos) {
|
||||||
|
line = line.substr(0, dblslash_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now the tricky part: c comments.
|
||||||
|
size_t begin_pos = line.find("/*");
|
||||||
|
size_t end_pos = line.find("*/");
|
||||||
|
if (in_comment && end_pos == string::npos) {
|
||||||
|
// we continue through multiline comment
|
||||||
|
line = "";
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (begin_pos != string::npos) {
|
||||||
|
in_comment = true;
|
||||||
|
if (end_pos != string::npos) {
|
||||||
|
// sigle line comment. Let's get rid of the content in between
|
||||||
|
line = line.replace(begin_pos, end_pos + 2, end_pos + 2 - begin_pos, ' ');
|
||||||
|
in_comment = false;
|
||||||
|
} else {
|
||||||
|
line = line.substr(0, begin_pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (in_comment && end_pos != string::npos) {
|
||||||
|
line = line.replace(0, end_pos +2 , end_pos + 2, ' ');
|
||||||
|
in_comment = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, write the line to the output file.
|
||||||
|
out << line << endl;
|
||||||
|
}
|
||||||
|
f.close();
|
||||||
|
out.close();
|
||||||
|
|
||||||
|
return (outfile);
|
||||||
|
}
|
||||||
|
|
||||||
}; // end of isc::dhcp::test namespace
|
}; // end of isc::dhcp::test namespace
|
||||||
}; // end of isc::dhcp namespace
|
}; // end of isc::dhcp namespace
|
||||||
}; // end of isc namespace
|
}; // end of isc namespace
|
||||||
|
|
||||||
|
@@ -26,6 +26,20 @@ bool fileExists(const std::string& file_path);
|
|||||||
/// @return File contents.
|
/// @return File contents.
|
||||||
std::string readFile(const std::string& file_path);
|
std::string readFile(const std::string& file_path);
|
||||||
|
|
||||||
|
/// @brief Removes comments from a JSON file
|
||||||
|
///
|
||||||
|
/// Removes //, # and /* */ comments from the input file and writes its content
|
||||||
|
/// to another file. The comments are replaced with spaces, so the original
|
||||||
|
/// token locations should remain unaffected. This is rather naive
|
||||||
|
/// implementation, but it's probably sufficient for testing. It won't be able
|
||||||
|
/// to pick any trickier cases, like # or // appearing in strings, nested C++
|
||||||
|
/// comments etc.
|
||||||
|
///
|
||||||
|
/// @param input_file file to be stripped of comments
|
||||||
|
/// @return filename of a new file that has comments stripped from it
|
||||||
|
/// @throw BadValue if the input file cannot be opened
|
||||||
|
std::string decommentJSONfile(const std::string& input_file);
|
||||||
|
|
||||||
}; // end of isc::dhcp::test namespace
|
}; // end of isc::dhcp::test namespace
|
||||||
}; // end of isc::dhcp namespace
|
}; // end of isc::dhcp namespace
|
||||||
}; // end of isc namespace
|
}; // end of isc namespace
|
||||||
|
Reference in New Issue
Block a user