mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-08-29 04:57:52 +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:
commit
f1c9dee093
@ -44,6 +44,14 @@
|
||||
"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.
|
||||
// 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
|
||||
@ -83,7 +91,14 @@
|
||||
},
|
||||
{
|
||||
"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.
|
||||
# It attempts to showcase some of the more advanced features.
|
||||
# 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
|
||||
# over ethX interface.
|
||||
#
|
||||
# The following features are currently showcased here:
|
||||
# 1. Configuration of MAC/hardware address sources in DHCPv6
|
||||
// This is an example configuration file for DHCPv6 server in Kea.
|
||||
// It attempts to showcase some of the more advanced features.
|
||||
// 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
|
||||
// over ethX interface.
|
||||
//
|
||||
// The following features are currently showcased here:
|
||||
// 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":
|
||||
|
||||
{
|
||||
# Kea is told to listen on ethX network interface only.
|
||||
// Kea is told to listen on ethX network interface only.
|
||||
"interfaces-config": {
|
||||
"interfaces": [ "ethX" ]
|
||||
},
|
||||
|
||||
# We need to specify the the database used to store leases. As of
|
||||
# September 2016, four database backends are supported: MySQL,
|
||||
# PostgreSQL, Cassandra, and the in-memory database, Memfile.
|
||||
# We will use memfile because it doesn't require any prior set up.
|
||||
// We need to specify the the database used to store leases. As of
|
||||
// September 2016, four database backends are supported: MySQL,
|
||||
// PostgreSQL, Cassandra, and the in-memory database, Memfile.
|
||||
// We will use memfile because it doesn't require any prior set up.
|
||||
"lease-database": {
|
||||
"type": "memfile",
|
||||
"lfc-interval": 3600
|
||||
},
|
||||
|
||||
# Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
|
||||
# no single reliable method of getting MAC address information in DHCPv6.
|
||||
# Kea supports several methods. Depending on your network set up, some
|
||||
# methods may be more preferable than others, hence the configuration
|
||||
# parameter. 'mac-sources' is a list of methods. Allowed parameters are:
|
||||
# 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
|
||||
# alias for remote-id, subscriber-id, rfc4580 (which is an alias for
|
||||
# subscriber-id) and docsis.
|
||||
#
|
||||
# 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
|
||||
# is used, using 'any' is marginally faster than enumerating them all.
|
||||
#
|
||||
# If mac-sources are not specified, a default value of 'any' is used.
|
||||
// Kea 0.9.1 introduced MAC/hardware addresses support in DHCPv6. There is
|
||||
// no single reliable method of getting MAC address information in DHCPv6.
|
||||
// Kea supports several methods. Depending on your network set up, some
|
||||
// methods may be more preferable than others, hence the configuration
|
||||
// parameter. 'mac-sources' is a list of methods. Allowed parameters are:
|
||||
// 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
|
||||
// alias for remote-id, subscriber-id, rfc4580 (which is an alias for
|
||||
// subscriber-id) and docsis.
|
||||
//
|
||||
// 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
|
||||
// is used, using 'any' is marginally faster than enumerating them all.
|
||||
//
|
||||
// If mac-sources are not specified, a default value of 'any' is used.
|
||||
"mac-sources": [ "client-link-addr-option", "duid", "ipv6-link-local" ],
|
||||
|
||||
# RFC6422 defines a mechanism called relay-supplied options option. The relay
|
||||
# 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
|
||||
# be RSOO-enabled (i.e. allowed to be echoed back). IANA maintains a list
|
||||
# of those options here:
|
||||
# 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.
|
||||
# This entry marks options 110, 120 and 130 as RSOO-enabled.
|
||||
// RFC6422 defines a mechanism called relay-supplied options option. The relay
|
||||
// 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
|
||||
// be RSOO-enabled (i.e. allowed to be echoed back). IANA maintains a list
|
||||
// of those options here:
|
||||
// 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.
|
||||
// This entry marks options 110, 120 and 130 as RSOO-enabled.
|
||||
"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
|
||||
# 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).
|
||||
|
||||
// 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/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,
|
||||
"valid-lifetime": 4000,
|
||||
"renew-timer": 1000,
|
||||
"rebind-timer": 2000,
|
||||
|
||||
# The following list defines subnets. Each subnet consists of at
|
||||
# least subnet and pool entries.
|
||||
// The following list defines subnets. Each subnet consists of at
|
||||
// least subnet and pool entries.
|
||||
"subnet6": [
|
||||
{
|
||||
"pools": [ { "pool": "2001:db8:1::/80" } ],
|
||||
|
||||
# This defines PD (prefix delegation) pools. In this case
|
||||
# we have only one pool. That consists of /64 prefixes
|
||||
# being delegated out of large /48 pool. Each delegated
|
||||
# prefix will contain an excluded-prefix option.
|
||||
// This defines PD (prefix delegation) pools. In this case
|
||||
// we have only one pool. That consists of /64 prefixes
|
||||
// being delegated out of large /48 pool. Each delegated
|
||||
// prefix will contain an excluded-prefix option.
|
||||
"pd-pools": [
|
||||
{
|
||||
"prefix": "2001:db8:abcd::",
|
||||
@ -82,13 +95,21 @@
|
||||
}
|
||||
],
|
||||
"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
|
||||
# informational level (info, warn, error and fatal) should be logged to stdout.
|
||||
// The following configures logging. It assumes that messages with at least
|
||||
// informational level (info, warn, error and fatal) should be logged to stdout.
|
||||
"Logging": {
|
||||
"loggers": [
|
||||
{
|
||||
|
@ -3203,6 +3203,9 @@ src/lib/dhcpsrv/cfg_host_operations.cc -->
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>If "relay" is specified, the "ip-address" parameter within
|
||||
it is mandatory.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="dhcp4-srv-example-client-class-relay">
|
||||
|
@ -3410,6 +3410,9 @@ If not specified, the default value is:
|
||||
</screen>
|
||||
</para>
|
||||
|
||||
<para>If "relay" is specified, the "ip-address" parameter within
|
||||
it is mandatory.</para>
|
||||
|
||||
</section>
|
||||
|
||||
<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
|
||||
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:
|
||||
<itemizedlist>
|
||||
|
@ -189,8 +189,7 @@ protected:
|
||||
parser = new StringParser(config_id, string_values_);
|
||||
} else if (config_id.compare("pools") == 0) {
|
||||
parser = new Pools4ListParser(config_id, pools_);
|
||||
} else if (config_id.compare("relay") == 0) {
|
||||
parser = new RelayInfoParser(config_id, relay_info_, Option::V4);
|
||||
// relay has been converted to SimpleParser already.
|
||||
// option-data has been converted to SimpleParser already.
|
||||
} else if (config_id.compare("match-client-id") == 0) {
|
||||
parser = new BooleanParser(config_id, boolean_values_);
|
||||
@ -440,8 +439,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
|
||||
parser = new D2ClientConfigParser(config_id);
|
||||
} else if (config_id.compare("match-client-id") == 0) {
|
||||
parser = new BooleanParser(config_id, globalContext()->boolean_values_);
|
||||
} else if (config_id.compare("control-socket") == 0) {
|
||||
parser = new ControlSocketParser(config_id);
|
||||
// control-socket has been converted to SimpleParser already.
|
||||
} else if (config_id.compare("expired-leases-processing") == 0) {
|
||||
parser = new ExpirationConfigParser();
|
||||
} else if (config_id.compare("client-classes") == 0) {
|
||||
@ -637,6 +635,13 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
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,
|
||||
config_pair.second));
|
||||
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
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -7,14 +7,14 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cc/data.h>
|
||||
#include <dhcp4/parser_context.h>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <testutils/io_utils.h>
|
||||
|
||||
using namespace isc::data;
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
namespace test {
|
||||
|
||||
/// @brief compares two JSON trees
|
||||
///
|
||||
@ -128,6 +128,8 @@ TEST(ParserTest, keywordDhcp4) {
|
||||
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) {
|
||||
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
@ -146,7 +148,8 @@ TEST(ParserTest, bashComments) {
|
||||
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\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
"},\n"
|
||||
@ -161,6 +164,7 @@ TEST(ParserTest, cComments) {
|
||||
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
||||
}
|
||||
|
||||
// Tests if bash (#) comments can start anywhere, not just in the first line.
|
||||
TEST(ParserTest, bashCommentsInline) {
|
||||
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
@ -176,6 +180,7 @@ TEST(ParserTest, bashCommentsInline) {
|
||||
testParser(txt, Parser4Context::PARSER_DHCP4, false);
|
||||
}
|
||||
|
||||
// Tests if multi-line C style comments are handled correctly.
|
||||
TEST(ParserTest, multilineComments) {
|
||||
string txt= "{ \"Dhcp4\": { \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
@ -193,89 +198,13 @@ TEST(ParserTest, multilineComments) {
|
||||
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
|
||||
///
|
||||
/// 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 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
|
||||
void testFile(const std::string& fname) {
|
||||
@ -284,8 +213,7 @@ void testFile(const std::string& fname) {
|
||||
|
||||
string decommented = decommentJSONfile(fname);
|
||||
|
||||
cout << "Attempting to load file " << fname << " (" << decommented
|
||||
<< ")" << endl;
|
||||
cout << "Parsing file " << fname << " (" << decommented << ")" << endl;
|
||||
|
||||
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,
|
||||
Parser4Context::ParserType parser_type,
|
||||
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) {
|
||||
// no input
|
||||
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) {
|
||||
// check the 4 possible encodings of solidus '/'
|
||||
ConstElementPtr result;
|
||||
@ -609,3 +542,5 @@ TEST(ParserTest, unicodeSlash) {
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -424,8 +424,7 @@ protected:
|
||||
parser = new StringParser(config_id, string_values_);
|
||||
} else if (config_id.compare("pools") == 0) {
|
||||
parser = new Pools6ListParser(config_id, pools_);
|
||||
} else if (config_id.compare("relay") == 0) {
|
||||
parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
|
||||
// relay has been converted to SimpleParser.
|
||||
} else if (config_id.compare("pd-pools") == 0) {
|
||||
parser = new PdPoolListParser(config_id, pools_);
|
||||
// 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);
|
||||
} else if (config_id.compare("dhcp-ddns") == 0) {
|
||||
parser = new D2ClientConfigParser(config_id);
|
||||
} else if (config_id.compare("mac-sources") == 0) {
|
||||
parser = new MACSourcesListConfigParser(config_id,
|
||||
globalContext());
|
||||
// mac-source has been converted to SimpleParser.
|
||||
} else if (config_id.compare("relay-supplied-options") == 0) {
|
||||
parser = new RSOOListConfigParser(config_id);
|
||||
} else if (config_id.compare("control-socket") == 0) {
|
||||
parser = new ControlSocketParser(config_id);
|
||||
// control-socket has been converted to SimpleParser.
|
||||
} else if (config_id.compare("expired-leases-processing") == 0) {
|
||||
parser = new ExpirationConfigParser();
|
||||
} else if (config_id.compare("client-classes") == 0) {
|
||||
@ -911,6 +907,20 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
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,
|
||||
config_pair.second));
|
||||
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
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -251,6 +251,9 @@ public:
|
||||
ASSERT_TRUE(status);
|
||||
comment_ = parseAnswer(rcode_, status);
|
||||
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
|
||||
@ -762,7 +765,7 @@ TEST_F(Dhcp6ParserTest, emptySubnet) {
|
||||
|
||||
ConstElementPtr status;
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||
|
||||
|
||||
// returned value should be 0 (success)
|
||||
checkResult(status, 0);
|
||||
}
|
||||
@ -4292,16 +4295,15 @@ TEST_F(Dhcp6ParserTest, reservationBogus) {
|
||||
}
|
||||
|
||||
/// The goal of this test is to verify that configuration can include
|
||||
/// MAC/Hardware sources. This test also checks if the aliases are
|
||||
/// handled properly (rfc6939 = client-addr-relay, rfc4649 = remote-id,
|
||||
/// rfc4580 = subscriber-id).
|
||||
TEST_F(Dhcp6ParserTest, macSources) {
|
||||
/// MAC/Hardware sources. This case uses RFC numbers to reference methods.
|
||||
/// Also checks if the aliases are handled properly (rfc6939 = client-addr-relay,
|
||||
/// rfc4649 = remote-id, rfc4580 = subscriber-id).
|
||||
TEST_F(Dhcp6ParserTest, macSources1) {
|
||||
|
||||
ConstElementPtr json;
|
||||
ASSERT_NO_THROW(json =
|
||||
parseDHCP6("{ " + genIfaceConfig() + ","
|
||||
"\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\","
|
||||
"\"client-link-addr-option\", \"remote-id\", \"subscriber-id\"],"
|
||||
"\"mac-sources\": [ \"rfc6939\", \"rfc4649\", \"rfc4580\" ],"
|
||||
"\"preferred-lifetime\": 3000,"
|
||||
"\"rebind-timer\": 2000, "
|
||||
"\"renew-timer\": 1000, "
|
||||
@ -4310,28 +4312,49 @@ TEST_F(Dhcp6ParserTest, macSources) {
|
||||
|
||||
ConstElementPtr status;
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_, json));
|
||||
|
||||
// returned value should be 0 (success)
|
||||
checkResult(status, 0);
|
||||
|
||||
CfgMACSources mac_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]);
|
||||
CfgMACSources sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
|
||||
|
||||
// Let's check if the actual methods are recognized properly.
|
||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_CLIENT_ADDR_RELAY_OPTION, mac_sources[3]);
|
||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_REMOTE_ID, mac_sources[4]);
|
||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_SUBSCRIBER_ID, mac_sources[5]);
|
||||
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 MAC sources configuration can be
|
||||
/// empty.
|
||||
/// Note the Dhcp6 parser requires the list to NOT be empty?!
|
||||
TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
|
||||
/// The goal of this test is to verify that configuration can include
|
||||
/// MAC/Hardware sources. This uses specific method names.
|
||||
TEST_F(Dhcp6ParserTest, macSources2) {
|
||||
|
||||
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;
|
||||
|
||||
EXPECT_NO_THROW(status = configureDhcp6Server(srv_,
|
||||
@ -4343,11 +4366,9 @@ TEST_F(Dhcp6ParserTest, macSourcesEmpty) {
|
||||
"\"subnet6\": [ ], "
|
||||
"\"valid-lifetime\": 4000 }")));
|
||||
|
||||
// returned value should be 0 (success)
|
||||
checkResult(status, 0);
|
||||
|
||||
CfgMACSources mac_sources = CfgMgr::instance().getStagingCfg()->getMACSources().get();
|
||||
EXPECT_EQ(0, mac_sources.size());
|
||||
// returned value should be 1 (failure), because the mac-sources must not
|
||||
// be empty when specified.
|
||||
checkResult(status, 1);
|
||||
}
|
||||
|
||||
/// 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
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
@ -7,28 +7,42 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <cc/data.h>
|
||||
#include <dhcp6/parser_context.h>
|
||||
#include <testutils/io_utils.h>
|
||||
|
||||
using namespace isc::data;
|
||||
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(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());
|
||||
}
|
||||
|
||||
void testParser(const std::string& txt, Parser6Context::ParserType parser_type) {
|
||||
ElementPtr reference_json;
|
||||
/// @brief Tests if the input string can be parsed with specific parser
|
||||
///
|
||||
/// 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;
|
||||
|
||||
ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
|
||||
ASSERT_NO_THROW({
|
||||
try {
|
||||
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.
|
||||
ElementPtr reference_json;
|
||||
ASSERT_NO_THROW(reference_json = Element::fromJSON(txt, true));
|
||||
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) {
|
||||
string txt = "{ \"xyzzy\": { \"foo\": 123, \"baz\": 456 } }";
|
||||
testParser(txt, Parser6Context::PARSER_JSON);
|
||||
@ -125,9 +126,11 @@ TEST(ParserTest, keywordDhcp6) {
|
||||
" \"subnet\": \"2001:db8:1::/48\", "
|
||||
" \"interface\": \"test\" } ],\n"
|
||||
"\"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) {
|
||||
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
@ -144,10 +147,11 @@ TEST(ParserTest, bashComments) {
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"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\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
"},\n"
|
||||
@ -160,9 +164,10 @@ TEST(ParserTest, cComments) {
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"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) {
|
||||
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
@ -176,9 +181,10 @@ TEST(ParserTest, bashCommentsInline) {
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"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) {
|
||||
string txt= "{ \"Dhcp6\": { \"interfaces-config\": {"
|
||||
" \"interfaces\": [ \"*\" ]"
|
||||
@ -193,17 +199,29 @@ TEST(ParserTest, multilineComments) {
|
||||
" \"interface\": \"eth0\""
|
||||
" } ],"
|
||||
"\"valid-lifetime\": 4000 } }";
|
||||
testParser2(txt, Parser6Context::PARSER_DHCP6);
|
||||
testParser(txt, Parser6Context::PARSER_DHCP6, false);
|
||||
}
|
||||
|
||||
|
||||
void testFile(const std::string& fname, bool print) {
|
||||
/// @brief Loads specified example config file
|
||||
///
|
||||
/// 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;
|
||||
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(
|
||||
try {
|
||||
@ -217,9 +235,7 @@ void testFile(const std::string& fname, bool print) {
|
||||
ASSERT_TRUE(reference_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
|
||||
@ -243,10 +259,15 @@ TEST(ParserTest, file) {
|
||||
configs.push_back("stateless.json");
|
||||
|
||||
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,
|
||||
Parser6Context::ParserType parser_type,
|
||||
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) {
|
||||
// no input
|
||||
testError("", Parser6Context::PARSER_JSON,
|
||||
@ -507,9 +528,13 @@ TEST(ParserTest, unicodeEscapes) {
|
||||
ASSERT_EQ(Element::string, result->getType());
|
||||
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 '/'
|
||||
json = "\"/\\/\\u002f\\u002F\"";
|
||||
ConstElementPtr result;
|
||||
string json = "\"/\\/\\u002f\\u002F\"";
|
||||
ASSERT_NO_THROW(
|
||||
try {
|
||||
Parser6Context ctx;
|
||||
@ -520,6 +545,8 @@ TEST(ParserTest, unicodeEscapes) {
|
||||
});
|
||||
ASSERT_EQ(Element::string, result->getType());
|
||||
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
|
||||
// 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.");
|
||||
}
|
||||
|
||||
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
|
||||
// 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_*)
|
||||
///
|
||||
/// Specified source is being added to the mac_sources_ array.
|
||||
/// @todo implement add(string) version of this method.
|
||||
void add(uint32_t source) {
|
||||
mac_sources_.push_back(source);
|
||||
}
|
||||
/// @throw InvalidParameter if such a source is already defined.
|
||||
void add(uint32_t source);
|
||||
|
||||
/// @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
|
||||
// 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(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
|
||||
MACSourcesListConfigParser::build(ConstElementPtr value) {
|
||||
MACSourcesListConfigParser::parse(CfgMACSource& mac_sources, ConstElementPtr value) {
|
||||
CfgIface cfg_iface;
|
||||
uint32_t source = 0;
|
||||
size_t cnt = 0;
|
||||
|
||||
// By default, there's only one source defined: ANY.
|
||||
// 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()) {
|
||||
std::string source_str = source_elem->stringValue();
|
||||
try {
|
||||
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) {
|
||||
isc_throw(DhcpConfigError, "Failed to convert '"
|
||||
<< source_str << "' to any recognized MAC source:"
|
||||
<< ex.what() << " (" << value->getPosition() << ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MACSourcesListConfigParser::commit() {
|
||||
// Nothing to do.
|
||||
if (!cnt) {
|
||||
isc_throw(DhcpConfigError, "If specified, MAC Sources cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
// ******************** ControlSocketParser *************************
|
||||
ControlSocketParser::ControlSocketParser(const std::string& param_name) {
|
||||
if (param_name != "control-socket") {
|
||||
isc_throw(BadValue, "Internal error. Control socket parser called "
|
||||
" for wrong parameter:" << param_name);
|
||||
void ControlSocketParser::parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value) {
|
||||
if (!value) {
|
||||
isc_throw(DhcpConfigError, "Logic error: specified control-socket is null");
|
||||
}
|
||||
}
|
||||
|
||||
void ControlSocketParser::build(isc::data::ConstElementPtr value) {
|
||||
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 { }");
|
||||
}
|
||||
CfgMgr::instance().getStagingCfg()->setControlSocketInfo(value);
|
||||
}
|
||||
|
||||
/// @brief Does nothing.
|
||||
void ControlSocketParser::commit() {
|
||||
// Nothing to do.
|
||||
srv_cfg.setControlSocketInfo(value);
|
||||
}
|
||||
|
||||
// ******************** HooksLibrariesParser *************************
|
||||
@ -804,66 +790,66 @@ OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_l
|
||||
}
|
||||
|
||||
//****************************** RelayInfoParser ********************************
|
||||
RelayInfoParser::RelayInfoParser(const std::string&,
|
||||
const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
|
||||
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");
|
||||
}
|
||||
|
||||
RelayInfoParser::RelayInfoParser(const Option::Universe& family)
|
||||
: family_(family) {
|
||||
};
|
||||
|
||||
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()) {
|
||||
ParserPtr parser(createConfigParser(param.first));
|
||||
parser->build(param.second);
|
||||
parser->commit();
|
||||
if (param.first == "ip-address") {
|
||||
ip_address_specified = true;
|
||||
|
||||
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
|
||||
boost::scoped_ptr<asiolink::IOAddress> ip;
|
||||
try {
|
||||
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_address_specified) {
|
||||
isc_throw(DhcpConfigError, "'relay' specified, but mandatory 'ip-address' "
|
||||
"paramter in it is missing");
|
||||
}
|
||||
|
||||
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")
|
||||
<< " (" << 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_;
|
||||
// Ok, we're done with parsing. Let's store the result in the structure
|
||||
// we were given as configuration storage.
|
||||
*cfg = isc::dhcp::Subnet::RelayInfo(ip);
|
||||
}
|
||||
|
||||
//****************************** PoolsListParser ********************************
|
||||
@ -1069,6 +1055,12 @@ SubnetConfigParser::build(ConstElementPtr subnet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.first == "relay") {
|
||||
RelayInfoParser parser(global_context_->universe_);
|
||||
parser.parse(relay_info_, param.second);
|
||||
continue;
|
||||
}
|
||||
|
||||
ParserPtr parser;
|
||||
// When unsupported parameter is specified, the function called
|
||||
// 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
|
||||
// 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/subnet.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 <cc/simple_parser.h>
|
||||
#include <hooks/libinfo.h>
|
||||
@ -372,56 +374,33 @@ private:
|
||||
/// 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
|
||||
/// methods, see @ref isc::dhcp::Pkt::getMAC.
|
||||
class MACSourcesListConfigParser : public DhcpConfigParser {
|
||||
class MACSourcesListConfigParser : public isc::data::SimpleParser {
|
||||
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
|
||||
///
|
||||
/// Parses configuration entry (list of sources) and adds each element
|
||||
/// to the sources list.
|
||||
///
|
||||
/// @param value pointer to the content of parsed values
|
||||
virtual void build(isc::data::ConstElementPtr value);
|
||||
|
||||
/// @brief Does nothing.
|
||||
virtual void commit();
|
||||
|
||||
private:
|
||||
|
||||
// Parsed parameter name
|
||||
std::string param_name_;
|
||||
|
||||
/// Global parser context.
|
||||
ParserContextPtr global_context_;
|
||||
void parse(CfgMACSource& mac_sources, isc::data::ConstElementPtr value);
|
||||
};
|
||||
|
||||
/// @brief Parser for the control-socket structure
|
||||
///
|
||||
/// It does not parse anything, simply stores the element in
|
||||
/// the staging config.
|
||||
class ControlSocketParser : public DhcpConfigParser {
|
||||
class ControlSocketParser : public isc::data::SimpleParser {
|
||||
public:
|
||||
|
||||
ControlSocketParser(const std::string& param_name);
|
||||
|
||||
/// @brief Stores contents of the control-socket map in the staging config.
|
||||
/// @brief "Parses" control-socket structure
|
||||
///
|
||||
/// 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
|
||||
virtual void build(isc::data::ConstElementPtr value);
|
||||
|
||||
/// @brief Does nothing.
|
||||
virtual void commit();
|
||||
void parse(SrvConfig& srv_cfg, isc::data::ConstElementPtr value);
|
||||
};
|
||||
|
||||
/// @brief Parser for hooks library list
|
||||
@ -849,48 +828,24 @@ protected:
|
||||
/// 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.
|
||||
class RelayInfoParser : public DhcpConfigParser {
|
||||
class RelayInfoParser : public isc::data::SimpleParser {
|
||||
public:
|
||||
|
||||
/// @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)
|
||||
RelayInfoParser(const std::string& unused,
|
||||
const isc::dhcp::Subnet::RelayInfoPtr& relay_info,
|
||||
const isc::dhcp::Option::Universe& family);
|
||||
RelayInfoParser(const isc::dhcp::Option::Universe& family);
|
||||
|
||||
/// @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:
|
||||
/// -# ip-address
|
||||
///
|
||||
/// @param parser is the "item_name" for a specific member element of
|
||||
/// the "relay" specification.
|
||||
///
|
||||
/// @return returns a pointer to newly created parser.
|
||||
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_;
|
||||
/// @param cfg configuration will be stored here
|
||||
/// @param relay_info JSON structure holding relay parameters to parse
|
||||
void parse(const isc::dhcp::Subnet::RelayInfoPtr& cfg,
|
||||
isc::data::ConstElementPtr relay_info);
|
||||
|
||||
protected:
|
||||
/// Protocol family (IPv4 or IPv6)
|
||||
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
|
||||
// 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);
|
||||
}
|
||||
|
||||
/// @brief Check MACSourcesListConfigParser basic functionality
|
||||
///
|
||||
/// 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);
|
||||
/// Verifies the code that parses mac sources and adds them to CfgMgr
|
||||
TEST_F(DhcpParserTest, MacSources) {
|
||||
|
||||
// That's an equivalent of the following snippet:
|
||||
// "mac-sources: [ \"duid\", \"ipv6\" ]";
|
||||
ElementPtr config = Element::createList();
|
||||
config->add(Element::create("duid"));
|
||||
config->add(Element::create("ipv6-link-local"));
|
||||
ElementPtr values = Element::createList();
|
||||
values->add(Element::create("duid"));
|
||||
values->add(Element::create("ipv6-link-local"));
|
||||
|
||||
boost::scoped_ptr<MACSourcesListConfigParser>
|
||||
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.
|
||||
// Let's grab server configuration from CfgMgr
|
||||
SrvConfigPtr cfg = CfgMgr::instance().getStagingCfg();
|
||||
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());
|
||||
EXPECT_EQ(HWAddr::HWADDR_SOURCE_DUID, configured_sources[0]);
|
||||
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
|
||||
/// configuration parsing. This is essentially the same structure provided
|
||||
/// by dhcp servers.
|
||||
@ -2416,6 +2463,19 @@ TEST_F(ParseConfigTest, validRelayInfo4) {
|
||||
" }";
|
||||
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)
|
||||
std::string config_str_bogus1 =
|
||||
" {"
|
||||
@ -2430,24 +2490,25 @@ TEST_F(ParseConfigTest, validRelayInfo4) {
|
||||
" }";
|
||||
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.
|
||||
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
|
||||
EXPECT_NO_THROW(parser.reset(new RelayInfoParser("ignored", result,
|
||||
Option::V4)));
|
||||
EXPECT_NO_THROW(parser->build(json));
|
||||
EXPECT_NO_THROW(parser->commit());
|
||||
// wrong family type
|
||||
EXPECT_THROW(parser.parse(result, json_bogus1), DhcpConfigError);
|
||||
|
||||
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)
|
||||
EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
|
||||
|
||||
// Let's check negative scenario (too large byte values in pseudo-IPv4 addr)
|
||||
EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
|
||||
// Mandatory ip-address is missing. What a pity.
|
||||
EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
|
||||
}
|
||||
|
||||
/// @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);
|
||||
|
||||
// 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
|
||||
std::string config_str_bogus1 =
|
||||
" {"
|
||||
@ -2474,23 +2547,25 @@ TEST_F(ParseConfigTest, validRelayInfo6) {
|
||||
" }";
|
||||
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.
|
||||
Subnet::RelayInfoPtr result(new Subnet::RelayInfo(asiolink::IOAddress("::")));
|
||||
|
||||
boost::shared_ptr<RelayInfoParser> parser;
|
||||
// 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());
|
||||
RelayInfoParser parser(Option::V6);
|
||||
|
||||
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)
|
||||
EXPECT_THROW(parser->build(json_bogus1), DhcpConfigError);
|
||||
// Looks like IPv6 address, but has too many colons
|
||||
EXPECT_THROW(parser.parse(result, json_bogus2), DhcpConfigError);
|
||||
|
||||
// Unparseable text that looks like IPv6 address, but has too many colons
|
||||
EXPECT_THROW(parser->build(json_bogus2), DhcpConfigError);
|
||||
// Mandatory ip-address is missing. What a pity.
|
||||
EXPECT_THROW(parser.parse(result, json_bogus3), DhcpConfigError);
|
||||
}
|
||||
|
||||
// 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <testutils/io_utils.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <fstream>
|
||||
@ -36,7 +37,78 @@ std::string readFile(const std::string& file_path) {
|
||||
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 namespace
|
||||
}; // end of isc namespace
|
||||
|
||||
|
@ -26,6 +26,20 @@ bool fileExists(const std::string& file_path);
|
||||
/// @return File contents.
|
||||
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 namespace
|
||||
}; // end of isc namespace
|
||||
|
Loading…
x
Reference in New Issue
Block a user