mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-01 06:25:34 +00:00
[master] Merge branch 'trac5039' (SimpleParser)
# Conflicts: # src/bin/dhcp4/Makefile.am # src/bin/dhcp4/json_config_parser.cc # src/bin/dhcp6/Makefile.am # src/bin/dhcp6/dhcp6.dox # src/bin/dhcp6/json_config_parser.cc # src/bin/dhcp6/tests/Makefile.am
This commit is contained in:
@@ -1578,22 +1578,12 @@ It is merely echoed by the server
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara><command>csv-format</command> - if this value is not specified
|
||||
and the definition for the particular option exists, the server will assume
|
||||
that the option data is specified as a list of comma separated values to be
|
||||
assigned to individual fields of the DHCP option. If the definition
|
||||
does not exist for this option, the server will assume that the data
|
||||
parameter contains the option payload in the binary format (represented
|
||||
as a string of hexadecimal digits). Note that not specifying this
|
||||
parameter doesn't imply that it defaults to a fixed value, but
|
||||
the configuration data interpretation also depends on the presence
|
||||
of the option definition. An administrator must be aware if the
|
||||
definition for the particular option exists when this parameter
|
||||
is not specified. It is generally recommended to not specify this
|
||||
parameter only for the options for which the definition exists, e.g.
|
||||
standard options. Setting <command>csv-format</command> to an explicit
|
||||
value will cause the server to strictly check the format of the option
|
||||
data specified.
|
||||
<simpara><command>csv-format</command> - if this value is not
|
||||
specified the server will assume that the option data is specified as
|
||||
a list of comma separated values to be assigned to individual fields
|
||||
of the DHCP option. This behavior has changed in Kea 1.2. Older
|
||||
versions used additional logic to determined whether the csv-format
|
||||
should be true or false. That is no longer the case.
|
||||
</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
@@ -1721,22 +1721,12 @@ should include options from the isc option space:
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<simpara><command>csv-format</command> - if this value is not specified
|
||||
and the definition for the particular option exists, the server will assume
|
||||
that the option data is specified as a list of comma separated values to be
|
||||
assigned to individual fields of the DHCP option. If the definition
|
||||
does not exist for this option, the server will assume that the data
|
||||
parameter contains the option payload in the binary format (represented
|
||||
as a string of hexadecimal digits). Note that not specifying this
|
||||
parameter doesn't imply that it defaults to a fixed value, but
|
||||
the configuration data interpretation also depends on the presence
|
||||
of the option definition. An administrator must be aware if the
|
||||
definition for the particular option exists when this parameter
|
||||
is not specified. It is generally recommended to not specify this
|
||||
parameter only for the options for which the definition exists, e.g.
|
||||
standard options. Setting <command>csv-format</command> to an explicit
|
||||
value will cause the server to strictly check the format of the option
|
||||
data specified.
|
||||
<simpara><command>csv-format</command> - if this value is not
|
||||
specified the server will assume that the option data is specified as
|
||||
a list of comma separated values to be assigned to individual fields
|
||||
of the DHCP option. This behavior has changed in Kea 1.2. Older
|
||||
versions used additional logic to determined whether the csv-format
|
||||
should be true or false. That is no longer the case.
|
||||
</simpara>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
@@ -62,12 +62,11 @@ libdhcp4_la_SOURCES += json_config_parser.cc json_config_parser.h
|
||||
libdhcp4_la_SOURCES += dhcp4_log.cc dhcp4_log.h
|
||||
libdhcp4_la_SOURCES += dhcp4_srv.cc dhcp4_srv.h
|
||||
libdhcp4_la_SOURCES += dhcp4to6_ipc.cc dhcp4to6_ipc.h
|
||||
|
||||
libdhcp4_la_SOURCES += dhcp4_lexer.ll location.hh position.hh stack.hh
|
||||
libdhcp4_la_SOURCES += dhcp4_parser.cc dhcp4_parser.h
|
||||
libdhcp4_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
|
||||
|
||||
libdhcp4_la_SOURCES += kea_controller.cc
|
||||
libdhcp4_la_SOURCES += simple_parser4.cc simple_parser4.h
|
||||
|
||||
nodist_libdhcp4_la_SOURCES = dhcp4_messages.h dhcp4_messages.cc
|
||||
EXTRA_DIST += dhcp4_messages.mes
|
||||
|
@@ -24,6 +24,9 @@ component implementation.
|
||||
|
||||
@section dhcpv4ConfigParser Configuration Parser in DHCPv4
|
||||
|
||||
Note: parsers are currently being migrated to @ref isc::data::SimpleParser. See
|
||||
@ref ccSimpleParser page for details.
|
||||
|
||||
The common configuration parsers for the DHCP servers are located in the
|
||||
src/lib/dhcpsrv/parsers/ directory. Parsers specific to the DHCPv4 component
|
||||
are located in the src/bin/dhcp4/json_config_parser.cc. These parsers derive
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
#include <cc/command_interpreter.h>
|
||||
#include <dhcp4/dhcp4_log.h>
|
||||
#include <dhcp4/simple_parser4.h>
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp/option_definition.h>
|
||||
#include <dhcpsrv/cfg_option.h>
|
||||
@@ -190,8 +191,7 @@ protected:
|
||||
parser = new Pools4ListParser(config_id, pools_);
|
||||
} else if (config_id.compare("relay") == 0) {
|
||||
parser = new RelayInfoParser(config_id, relay_info_, Option::V4);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id, options_, AF_INET);
|
||||
// option-data has been converted to SimpleParser already.
|
||||
} else if (config_id.compare("match-client-id") == 0) {
|
||||
parser = new BooleanParser(config_id, boolean_values_);
|
||||
} else if (config_id.compare("4o6-subnet") == 0) {
|
||||
@@ -424,10 +424,7 @@ DhcpConfigParser* createGlobalDhcp4ConfigParser(const std::string& config_id,
|
||||
parser = new IfacesConfigParser4();
|
||||
} else if (config_id.compare("subnet4") == 0) {
|
||||
parser = new Subnets4ListConfigParser(config_id);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET);
|
||||
} else if (config_id.compare("option-def") == 0) {
|
||||
parser = new OptionDefListParser(config_id, globalContext());
|
||||
// option-data and option-def have been converted to SimpleParser already.
|
||||
} else if ((config_id.compare("next-server") == 0)) {
|
||||
parser = new StringParser(config_id,
|
||||
globalContext()->string_values_);
|
||||
@@ -537,7 +534,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
// Please do not change this order!
|
||||
ParserCollection independent_parsers;
|
||||
ParserPtr subnet_parser;
|
||||
ParserPtr option_parser;
|
||||
ParserPtr iface_parser;
|
||||
ParserPtr leases_parser;
|
||||
ParserPtr client_classes_parser;
|
||||
@@ -566,10 +562,40 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
// the name of the failing parser can be retrieved in the "catch" clause.
|
||||
ConfigPair config_pair;
|
||||
try {
|
||||
|
||||
// This is a way to convert ConstElementPtr to ElementPtr.
|
||||
// We need a config that can be edited, because we will insert
|
||||
// default values and will insert derived values as well.
|
||||
ElementPtr mutable_cfg = Element::getMutableMap(config_set);
|
||||
|
||||
// Set all default values if not specified by the user.
|
||||
SimpleParser4::setAllDefaults(mutable_cfg);
|
||||
|
||||
// We need definitions first
|
||||
ConstElementPtr option_defs = mutable_cfg->get("option-def");
|
||||
if (option_defs) {
|
||||
OptionDefListParser parser;
|
||||
CfgOptionDefPtr cfg_option_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
|
||||
parser.parse(cfg_option_def, option_defs);
|
||||
}
|
||||
|
||||
// Make parsers grouping.
|
||||
const std::map<std::string, ConstElementPtr>& values_map =
|
||||
config_set->mapValue();
|
||||
mutable_cfg->mapValue();
|
||||
BOOST_FOREACH(config_pair, values_map) {
|
||||
|
||||
if (config_pair.first == "option-def") {
|
||||
// This is converted to SimpleParser and is handled already above.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config_pair.first == "option-data") {
|
||||
OptionDataListParser parser(AF_INET);
|
||||
CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
|
||||
parser.parse(cfg_option, config_pair.second);
|
||||
continue;
|
||||
}
|
||||
|
||||
ParserPtr parser(createGlobalDhcp4ConfigParser(config_pair.first,
|
||||
config_pair.second));
|
||||
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_DETAIL, DHCP4_PARSER_CREATED)
|
||||
@@ -578,8 +604,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
subnet_parser = parser;
|
||||
} else if (config_pair.first == "lease-database") {
|
||||
leases_parser = parser;
|
||||
} else if (config_pair.first == "option-data") {
|
||||
option_parser = parser;
|
||||
} else if (config_pair.first == "interfaces-config") {
|
||||
// The interface parser is independent from any other
|
||||
// parser and can be run here before any other parsers.
|
||||
@@ -605,15 +629,6 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set) {
|
||||
}
|
||||
}
|
||||
|
||||
// The option values parser is the next one to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator option_config =
|
||||
values_map.find("option-data");
|
||||
if (option_config != values_map.end()) {
|
||||
config_pair.first = "option-data";
|
||||
option_parser->build(option_config->second);
|
||||
option_parser->commit();
|
||||
}
|
||||
|
||||
// The class definitions parser is the next one to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator cc_config =
|
||||
values_map.find("client-classes");
|
||||
|
106
src/bin/dhcp4/simple_parser4.cc
Normal file
106
src/bin/dhcp4/simple_parser4.cc
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <dhcp4/simple_parser4.h>
|
||||
#include <cc/data.h>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace isc::data;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
/// @brief This sets of arrays define the default values and
|
||||
/// values inherited (derived) between various scopes.
|
||||
///
|
||||
/// Each of those is documented in @file simple_parser4.cc. This
|
||||
/// is different than most other comments in Kea code. The reason
|
||||
/// for placing those in .cc rather than .h file is that it
|
||||
/// is expected to be one centralized place to look at for
|
||||
/// the default values. This is expected to be looked at also by
|
||||
/// people who are not skilled in C or C++, so they may be
|
||||
/// confused with the differences between declaration and definition.
|
||||
/// As such, there's one file to look at that hopefully is readable
|
||||
/// without any C or C++ skills.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// @brief This table defines default values for option definitions in DHCPv4.
|
||||
///
|
||||
/// Dhcp4 may contain an array called option-def that enumerates new option
|
||||
/// definitions. This array lists default values for those option definitions.
|
||||
const SimpleDefaults SimpleParser4::OPTION4_DEF_DEFAULTS = {
|
||||
{ "record-types", Element::string, ""},
|
||||
{ "space", Element::string, "dhcp4"},
|
||||
{ "array", Element::boolean, "false"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// @brief This table defines default values for options in DHCPv4.
|
||||
///
|
||||
/// Dhcp4 usually contains option values (option-data) defined in global,
|
||||
/// subnet, class or host reservations scopes. This array lists default values
|
||||
/// for those option-data declarations.
|
||||
const SimpleDefaults SimpleParser4::OPTION4_DEFAULTS = {
|
||||
{ "space", Element::string, "dhcp4"},
|
||||
{ "csv-format", Element::boolean, "true"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// @brief This table defines default global values for DHCPv4
|
||||
///
|
||||
/// Some of the global parameters defined in the global scope (i.e. directly
|
||||
/// in Dhcp4) are optional. If not defined, the following values will be
|
||||
/// used.
|
||||
const SimpleDefaults SimpleParser4::GLOBAL4_DEFAULTS = {
|
||||
{ "renew-timer", Element::integer, "900" },
|
||||
{ "rebind-timer", Element::integer, "1800" },
|
||||
{ "valid-lifetime", Element::integer, "7200" }
|
||||
};
|
||||
|
||||
/// @brief List of parameters that can be inherited from the global to subnet4 scope.
|
||||
///
|
||||
/// Some parameters may be defined on both global (directly in Dhcp4) and
|
||||
/// subnet (Dhcp4/subnet4/...) scope. If not defined in the subnet scope,
|
||||
/// the value is being inherited (derived) from the global scope. This
|
||||
/// array lists all of such parameters.
|
||||
const ParamsList SimpleParser4::INHERIT_GLOBAL_TO_SUBNET4 = {
|
||||
"renew-timer",
|
||||
"rebind-timer",
|
||||
"valid-lifetime"
|
||||
};
|
||||
/// @}
|
||||
|
||||
/// ---------------------------------------------------------------------------
|
||||
/// --- end of default values -------------------------------------------------
|
||||
/// ---------------------------------------------------------------------------
|
||||
|
||||
size_t SimpleParser4::setAllDefaults(isc::data::ElementPtr global) {
|
||||
size_t cnt = 0;
|
||||
|
||||
// Set global defaults first.
|
||||
cnt = setDefaults(global, GLOBAL4_DEFAULTS);
|
||||
|
||||
// Now set option definition defaults for each specified option definition
|
||||
ConstElementPtr option_defs = global->get("option-def");
|
||||
if (option_defs) {
|
||||
BOOST_FOREACH(ElementPtr option_def, option_defs->listValue()) {
|
||||
cnt += SimpleParser::setDefaults(option_def, OPTION4_DEF_DEFAULTS);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, set the defaults for option data
|
||||
ConstElementPtr options = global->get("option-data");
|
||||
if (options) {
|
||||
BOOST_FOREACH(ElementPtr single_option, options->listValue()) {
|
||||
cnt += SimpleParser::setDefaults(single_option, OPTION4_DEFAULTS);
|
||||
}
|
||||
}
|
||||
|
||||
return (cnt);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
40
src/bin/dhcp4/simple_parser4.h
Normal file
40
src/bin/dhcp4/simple_parser4.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef SIMPLE_PARSER4_H
|
||||
#define SIMPLE_PARSER4_H
|
||||
|
||||
#include <cc/simple_parser.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief SimpleParser specialized for DHCPv4
|
||||
///
|
||||
/// This class is a @ref isc::data::SimpleParser dedicated to DHCPv4 parser.
|
||||
/// In particular, it contains all the default values and names of the
|
||||
/// parameters that are to be derived (inherited) between scopes.
|
||||
/// For the actual values, see @file simple_parser4.cc
|
||||
class SimpleParser4 : public isc::data::SimpleParser {
|
||||
public:
|
||||
/// @brief Sets all defaults for DHCPv4 configuration
|
||||
///
|
||||
/// This method sets global, option data and option definitions defaults.
|
||||
///
|
||||
/// @param global scope to be filled in with defaults.
|
||||
/// @return number of default values added
|
||||
static size_t setAllDefaults(isc::data::ElementPtr global);
|
||||
|
||||
// see simple_parser4.cc for comments for those parameters
|
||||
static const isc::data::SimpleDefaults OPTION4_DEF_DEFAULTS;
|
||||
static const isc::data::SimpleDefaults OPTION4_DEFAULTS;
|
||||
static const isc::data::SimpleDefaults GLOBAL4_DEFAULTS;
|
||||
static const isc::data::ParamsList INHERIT_GLOBAL_TO_SUBNET4;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
#endif
|
@@ -94,6 +94,7 @@ dhcp4_unittests_SOURCES += out_of_range_unittest.cc
|
||||
dhcp4_unittests_SOURCES += decline_unittest.cc
|
||||
dhcp4_unittests_SOURCES += kea_controller_unittest.cc
|
||||
dhcp4_unittests_SOURCES += dhcp4to6_ipc_unittest.cc
|
||||
dhcp4_unittests_SOURCES += simple_parser4_unittest.cc
|
||||
|
||||
nodist_dhcp4_unittests_SOURCES = marker_file.h test_libraries.h
|
||||
|
||||
|
@@ -662,8 +662,9 @@ TEST_F(Dhcp4ParserTest, unspecifiedRenewTimer) {
|
||||
Subnet4Ptr subnet = CfgMgr::instance().getStagingCfg()->
|
||||
getCfgSubnets4()->selectSubnet(IOAddress("192.0.2.200"));
|
||||
ASSERT_TRUE(subnet);
|
||||
EXPECT_TRUE(subnet->getT1().unspecified());
|
||||
EXPECT_FALSE(subnet->getT1().unspecified());
|
||||
EXPECT_FALSE(subnet->getT2().unspecified());
|
||||
EXPECT_EQ(900, subnet->getT1()); // that's the default value
|
||||
EXPECT_EQ(2000, subnet->getT2());
|
||||
EXPECT_EQ(4000, subnet->getValid());
|
||||
|
||||
@@ -699,7 +700,8 @@ TEST_F(Dhcp4ParserTest, unspecifiedRebindTimer) {
|
||||
ASSERT_TRUE(subnet);
|
||||
EXPECT_FALSE(subnet->getT1().unspecified());
|
||||
EXPECT_EQ(1000, subnet->getT1());
|
||||
EXPECT_TRUE(subnet->getT2().unspecified());
|
||||
EXPECT_FALSE(subnet->getT2().unspecified());
|
||||
EXPECT_EQ(1800, subnet->getT2()); // that's the default value
|
||||
EXPECT_EQ(4000, subnet->getValid());
|
||||
|
||||
// Check that subnet-id is 1
|
||||
|
@@ -203,7 +203,7 @@ const char* HOST_CONFIGS[] = {
|
||||
"\"valid-lifetime\": 600,"
|
||||
"\"option-data\": [ {"
|
||||
" \"name\": \"vivso-suboptions\","
|
||||
" \"data\": 4491"
|
||||
" \"data\": \"4491\""
|
||||
"},"
|
||||
"{"
|
||||
" \"name\": \"tftp-servers\","
|
||||
@@ -221,7 +221,7 @@ const char* HOST_CONFIGS[] = {
|
||||
" \"ip-address\": \"10.0.0.7\","
|
||||
" \"option-data\": [ {"
|
||||
" \"name\": \"vivso-suboptions\","
|
||||
" \"data\": 4491"
|
||||
" \"data\": \"4491\""
|
||||
" },"
|
||||
" {"
|
||||
" \"name\": \"tftp-servers\","
|
||||
|
91
src/bin/dhcp4/tests/simple_parser4_unittest.cc
Normal file
91
src/bin/dhcp4/tests/simple_parser4_unittest.cc
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <config.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <dhcp4/simple_parser4.h>
|
||||
#include <cc/data.h>
|
||||
|
||||
using namespace isc::data;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
namespace test {
|
||||
|
||||
/// @brief DHCP Parser test fixture class
|
||||
class SimpleParser4Test : public ::testing::Test {
|
||||
public:
|
||||
/// @brief Checks if specified map has an integer parameter with expected value
|
||||
///
|
||||
/// @param map map to be checked
|
||||
/// @param param_name name of the parameter to be checked
|
||||
/// @param exp_value expected value of the parameter.
|
||||
void checkIntegerValue(const ConstElementPtr& map, const std::string& param_name,
|
||||
int64_t exp_value) {
|
||||
|
||||
// First check if the passed element is a map.
|
||||
ASSERT_EQ(Element::map, map->getType());
|
||||
|
||||
// Now try to get the element being checked
|
||||
ConstElementPtr elem = map->get(param_name);
|
||||
ASSERT_TRUE(elem);
|
||||
|
||||
// Now check if it's indeed integer
|
||||
ASSERT_EQ(Element::integer, elem->getType());
|
||||
|
||||
// Finally, check if its value meets expectation.
|
||||
EXPECT_EQ(exp_value, elem->intValue());
|
||||
}
|
||||
};
|
||||
|
||||
// This test checks if global defaults are properly set for DHCPv4.
|
||||
TEST_F(SimpleParser4Test, globalDefaults4) {
|
||||
|
||||
ElementPtr empty = Element::fromJSON("{ }");
|
||||
size_t num = 0;
|
||||
|
||||
EXPECT_NO_THROW(num = SimpleParser4::setAllDefaults(empty));
|
||||
|
||||
|
||||
// We expect at least 3 parameters to be inserted.
|
||||
EXPECT_TRUE(num >= 3);
|
||||
|
||||
checkIntegerValue(empty, "valid-lifetime", 7200);
|
||||
checkIntegerValue(empty, "rebind-timer", 1800);
|
||||
checkIntegerValue(empty, "renew-timer", 900);
|
||||
|
||||
// Make sure that preferred-lifetime is not set for v4 (it's v6 only
|
||||
// parameter)
|
||||
EXPECT_FALSE(empty->get("preferred-lifetime"));
|
||||
}
|
||||
|
||||
// This test checks if the parameters can be inherited from the global
|
||||
// scope to the subnet scope.
|
||||
TEST_F(SimpleParser4Test, inheritGlobalToSubnet4) {
|
||||
ElementPtr global = Element::fromJSON("{ \"renew-timer\": 1,"
|
||||
" \"rebind-timer\": 2,"
|
||||
" \"preferred-lifetime\": 3,"
|
||||
" \"valid-lifetime\": 4"
|
||||
"}");
|
||||
ElementPtr subnet = Element::fromJSON("{ \"renew-timer\": 100 }");
|
||||
|
||||
// we should inherit 3 parameters. Renew-timer should remain intact,
|
||||
// as it was already defined in the subnet scope.
|
||||
size_t num;
|
||||
EXPECT_NO_THROW(num = SimpleParser4::deriveParams(global, subnet,
|
||||
SimpleParser4::INHERIT_GLOBAL_TO_SUBNET4));
|
||||
EXPECT_EQ(2, num);
|
||||
|
||||
// Check the values. 2 of them are inherited, while the third one
|
||||
// was already defined in the subnet, so should not be inherited.
|
||||
checkIntegerValue(subnet, "renew-timer", 100);
|
||||
checkIntegerValue(subnet, "rebind-timer", 2);
|
||||
checkIntegerValue(subnet, "valid-lifetime", 4);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
};
|
@@ -63,12 +63,11 @@ libdhcp6_la_SOURCES += dhcp6_srv.cc dhcp6_srv.h
|
||||
libdhcp6_la_SOURCES += ctrl_dhcp6_srv.cc ctrl_dhcp6_srv.h
|
||||
libdhcp6_la_SOURCES += json_config_parser.cc json_config_parser.h
|
||||
libdhcp6_la_SOURCES += dhcp6to4_ipc.cc dhcp6to4_ipc.h
|
||||
|
||||
libdhcp6_la_SOURCES += dhcp6_lexer.ll location.hh position.hh stack.hh
|
||||
libdhcp6_la_SOURCES += dhcp6_parser.cc dhcp6_parser.h
|
||||
libdhcp6_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
|
||||
|
||||
libdhcp6_la_SOURCES += kea_controller.cc
|
||||
libdhcp6_la_SOURCES += simple_parser6.cc simple_parser6.h
|
||||
|
||||
nodist_libdhcp6_la_SOURCES = dhcp6_messages.h dhcp6_messages.cc
|
||||
EXTRA_DIST += dhcp6_messages.mes
|
||||
|
@@ -34,7 +34,9 @@ the new parser, here's how you use it:
|
||||
// can be replaced with this:
|
||||
Parser6Context parser;
|
||||
json = parser.parseFile(file_name, Parser6Context::PARSER_DHCP6);
|
||||
@endcode
|
||||
|
||||
Note: parsers are currently being migrated to @ref isc::data::SimpleParser. See
|
||||
@ref ccSimpleParser page for details.
|
||||
|
||||
The common configuration parsers for the DHCP servers are located in the
|
||||
src/lib/dhcpsrv/parsers/ directory. Parsers specific to the DHCPv6 component
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <dhcp/libdhcp++.h>
|
||||
#include <dhcp6/json_config_parser.h>
|
||||
#include <dhcp6/dhcp6_log.h>
|
||||
#include <dhcp6/simple_parser6.h>
|
||||
#include <dhcp/iface_mgr.h>
|
||||
#include <dhcpsrv/cfg_option.h>
|
||||
#include <dhcpsrv/cfgmgr.h>
|
||||
@@ -184,10 +185,12 @@ public:
|
||||
uint32_values_));
|
||||
parser = code_parser;
|
||||
} else if (entry == "option-data") {
|
||||
OptionDataListParserPtr option_parser(new OptionDataListParser(entry,
|
||||
options_,
|
||||
AF_INET6));
|
||||
parser = option_parser;
|
||||
OptionDataListParser opts_parser(AF_INET6);
|
||||
opts_parser.parse(options_, param.second);
|
||||
|
||||
// OptionDataListParser is converted to SimpleParser already,
|
||||
// no need to go through build/commit phases.
|
||||
continue;
|
||||
} else if (entry == "user-context") {
|
||||
user_context_ = param.second;
|
||||
continue; // no parser to remember, simply store the value
|
||||
@@ -425,8 +428,7 @@ protected:
|
||||
parser = new RelayInfoParser(config_id, relay_info_, Option::V6);
|
||||
} else if (config_id.compare("pd-pools") == 0) {
|
||||
parser = new PdPoolListParser(config_id, pools_);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id, options_, AF_INET6);
|
||||
// option-data was here, but it is now converted to SimpleParser
|
||||
} else if (config_id.compare("rapid-commit") == 0) {
|
||||
parser = new BooleanParser(config_id, boolean_values_);
|
||||
} else {
|
||||
@@ -703,10 +705,11 @@ DhcpConfigParser* createGlobal6DhcpConfigParser(const std::string& config_id,
|
||||
parser = new IfacesConfigParser6();
|
||||
} else if (config_id.compare("subnet6") == 0) {
|
||||
parser = new Subnets6ListConfigParser(config_id);
|
||||
} else if (config_id.compare("option-data") == 0) {
|
||||
parser = new OptionDataListParser(config_id, CfgOptionPtr(), AF_INET6);
|
||||
} else if (config_id.compare("option-def") == 0) {
|
||||
parser = new OptionDefListParser(config_id, globalContext());
|
||||
// option-data and option-def are no longer needed here. They're now
|
||||
// converted to SimpleParser and are handled in configureDhcp6Server
|
||||
} else if (config_id.compare("version") == 0) {
|
||||
parser = new StringParser(config_id,
|
||||
globalContext()->string_values_);
|
||||
} else if (config_id.compare("lease-database") == 0) {
|
||||
parser = new DbAccessParser(config_id, DbAccessParser::LEASE_DB);
|
||||
} else if (config_id.compare("hosts-database") == 0) {
|
||||
@@ -805,7 +808,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
// Please do not change this order!
|
||||
ParserCollection independent_parsers;
|
||||
ParserPtr subnet_parser;
|
||||
ParserPtr option_parser;
|
||||
ParserPtr iface_parser;
|
||||
ParserPtr leases_parser;
|
||||
ParserPtr client_classes_parser;
|
||||
@@ -835,10 +837,39 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
ConfigPair config_pair;
|
||||
try {
|
||||
|
||||
// This is a way to convert ConstElementPtr to ElementPtr.
|
||||
// We need a config that can be edited, because we will insert
|
||||
// default values and will insert derived values as well.
|
||||
ElementPtr mutable_cfg = Element::getMutableMap(config_set);
|
||||
|
||||
SimpleParser6::setAllDefaults(mutable_cfg);
|
||||
|
||||
// Make parsers grouping.
|
||||
const std::map<std::string, ConstElementPtr>& values_map =
|
||||
config_set->mapValue();
|
||||
mutable_cfg->mapValue();
|
||||
|
||||
// We need definitions first
|
||||
ConstElementPtr option_defs = mutable_cfg->get("option-def");
|
||||
if (option_defs) {
|
||||
OptionDefListParser parser;
|
||||
CfgOptionDefPtr cfg_option_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
|
||||
parser.parse(cfg_option_def, option_defs);
|
||||
}
|
||||
|
||||
BOOST_FOREACH(config_pair, values_map) {
|
||||
|
||||
if (config_pair.first == "option-def") {
|
||||
// This is converted to SimpleParser and is handled already above.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (config_pair.first == "option-data") {
|
||||
OptionDataListParser parser(AF_INET6);
|
||||
CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
|
||||
parser.parse(cfg_option, config_pair.second);
|
||||
continue;
|
||||
}
|
||||
|
||||
ParserPtr parser(createGlobal6DhcpConfigParser(config_pair.first,
|
||||
config_pair.second));
|
||||
LOG_DEBUG(dhcp6_logger, DBG_DHCP6_DETAIL, DHCP6_PARSER_CREATED)
|
||||
@@ -847,8 +878,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
subnet_parser = parser;
|
||||
} else if (config_pair.first == "lease-database") {
|
||||
leases_parser = parser;
|
||||
} else if (config_pair.first == "option-data") {
|
||||
option_parser = parser;
|
||||
} else if (config_pair.first == "hooks-libraries") {
|
||||
// Executing the commit will alter currently loaded hooks
|
||||
// libraries. Check if the supplied libraries are valid,
|
||||
@@ -875,15 +904,6 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set) {
|
||||
}
|
||||
}
|
||||
|
||||
// The option values parser is the next one to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator option_config =
|
||||
values_map.find("option-data");
|
||||
if (option_config != values_map.end()) {
|
||||
config_pair.first = "option-data";
|
||||
option_parser->build(option_config->second);
|
||||
option_parser->commit();
|
||||
}
|
||||
|
||||
// The class definitions parser is the next one to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator cc_config =
|
||||
values_map.find("client-classes");
|
||||
|
108
src/bin/dhcp6/simple_parser6.cc
Normal file
108
src/bin/dhcp6/simple_parser6.cc
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <dhcp6/simple_parser6.h>
|
||||
#include <cc/data.h>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace isc::data;
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
/// @brief This sets of arrays define the default values and
|
||||
/// values inherited (derived) between various scopes.
|
||||
///
|
||||
/// Each of those is documented in @file simple_parser6.cc. This
|
||||
/// is different than most other comments in Kea code. The reason
|
||||
/// for placing those in .cc rather than .h file is that it
|
||||
/// is expected to be one centralized place to look at for
|
||||
/// the default values. This is expected to be looked at also by
|
||||
/// people who are not skilled in C or C++, so they may be
|
||||
/// confused with the differences between declaration and definition.
|
||||
/// As such, there's one file to look at that hopefully is readable
|
||||
/// without any C or C++ skills.
|
||||
///
|
||||
/// @{
|
||||
|
||||
/// @brief This table defines default values for option definitions in DHCPv6.
|
||||
///
|
||||
/// Dhcp6 may contain an array called option-def that enumerates new option
|
||||
/// definitions. This array lists default values for those option definitions.
|
||||
const SimpleDefaults SimpleParser6::OPTION6_DEF_DEFAULTS = {
|
||||
{ "record-types", Element::string, ""},
|
||||
{ "space", Element::string, "dhcp6"},
|
||||
{ "array", Element::boolean, "false"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// @brief This table defines default values for options in DHCPv6.
|
||||
///
|
||||
/// Dhcp6 usually contains option values (option-data) defined in global,
|
||||
/// subnet, class or host reservations scopes. This array lists default values
|
||||
/// for those option-data declarations.
|
||||
const SimpleDefaults SimpleParser6::OPTION6_DEFAULTS = {
|
||||
{ "space", Element::string, "dhcp6"},
|
||||
{ "csv-format", Element::boolean, "true"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// @brief This table defines default global values for DHCPv6
|
||||
///
|
||||
/// Some of the global parameters defined in the global scope (i.e. directly
|
||||
/// in Dhcp6) are optional. If not defined, the following values will be
|
||||
/// used.
|
||||
const SimpleDefaults SimpleParser6::GLOBAL6_DEFAULTS = {
|
||||
{ "renew-timer", Element::integer, "900" },
|
||||
{ "rebind-timer", Element::integer, "1800" },
|
||||
{ "preferred-lifetime", Element::integer, "3600" },
|
||||
{ "valid-lifetime", Element::integer, "7200" }
|
||||
};
|
||||
|
||||
/// @brief List of parameters that can be inherited from the global to subnet6 scope.
|
||||
///
|
||||
/// Some parameters may be defined on both global (directly in Dhcp6) and
|
||||
/// subnet (Dhcp6/subnet6/...) scope. If not defined in the subnet scope,
|
||||
/// the value is being inherited (derived) from the global scope. This
|
||||
/// array lists all of such parameters.
|
||||
const ParamsList SimpleParser6::INHERIT_GLOBAL_TO_SUBNET6 = {
|
||||
"renew-timer",
|
||||
"rebind-timer",
|
||||
"preferred-lifetime",
|
||||
"valid-lifetime"
|
||||
};
|
||||
/// @}
|
||||
|
||||
/// ---------------------------------------------------------------------------
|
||||
/// --- end of default values -------------------------------------------------
|
||||
/// ---------------------------------------------------------------------------
|
||||
|
||||
size_t SimpleParser6::setAllDefaults(isc::data::ElementPtr global) {
|
||||
size_t cnt = 0;
|
||||
|
||||
// Set global defaults first.
|
||||
cnt = setDefaults(global, GLOBAL6_DEFAULTS);
|
||||
|
||||
// Now set the defaults for each specified option definition
|
||||
ConstElementPtr option_defs = global->get("option-def");
|
||||
if (option_defs) {
|
||||
BOOST_FOREACH(ElementPtr option_def, option_defs->listValue()) {
|
||||
cnt += SimpleParser::setDefaults(option_def, OPTION6_DEF_DEFAULTS);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, set the defaults for option data
|
||||
ConstElementPtr options = global->get("option-data");
|
||||
if (options) {
|
||||
BOOST_FOREACH(ElementPtr single_option, options->listValue()) {
|
||||
cnt += SimpleParser::setDefaults(single_option, OPTION6_DEFAULTS);
|
||||
}
|
||||
}
|
||||
|
||||
return (cnt);
|
||||
}
|
||||
|
||||
};
|
||||
};
|
42
src/bin/dhcp6/simple_parser6.h
Normal file
42
src/bin/dhcp6/simple_parser6.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef SIMPLE_PARSER6_H
|
||||
#define SIMPLE_PARSER6_H
|
||||
|
||||
#include <cc/simple_parser.h>
|
||||
|
||||
namespace isc {
|
||||
namespace dhcp {
|
||||
|
||||
/// @brief SimpleParser specialized for DHCPv6
|
||||
///
|
||||
/// This class is a @ref isc::data::SimpleParser dedicated to DHCPv6 parser.
|
||||
/// In particular, it contains all the default values and names of the
|
||||
/// parameters that are to be derived (inherited) between scopes.
|
||||
/// For the actual values, see @file simple_parser6.cc
|
||||
class SimpleParser6 : public isc::data::SimpleParser {
|
||||
public:
|
||||
|
||||
/// @brief Sets all defaults for DHCPv6 configuration
|
||||
///
|
||||
/// This method sets global, option data and option definitions defaults.
|
||||
///
|
||||
/// @param global scope to be filled in with defaults.
|
||||
/// @return number of default values added
|
||||
static size_t setAllDefaults(isc::data::ElementPtr global);
|
||||
|
||||
// see simple_parser6.cc for comments for those parameters
|
||||
static const isc::data::SimpleDefaults OPTION6_DEF_DEFAULTS;
|
||||
static const isc::data::SimpleDefaults OPTION6_DEFAULTS;
|
||||
static const isc::data::SimpleDefaults GLOBAL6_DEFAULTS;
|
||||
static const isc::data::ParamsList INHERIT_GLOBAL_TO_SUBNET6;
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@@ -95,6 +95,7 @@ dhcp6_unittests_SOURCES += kea_controller_unittest.cc
|
||||
dhcp6_unittests_SOURCES += dhcp6to4_ipc_unittest.cc
|
||||
dhcp6_unittests_SOURCES += classify_unittests.cc
|
||||
dhcp6_unittests_SOURCES += parser_unittest.cc
|
||||
dhcp6_unittests_SOURCES += simple_parser6_unittest.cc
|
||||
|
||||
nodist_dhcp6_unittests_SOURCES = marker_file.h test_libraries.h
|
||||
|
||||
|
@@ -2030,6 +2030,7 @@ TEST_F(Dhcpv6SrvTest, rsooOverride) {
|
||||
" } ],"
|
||||
" \"option-data\": [ {"
|
||||
" \"code\": 120,"
|
||||
" \"csv-format\": false,"
|
||||
" \"data\": \"05\""
|
||||
" } ],"
|
||||
" \"preferred-lifetime\": 3000,"
|
||||
|
@@ -229,7 +229,7 @@ const char* CONFIGS[] = {
|
||||
"\"renew-timer\": 1000, "
|
||||
"\"option-data\": [ {"
|
||||
" \"name\": \"vendor-opts\","
|
||||
" \"data\": 4491"
|
||||
" \"data\": \"4491\""
|
||||
"},"
|
||||
"{"
|
||||
" \"name\": \"tftp-servers\","
|
||||
@@ -247,7 +247,7 @@ const char* CONFIGS[] = {
|
||||
" \"ip-addresses\": [ \"2001:db8:1::2\" ],"
|
||||
" \"option-data\": [ {"
|
||||
" \"name\": \"vendor-opts\","
|
||||
" \"data\": 4491"
|
||||
" \"data\": \"4491\""
|
||||
" },"
|
||||
" {"
|
||||
" \"name\": \"tftp-servers\","
|
||||
|
87
src/bin/dhcp6/tests/simple_parser6_unittest.cc
Normal file
87
src/bin/dhcp6/tests/simple_parser6_unittest.cc
Normal file
@@ -0,0 +1,87 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <config.h>
|
||||
#include <cc/data.h>
|
||||
#include <dhcp6/simple_parser6.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace isc;
|
||||
using namespace isc::data;
|
||||
using namespace isc::dhcp;
|
||||
|
||||
namespace {
|
||||
|
||||
/// @brief DHCP Parser test fixture class
|
||||
class SimpleParser6Test : public ::testing::Test {
|
||||
public:
|
||||
/// @brief Checks if specified map has an integer parameter with expected value
|
||||
///
|
||||
/// @param map map to be checked
|
||||
/// @param param_name name of the parameter to be checked
|
||||
/// @param exp_value expected value of the parameter.
|
||||
void checkIntegerValue(const ConstElementPtr& map, const std::string& param_name,
|
||||
int64_t exp_value) {
|
||||
|
||||
// First check if the passed element is a map.
|
||||
ASSERT_EQ(Element::map, map->getType());
|
||||
|
||||
// Now try to get the element being checked
|
||||
ConstElementPtr elem = map->get(param_name);
|
||||
ASSERT_TRUE(elem);
|
||||
|
||||
// Now check if it's indeed integer
|
||||
ASSERT_EQ(Element::integer, elem->getType());
|
||||
|
||||
// Finally, check if its value meets expectation.
|
||||
EXPECT_EQ(exp_value, elem->intValue());
|
||||
}
|
||||
};
|
||||
|
||||
// This test checks if global defaults are properly set for DHCPv6.
|
||||
TEST_F(SimpleParser6Test, globalDefaults6) {
|
||||
|
||||
ElementPtr empty = Element::fromJSON("{ }");
|
||||
size_t num = 0;
|
||||
|
||||
EXPECT_NO_THROW(num = SimpleParser6::setAllDefaults(empty));
|
||||
|
||||
// We expect at least 4 parameters to be inserted.
|
||||
EXPECT_TRUE(num >= 4);
|
||||
|
||||
checkIntegerValue(empty, "valid-lifetime", 7200);
|
||||
checkIntegerValue(empty, "preferred-lifetime", 3600);
|
||||
checkIntegerValue(empty, "rebind-timer", 1800);
|
||||
checkIntegerValue(empty, "renew-timer", 900);
|
||||
}
|
||||
|
||||
// This test checks if the parameters can be inherited from the global
|
||||
// scope to the subnet scope.
|
||||
TEST_F(SimpleParser6Test, inheritGlobalToSubnet6) {
|
||||
ElementPtr global = Element::fromJSON("{ \"renew-timer\": 1,"
|
||||
" \"rebind-timer\": 2,"
|
||||
" \"preferred-lifetime\": 3,"
|
||||
" \"valid-lifetime\": 4"
|
||||
"}");
|
||||
ElementPtr subnet = Element::fromJSON("{ \"renew-timer\": 100 }");
|
||||
|
||||
// we should inherit 3 parameters. Renew-timer should remain intact,
|
||||
// as it was already defined in the subnet scope.
|
||||
size_t num;
|
||||
EXPECT_NO_THROW(num = SimpleParser::deriveParams(global, subnet,
|
||||
SimpleParser6::INHERIT_GLOBAL_TO_SUBNET6));
|
||||
EXPECT_EQ(3, num);
|
||||
|
||||
// Check the values. 3 of them are inherited, while the fourth one
|
||||
// was already defined in the subnet, so should not be inherited.
|
||||
checkIntegerValue(subnet, "renew-timer", 100);
|
||||
checkIntegerValue(subnet, "rebind-timer", 2);
|
||||
checkIntegerValue(subnet, "preferred-lifetime", 3);
|
||||
checkIntegerValue(subnet, "valid-lifetime", 4);
|
||||
}
|
||||
|
||||
};
|
||||
|
@@ -7,6 +7,7 @@ AM_CXXFLAGS = $(KEA_CXXFLAGS)
|
||||
lib_LTLIBRARIES = libkea-cc.la
|
||||
libkea_cc_la_SOURCES = data.cc data.h
|
||||
libkea_cc_la_SOURCES += command_interpreter.cc command_interpreter.h
|
||||
libkea_cc_la_SOURCES += simple_parser.cc simple_parser.h
|
||||
|
||||
libkea_cc_la_LIBADD = $(top_builddir)/src/lib/exceptions/libkea-exceptions.la
|
||||
libkea_cc_la_LIBADD += $(BOOST_LIBS)
|
||||
@@ -18,4 +19,6 @@ libkea_cc_la_LDFLAGS = -no-undefined -version-info 1:0:0
|
||||
libkea_cc_includedir = $(pkgincludedir)/cc
|
||||
libkea_cc_include_HEADERS = data.h
|
||||
|
||||
EXTRA_DIST = cc.dox
|
||||
|
||||
CLEANFILES = *.gcno *.gcda
|
||||
|
99
src/lib/cc/cc.dox
Normal file
99
src/lib/cc/cc.dox
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
/**
|
||||
@page libcc libcc - Kea configuration commands library
|
||||
|
||||
@section ccSimpleParser Simple JSON Parser
|
||||
|
||||
Since the early beginnings, our configuration parsing code was a mess. It
|
||||
started back in 2011 when Tomek joined ISC recently and was told to implement
|
||||
Kea configuration handling in similar way as DNS Auth module. The code grew
|
||||
over time (DHCP configuration is significantly more complex than DNS, with
|
||||
more interdependent values) and as of Kea 1.1 release it became very difficult
|
||||
to manage. The decision has been made to significantly refactor or even
|
||||
partially rewrite the parser code. The design for this effort is documented
|
||||
here: http://kea.isc.org/wiki/SimpleParser It discusses the original issues
|
||||
and the proposed architecture.
|
||||
|
||||
There are several aspects of this new approach. The base class for all parsers
|
||||
is @ref isc::data::SimpleParser. It simplifies the parsers based on
|
||||
@ref isc::dhcp::DhcpConfigParser by rejecting the
|
||||
concept of build/commit phases. Instead, there should be a single method
|
||||
called parse that takes ConstElementPtr as a single parameter (that's the
|
||||
JSON structures to be parsed) and returns the config structure to be used
|
||||
in CfgMgr. An example of such a method can be the following:
|
||||
|
||||
@code
|
||||
std::pair<OptionDescriptor, std::string>
|
||||
OptionDataParser::parse(isc::data::ConstElementPtr single_option)
|
||||
@endcode
|
||||
|
||||
Since each derived class will have the same parameter, but a different return
|
||||
type, it's not possible to use virtual methods mechanism. That's perfectly
|
||||
ok, though, as there is only a single instance of the class needed to parse
|
||||
arbitrary number of parameters of the same type. There is no need to
|
||||
keep pointers to the parser object. As such there's fewer incentives to have
|
||||
one generic way to handle all parsers.
|
||||
|
||||
@subsection ccSimpleParserDefaults Default values in Simple Parser
|
||||
|
||||
Another simplification comes from the fact that almost all parameters
|
||||
are mandatory in SimpleParser. One source of complexities in the old
|
||||
parser was the necessity to deal with optional parameters. Simple
|
||||
parser deals with that by explicitly requiring the input structure to
|
||||
have all parameters filled. Obviously, it's not feasible to expect
|
||||
everyone to always specify all parameters, therefore there's an easy
|
||||
way to fill missing parameters with their default values. There are
|
||||
several methods to do this, but the most generic one is:
|
||||
|
||||
@code
|
||||
static size_t
|
||||
isc::data::SimpleParser::setDefaults(isc::data::ElementPtr scope,
|
||||
const SimpleDefaults& default_values);
|
||||
@endcode
|
||||
|
||||
It takes a pointer to element to be filled with default values and
|
||||
vector of default values. Having those values specified in a single
|
||||
place in a way that can easily be read even by non-programmers is a
|
||||
big advantage of this approach. Here's an example from simple_parser.cc file:
|
||||
|
||||
@code
|
||||
/// This table defines default values for option definitions in DHCPv6
|
||||
const SimpleDefaults OPTION6_DEF_DEFAULTS = {
|
||||
{ "record-types", Element::string, ""},
|
||||
{ "space", Element::string, "dhcp6"},
|
||||
{ "array", Element::boolean, "false"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
@endcode
|
||||
|
||||
This array (which technically is implemented as a vector and
|
||||
initialized the C++11 way) can be passed to the aforementioned
|
||||
setDefaults. That code will iterate over all default values and see if
|
||||
there are explicit values provided. If not, the gaps will be filled
|
||||
with default values. There are also convenience methods specified for
|
||||
filling in option data defaults, option definition defaults and
|
||||
setAllDefaults that sets all defaults (starts with global, but then
|
||||
walks down the Element tree and fills defaults in subsequent scopes).
|
||||
|
||||
@subsection ccSimpleParserInherits Inheriting parameters between scopes
|
||||
|
||||
SimpleParser provides a mechanism to inherit parameters between scopes,
|
||||
e.g. to inherit global parameters in the subnet scope if more specific
|
||||
values are not defined in the subnet scope. This is achieved by calling
|
||||
@code
|
||||
static size_t SimpleParser::deriveParams(isc::data::ConstElementPtr parent,
|
||||
isc::data::ElementPtr child,
|
||||
const ParamsList& params);
|
||||
|
||||
@endcode
|
||||
|
||||
ParamsList is a simple vector<string>. There will be more specific
|
||||
methods implemented in the future, but for the time being only
|
||||
@ref isc::data::SimpleParser::deriveParams is implemented.
|
||||
|
||||
*/
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2010-2016 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
|
||||
@@ -86,7 +86,7 @@ Element::getValue(std::string&) const {
|
||||
}
|
||||
|
||||
bool
|
||||
Element::getValue(std::vector<ConstElementPtr>&) const {
|
||||
Element::getValue(std::vector<ElementPtr>&) const {
|
||||
return (false);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ Element::setValue(const std::string&) {
|
||||
}
|
||||
|
||||
bool
|
||||
Element::setValue(const std::vector<ConstElementPtr>&) {
|
||||
Element::setValue(const std::vector<ElementPtr>&) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
@@ -130,13 +130,18 @@ Element::get(const int) const {
|
||||
throwTypeError("get(int) called on a non-list Element");
|
||||
}
|
||||
|
||||
ElementPtr
|
||||
Element::getNonConst(const int) const {
|
||||
throwTypeError("get(int) called on a non-list Element");
|
||||
}
|
||||
|
||||
void
|
||||
Element::set(const size_t, ConstElementPtr) {
|
||||
Element::set(const size_t, ElementPtr) {
|
||||
throwTypeError("set(int, element) called on a non-list Element");
|
||||
}
|
||||
|
||||
void
|
||||
Element::add(ConstElementPtr) {
|
||||
Element::add(ElementPtr) {
|
||||
throwTypeError("add() called on a non-list Element");
|
||||
}
|
||||
|
||||
@@ -507,7 +512,7 @@ fromStringstreamList(std::istream& in, const std::string& file, int& line,
|
||||
{
|
||||
int c = 0;
|
||||
ElementPtr list = Element::createList(Element::Position(file, line, pos));
|
||||
ConstElementPtr cur_list_element;
|
||||
ElementPtr cur_list_element;
|
||||
|
||||
skipChars(in, WHITESPACE, line, pos);
|
||||
while (c != EOF && c != ']') {
|
||||
@@ -810,8 +815,8 @@ void
|
||||
ListElement::toJSON(std::ostream& ss) const {
|
||||
ss << "[ ";
|
||||
|
||||
const std::vector<ConstElementPtr>& v = listValue();
|
||||
for (std::vector<ConstElementPtr>::const_iterator it = v.begin();
|
||||
const std::vector<ElementPtr>& v = listValue();
|
||||
for (std::vector<ElementPtr>::const_iterator it = v.begin();
|
||||
it != v.end(); ++it) {
|
||||
if (it != v.begin()) {
|
||||
ss << ", ";
|
||||
@@ -1079,5 +1084,14 @@ void Element::preprocess(std::istream& in, std::stringstream& out) {
|
||||
}
|
||||
}
|
||||
|
||||
ElementPtr Element::getMutableMap(ConstElementPtr& const_map) {
|
||||
std::map<std::string, ConstElementPtr> values;
|
||||
const_map->getValue(values);
|
||||
ElementPtr mutable_map(new MapElement());
|
||||
mutable_map->setValue(values);
|
||||
|
||||
return (mutable_map);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC")
|
||||
// Copyright (C) 2010-2016 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
|
||||
@@ -216,7 +216,7 @@ public:
|
||||
{ throwTypeError("boolValue() called on non-Bool Element"); };
|
||||
virtual std::string stringValue() const
|
||||
{ throwTypeError("stringValue() called on non-string Element"); };
|
||||
virtual const std::vector<ConstElementPtr>& listValue() const {
|
||||
virtual const std::vector<ElementPtr>& listValue() const {
|
||||
// replace with real exception or empty vector?
|
||||
throwTypeError("listValue() called on non-list Element");
|
||||
};
|
||||
@@ -239,7 +239,7 @@ public:
|
||||
virtual bool getValue(double& t) const;
|
||||
virtual bool getValue(bool& t) const;
|
||||
virtual bool getValue(std::string& t) const;
|
||||
virtual bool getValue(std::vector<ConstElementPtr>& t) const;
|
||||
virtual bool getValue(std::vector<ElementPtr>& t) const;
|
||||
virtual bool getValue(std::map<std::string, ConstElementPtr>& t) const;
|
||||
//@}
|
||||
|
||||
@@ -259,7 +259,7 @@ public:
|
||||
virtual bool setValue(const double v);
|
||||
virtual bool setValue(const bool t);
|
||||
virtual bool setValue(const std::string& v);
|
||||
virtual bool setValue(const std::vector<ConstElementPtr>& v);
|
||||
virtual bool setValue(const std::vector<ElementPtr>& v);
|
||||
virtual bool setValue(const std::map<std::string, ConstElementPtr>& v);
|
||||
//@}
|
||||
|
||||
@@ -277,15 +277,21 @@ public:
|
||||
/// \param i The position of the ElementPtr to return
|
||||
virtual ConstElementPtr get(const int i) const;
|
||||
|
||||
/// \brief returns element as non-const pointer
|
||||
///
|
||||
/// \param i The position of the ElementPtr to retrieve
|
||||
/// \return specified element pointer
|
||||
virtual ElementPtr getNonConst(const int i) const;
|
||||
|
||||
/// Sets the ElementPtr at the given index. If the index is out
|
||||
/// of bounds, this function throws an std::out_of_range exception.
|
||||
/// \param i The position of the ElementPtr to set
|
||||
/// \param element The ElementPtr to set at the position
|
||||
virtual void set(const size_t i, ConstElementPtr element);
|
||||
virtual void set(const size_t i, ElementPtr element);
|
||||
|
||||
/// Adds an ElementPtr to the list
|
||||
/// \param element The ElementPtr to add
|
||||
virtual void add(ConstElementPtr element);
|
||||
virtual void add(ElementPtr element);
|
||||
|
||||
/// Removes the element at the given position. If the index is out
|
||||
/// of nothing happens.
|
||||
@@ -521,6 +527,12 @@ public:
|
||||
/// \return ElementPtr with the data that is parsed.
|
||||
static ElementPtr fromWire(const std::string& s);
|
||||
//@}
|
||||
|
||||
/// @brief Creates mutable map based on const map
|
||||
///
|
||||
/// @param const_map const map to be used as a donor
|
||||
/// @return mutable map
|
||||
static ElementPtr getMutableMap(ConstElementPtr& const_map);
|
||||
};
|
||||
|
||||
/// Notes: IntElement type is changed to int64_t.
|
||||
@@ -603,29 +615,30 @@ public:
|
||||
};
|
||||
|
||||
class ListElement : public Element {
|
||||
std::vector<ConstElementPtr> l;
|
||||
std::vector<ElementPtr> l;
|
||||
|
||||
public:
|
||||
ListElement(const Position& pos = ZERO_POSITION())
|
||||
: Element(list, pos) {}
|
||||
const std::vector<ConstElementPtr>& listValue() const { return (l); }
|
||||
const std::vector<ElementPtr>& listValue() const { return (l); }
|
||||
using Element::getValue;
|
||||
bool getValue(std::vector<ConstElementPtr>& t) const {
|
||||
bool getValue(std::vector<ElementPtr>& t) const {
|
||||
t = l;
|
||||
return (true);
|
||||
}
|
||||
using Element::setValue;
|
||||
bool setValue(const std::vector<ConstElementPtr>& v) {
|
||||
bool setValue(const std::vector<ElementPtr>& v) {
|
||||
l = v;
|
||||
return (true);
|
||||
}
|
||||
using Element::get;
|
||||
ConstElementPtr get(int i) const { return (l.at(i)); }
|
||||
ElementPtr getNonConst(int i) const { return (l.at(i)); }
|
||||
using Element::set;
|
||||
void set(size_t i, ConstElementPtr e) {
|
||||
void set(size_t i, ElementPtr e) {
|
||||
l.at(i) = e;
|
||||
}
|
||||
void add(ConstElementPtr e) { l.push_back(e); };
|
||||
void add(ElementPtr e) { l.push_back(e); };
|
||||
using Element::remove;
|
||||
void remove(int i) { l.erase(l.begin() + i); };
|
||||
void toJSON(std::ostream& ss) const;
|
||||
|
175
src/lib/cc/simple_parser.cc
Normal file
175
src/lib/cc/simple_parser.cc
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <cc/simple_parser.h>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cc/data.h>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace isc {
|
||||
namespace data {
|
||||
|
||||
std::string
|
||||
SimpleParser::getString(isc::data::ConstElementPtr scope, const std::string& name) {
|
||||
ConstElementPtr x = scope->get(name);
|
||||
if (!x) {
|
||||
isc_throw(BadValue, "Element " << name << " not found");
|
||||
}
|
||||
if (x->getType() != Element::string) {
|
||||
isc_throw(BadValue, "Element " << name << " found, but is not a string");
|
||||
}
|
||||
|
||||
return (x->stringValue());
|
||||
}
|
||||
|
||||
int64_t
|
||||
SimpleParser::getInteger(isc::data::ConstElementPtr scope, const std::string& name) {
|
||||
ConstElementPtr x = scope->get(name);
|
||||
if (!x) {
|
||||
isc_throw(BadValue, "Element " << name << " not found");
|
||||
}
|
||||
if (x->getType() != Element::integer) {
|
||||
isc_throw(BadValue, "Element " << name << " found, but is not an integer");
|
||||
}
|
||||
|
||||
return (x->intValue());
|
||||
}
|
||||
|
||||
bool
|
||||
SimpleParser::getBoolean(isc::data::ConstElementPtr scope, const std::string& name) {
|
||||
ConstElementPtr x = scope->get(name);
|
||||
if (!x) {
|
||||
isc_throw(BadValue, "Element " << name << " not found");
|
||||
}
|
||||
if (x->getType() != Element::boolean) {
|
||||
isc_throw(BadValue, "Element " << name << " found, but is not a boolean");
|
||||
}
|
||||
|
||||
return (x->boolValue());
|
||||
}
|
||||
|
||||
const data::Element::Position&
|
||||
SimpleParser::getPosition(const std::string& name, const data::ConstElementPtr parent) {
|
||||
if (!parent) {
|
||||
return (data::Element::ZERO_POSITION());
|
||||
}
|
||||
ConstElementPtr elem = parent->get(name);
|
||||
if (!elem) {
|
||||
return (data::Element::ZERO_POSITION());
|
||||
}
|
||||
return (elem->getPosition());
|
||||
}
|
||||
|
||||
size_t SimpleParser::setDefaults(isc::data::ElementPtr scope,
|
||||
const SimpleDefaults& default_values) {
|
||||
size_t cnt = 0;
|
||||
|
||||
// This is the position representing a default value. As the values
|
||||
// we're inserting here are not present in whatever the config file
|
||||
// came from, we need to make sure it's clearly labeled as default.
|
||||
const Element::Position pos("<default-value>", 0, 0);
|
||||
|
||||
// Let's go over all parameters we have defaults for.
|
||||
BOOST_FOREACH(SimpleDefault def_value, default_values) {
|
||||
|
||||
// Try if such a parameter is there. If it is, let's
|
||||
// skip it, because user knows best *cough*.
|
||||
ConstElementPtr x = scope->get(string(def_value.name_));
|
||||
if (x) {
|
||||
// There is such a value already, skip it.
|
||||
continue;
|
||||
}
|
||||
|
||||
// There isn't such a value defined, let's create the default
|
||||
// value...
|
||||
switch (def_value.type_) {
|
||||
case Element::string: {
|
||||
x.reset(new StringElement(def_value.value_, pos));
|
||||
break;
|
||||
}
|
||||
case Element::integer: {
|
||||
int int_value = boost::lexical_cast<int>(def_value.value_);
|
||||
x.reset(new IntElement(int_value, pos));
|
||||
break;
|
||||
}
|
||||
case Element::boolean: {
|
||||
bool bool_value;
|
||||
if (def_value.value_ == string("true")) {
|
||||
bool_value = true;
|
||||
} else if (def_value.value_ == string("false")) {
|
||||
bool_value = false;
|
||||
} else {
|
||||
isc_throw(BadValue, "Internal error. Boolean value specified as "
|
||||
<< def_value.value_ << ", expected true or false");
|
||||
}
|
||||
x.reset(new BoolElement(bool_value, pos));
|
||||
break;
|
||||
}
|
||||
case Element::real: {
|
||||
double dbl_value = boost::lexical_cast<double>(def_value.value_);
|
||||
x.reset(new DoubleElement(dbl_value, pos));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// No default values for null, list or map
|
||||
isc_throw(BadValue, "Internal error. Incorrect default value type.");
|
||||
}
|
||||
|
||||
// ... and insert it into the provided Element tree.
|
||||
scope->set(def_value.name_, x);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return (cnt);
|
||||
}
|
||||
|
||||
size_t
|
||||
SimpleParser::setListDefaults(isc::data::ElementPtr list,
|
||||
const SimpleDefaults& default_values) {
|
||||
size_t cnt = 0;
|
||||
BOOST_FOREACH(ElementPtr entry, list->listValue()) {
|
||||
cnt += setDefaults(entry, default_values);
|
||||
}
|
||||
return (cnt);
|
||||
}
|
||||
|
||||
size_t
|
||||
SimpleParser::deriveParams(isc::data::ConstElementPtr parent,
|
||||
isc::data::ElementPtr child,
|
||||
const ParamsList& params) {
|
||||
if ( (parent->getType() != Element::map) ||
|
||||
(child->getType() != Element::map)) {
|
||||
return (0);
|
||||
}
|
||||
|
||||
size_t cnt = 0;
|
||||
BOOST_FOREACH(string param, params) {
|
||||
ConstElementPtr x = parent->get(param);
|
||||
if (!x) {
|
||||
// Parent doesn't define this parameter, so there's
|
||||
// nothing to derive from
|
||||
continue;
|
||||
}
|
||||
|
||||
if (child->get(param)) {
|
||||
// Child defines this parameter already. There's
|
||||
// nothing to do here.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Copy the parameters to the child scope.
|
||||
child->set(param, x);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return (cnt);
|
||||
}
|
||||
|
||||
}; // end of isc::dhcp namespace
|
||||
}; // end of isc namespace
|
149
src/lib/cc/simple_parser.h
Normal file
149
src/lib/cc/simple_parser.h
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#ifndef SIMPLE_PARSER_H
|
||||
#define SIMPLE_PARSER_H
|
||||
|
||||
#include <cc/data.h>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace isc {
|
||||
namespace data {
|
||||
|
||||
/// This array defines a single entry of default values
|
||||
struct SimpleDefault {
|
||||
SimpleDefault(const char* name, isc::data::Element::types type, const char* value)
|
||||
:name_(name), type_(type), value_(value) {}
|
||||
std::string name_;
|
||||
const isc::data::Element::types type_;
|
||||
const char* value_;
|
||||
};
|
||||
|
||||
/// This specifies all default values in a given scope (e.g. a subnet)
|
||||
typedef std::vector<SimpleDefault> SimpleDefaults;
|
||||
|
||||
/// This defines a list of all parameters that are derived (or inherited) between
|
||||
/// contexts
|
||||
typedef std::vector<std::string> ParamsList;
|
||||
|
||||
|
||||
/// @brief A simple parser
|
||||
///
|
||||
/// This class is intended to be a simpler replacement for
|
||||
/// @ref isc::dhcp::DhcpConfigParser.
|
||||
/// The simplification comes from several factors:
|
||||
/// - no build/commit nonsense. There's a single step:
|
||||
/// CfgStorage parse(ConstElementPtr json)
|
||||
/// that converts JSON configuration into an object and returns it.
|
||||
/// - almost no state kept. The only state kept in most cases is whether the
|
||||
/// parsing is done in v4 or v6 context. This greatly simplifies the
|
||||
/// parsers (no contexts, no child parsers list, no separate storage for
|
||||
/// uint32, strings etc. In fact, there's so little state kept, that this
|
||||
/// parser is mostly a collection of static methods.
|
||||
/// - no optional parameters (all are mandatory). This simplifies the parser,
|
||||
/// but introduces a new step before parsing where we insert the default
|
||||
/// values into client configuration before parsing. This is actually a good
|
||||
/// thing, because we now have a clear picture of the default parameters as
|
||||
/// they're defined in a single place (the DhcpConfigParser had the defaults
|
||||
/// spread out in multiple files in multiple directories).
|
||||
class SimpleParser {
|
||||
public:
|
||||
|
||||
/// @brief Derives (inherits) parameters from parent scope to a child
|
||||
///
|
||||
/// This method derives parameters from the parent scope to the child,
|
||||
/// if there are no values specified in the child scope. For example,
|
||||
/// this method can be used to derive timers from global scope (e.g. for
|
||||
/// the whole DHCPv6 server) to a subnet scope. This method checks
|
||||
/// if the child scope doesn't have more specific values defined. If
|
||||
/// it doesn't, then the value from parent scope is copied over.
|
||||
///
|
||||
/// @param parent scope to copy from (e.g. global)
|
||||
/// @param child scope to copy from (e.g. subnet)
|
||||
/// @param params names of the parameters to copy
|
||||
/// @return number of parameters copied
|
||||
static size_t deriveParams(isc::data::ConstElementPtr parent,
|
||||
isc::data::ElementPtr child,
|
||||
const ParamsList& params);
|
||||
|
||||
/// @brief Sets the default values
|
||||
///
|
||||
/// This method sets the default values for parameters that are not
|
||||
/// defined. The list of default values is specified by default_values.
|
||||
/// If not present, those will be inserted into the scope. If
|
||||
/// a parameter is already present, the default value will not
|
||||
/// be inserted.
|
||||
///
|
||||
/// @param scope default values will be inserted here
|
||||
/// @param default_values list of default values
|
||||
/// @return number of parameters inserted
|
||||
static size_t setDefaults(isc::data::ElementPtr scope,
|
||||
const SimpleDefaults& default_values);
|
||||
|
||||
/// @brief Sets the default values for all entries in a list
|
||||
///
|
||||
/// This is a simple utility method that iterates over all
|
||||
/// parameters in a list and calls setDefaults for each
|
||||
/// entry.
|
||||
///
|
||||
/// @param list list to be iterated over
|
||||
/// @param default_values list of default values
|
||||
/// @return number of parameters inserted
|
||||
static size_t setListDefaults(isc::data::ElementPtr list,
|
||||
const SimpleDefaults& default_values);
|
||||
|
||||
/// @brief Utility method that returns position of an element
|
||||
///
|
||||
/// It's mostly useful for logging.
|
||||
///
|
||||
/// @param name position of that element will be returned
|
||||
/// @param parent parent element (optional)
|
||||
/// @return position of the element specified.
|
||||
static const data::Element::Position&
|
||||
getPosition(const std::string& name, const data::ConstElementPtr parent =
|
||||
data::ConstElementPtr());
|
||||
|
||||
protected:
|
||||
|
||||
/// @brief Returns a string parameter from a scope
|
||||
///
|
||||
/// Unconditionally returns a parameter. If the parameter is not there or
|
||||
/// is not of appropriate type, BadValue exception is thrown.
|
||||
///
|
||||
/// @param scope specified parameter will be extracted from this scope
|
||||
/// @param name name of the parameter
|
||||
/// @return a string value of the parameter
|
||||
static std::string getString(isc::data::ConstElementPtr scope,
|
||||
const std::string& name);
|
||||
|
||||
/// @brief Returns an integer parameter from a scope
|
||||
///
|
||||
/// Unconditionally returns a parameter. If the parameter is not there or
|
||||
/// is not of appropriate type, BadValue exception is thrown.
|
||||
///
|
||||
/// @param scope specified parameter will be extracted from this scope
|
||||
/// @param name name of the parameter
|
||||
/// @return an integer value of the parameter
|
||||
static int64_t getInteger(isc::data::ConstElementPtr scope,
|
||||
const std::string& name);
|
||||
|
||||
/// @brief Returns a boolean parameter from a scope
|
||||
///
|
||||
/// Unconditionally returns a parameter. If the parameter is not there or
|
||||
/// is not of appropriate type, BadValue exception is thrown.
|
||||
///
|
||||
/// @param scope specified parameter will be extracted from this scope
|
||||
/// @param name name of the parameter
|
||||
/// @return a boolean value of the parameter
|
||||
static bool getBoolean(isc::data::ConstElementPtr scope,
|
||||
const std::string& name);
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@@ -16,6 +16,7 @@ if HAVE_GTEST
|
||||
TESTS += run_unittests
|
||||
run_unittests_SOURCES = command_interpreter_unittests.cc data_unittests.cc
|
||||
run_unittests_SOURCES += data_file_unittests.cc run_unittests.cc
|
||||
run_unittests_SOURCES += simple_parser_unittest.cc
|
||||
run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
|
||||
run_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
|
||||
|
||||
|
@@ -206,7 +206,7 @@ testGetValueInt() {
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::vector<ElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::create(1);
|
||||
@@ -266,7 +266,7 @@ testGetValueDouble() {
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::vector<ElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::create(1.1);
|
||||
@@ -293,7 +293,7 @@ testGetValueBool() {
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::vector<ElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::create(true);
|
||||
@@ -320,7 +320,7 @@ testGetValueString() {
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::vector<ElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::create("foo");
|
||||
@@ -347,7 +347,7 @@ testGetValueList() {
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::vector<ElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::createList();
|
||||
@@ -374,7 +374,7 @@ testGetValueMap() {
|
||||
double d;
|
||||
bool b;
|
||||
std::string s;
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::vector<ElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
|
||||
el = Element::createMap();
|
||||
@@ -402,7 +402,7 @@ TEST(Element, create_and_value_throws) {
|
||||
double d = 0.0;
|
||||
bool b = false;
|
||||
std::string s("asdf");
|
||||
std::vector<ConstElementPtr> v;
|
||||
std::vector<ElementPtr> v;
|
||||
std::map<std::string, ConstElementPtr> m;
|
||||
ConstElementPtr tmp;
|
||||
|
||||
|
132
src/lib/cc/tests/simple_parser_unittest.cc
Normal file
132
src/lib/cc/tests/simple_parser_unittest.cc
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright (C) 2016 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
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include <cc/simple_parser.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
using namespace isc::data;
|
||||
|
||||
/// This table defines sample default values. Although these are DHCPv6
|
||||
/// specific, the mechanism is generic and can be used by any other component.
|
||||
const SimpleDefaults SAMPLE_DEFAULTS = {
|
||||
{ "renew-timer", Element::integer, "900" },
|
||||
{ "rebind-timer", Element::integer, "1800" },
|
||||
{ "preferred-lifetime", Element::integer, "3600" },
|
||||
{ "valid-lifetime", Element::integer, "7200" }
|
||||
};
|
||||
|
||||
/// This list defines parameters that can be inherited from one scope
|
||||
/// to another. Although these are DHCPv6 specific, the mechanism is generic and
|
||||
/// can be used by any other component.
|
||||
const ParamsList SAMPLE_INHERITS = {
|
||||
"renew-timer",
|
||||
"rebind-timer",
|
||||
"preferred-lifetime",
|
||||
"valid-lifetime"
|
||||
};
|
||||
|
||||
/// @brief Simple Parser test fixture class
|
||||
class SimpleParserTest : public ::testing::Test {
|
||||
public:
|
||||
/// @brief Checks if specified map has an integer parameter with expected value
|
||||
///
|
||||
/// @param map map to be checked
|
||||
/// @param param_name name of the parameter to be checked
|
||||
/// @param exp_value expected value of the parameter.
|
||||
void checkIntegerValue(const ConstElementPtr& map, const std::string& param_name,
|
||||
int64_t exp_value) {
|
||||
|
||||
// First check if the passed element is a map.
|
||||
ASSERT_EQ(Element::map, map->getType());
|
||||
|
||||
// Now try to get the element being checked
|
||||
ConstElementPtr elem = map->get(param_name);
|
||||
ASSERT_TRUE(elem);
|
||||
|
||||
// Now check if it's indeed integer
|
||||
ASSERT_EQ(Element::integer, elem->getType());
|
||||
|
||||
// Finally, check if its value meets expectation.
|
||||
EXPECT_EQ(exp_value, elem->intValue());
|
||||
}
|
||||
};
|
||||
|
||||
// This test checks if the parameters can be inherited from the global
|
||||
// scope to the subnet scope.
|
||||
TEST_F(SimpleParserTest, deriveParams) {
|
||||
ElementPtr global = Element::fromJSON("{ \"renew-timer\": 1,"
|
||||
" \"rebind-timer\": 2,"
|
||||
" \"preferred-lifetime\": 3,"
|
||||
" \"valid-lifetime\": 4"
|
||||
"}");
|
||||
ElementPtr subnet = Element::fromJSON("{ \"renew-timer\": 100 }");
|
||||
|
||||
// we should inherit 3 parameters. Renew-timer should remain intact,
|
||||
// as it was already defined in the subnet scope.
|
||||
size_t num;
|
||||
EXPECT_NO_THROW(num = SimpleParser::deriveParams(global, subnet,
|
||||
SAMPLE_INHERITS));
|
||||
EXPECT_EQ(3, num);
|
||||
|
||||
// Check the values. 3 of them are inherited, while the fourth one
|
||||
// was already defined in the subnet, so should not be inherited.
|
||||
checkIntegerValue(subnet, "renew-timer", 100);
|
||||
checkIntegerValue(subnet, "rebind-timer", 2);
|
||||
checkIntegerValue(subnet, "preferred-lifetime", 3);
|
||||
checkIntegerValue(subnet, "valid-lifetime", 4);
|
||||
}
|
||||
|
||||
// This test checks if global defaults are properly set for DHCPv6.
|
||||
TEST_F(SimpleParserTest, setDefaults) {
|
||||
|
||||
ElementPtr empty = Element::fromJSON("{ }");
|
||||
size_t num = 0;
|
||||
|
||||
EXPECT_NO_THROW(num = SimpleParser::setDefaults(empty, SAMPLE_DEFAULTS));
|
||||
|
||||
// We expect at least 4 parameters to be inserted.
|
||||
EXPECT_GE(num, 3);
|
||||
|
||||
checkIntegerValue(empty, "valid-lifetime", 7200);
|
||||
checkIntegerValue(empty, "preferred-lifetime", 3600);
|
||||
checkIntegerValue(empty, "rebind-timer", 1800);
|
||||
checkIntegerValue(empty, "renew-timer", 900);
|
||||
}
|
||||
|
||||
// This test checks if global defaults are properly set for DHCPv6.
|
||||
TEST_F(SimpleParserTest, setListDefaults) {
|
||||
|
||||
ElementPtr empty = Element::fromJSON("[{}, {}, {}]");
|
||||
size_t num;
|
||||
|
||||
EXPECT_NO_THROW(num = SimpleParser::setListDefaults(empty, SAMPLE_DEFAULTS));
|
||||
|
||||
// We expect at least 12 parameters to be inserted (3 entries, with
|
||||
// 4 parameters inserted in each)
|
||||
EXPECT_EQ(12, num);
|
||||
|
||||
ASSERT_EQ(Element::list, empty->getType());
|
||||
ASSERT_EQ(3, empty->size());
|
||||
|
||||
ConstElementPtr first = empty->get(0);
|
||||
ConstElementPtr second = empty->get(1);
|
||||
ConstElementPtr third = empty->get(2);
|
||||
|
||||
checkIntegerValue(first, "valid-lifetime", 7200);
|
||||
checkIntegerValue(first, "preferred-lifetime", 3600);
|
||||
checkIntegerValue(first, "rebind-timer", 1800);
|
||||
checkIntegerValue(first, "renew-timer", 900);
|
||||
|
||||
checkIntegerValue(second, "valid-lifetime", 7200);
|
||||
checkIntegerValue(second, "preferred-lifetime", 3600);
|
||||
checkIntegerValue(second, "rebind-timer", 1800);
|
||||
checkIntegerValue(second, "renew-timer", 900);
|
||||
|
||||
checkIntegerValue(third, "valid-lifetime", 7200);
|
||||
checkIntegerValue(third, "preferred-lifetime", 3600);
|
||||
checkIntegerValue(third, "rebind-timer", 1800);
|
||||
checkIntegerValue(third, "renew-timer", 900);
|
||||
}
|
@@ -20,8 +20,6 @@ if HAVE_CQL
|
||||
AM_CPPFLAGS += $(CQL_CPPFLAGS)
|
||||
endif
|
||||
|
||||
AM_CXXFLAGS = $(KEA_CXXFLAGS)
|
||||
|
||||
# The files in the subfolder must be explicitly specified here so
|
||||
# as they are copied to the distribution. The other option would
|
||||
# be to specify a whole 'parsers' folder here but that would also
|
||||
|
@@ -476,4 +476,5 @@ is used by DHCPv4 and DHCPv6 components.
|
||||
|
||||
DHCPv4-over-DHCPv6 which are relayed by a DHCPv6 relay are not yet supported.
|
||||
|
||||
|
||||
*/
|
||||
|
@@ -91,12 +91,16 @@ ClientClassDefParser::build(ConstElementPtr class_def_cfg) {
|
||||
global_context_));
|
||||
parser = exp_parser;
|
||||
} else if (entry == "option-data") {
|
||||
OptionDataListParserPtr opts_parser;
|
||||
|
||||
uint16_t family = (global_context_->universe_ == Option::V4 ?
|
||||
AF_INET : AF_INET6);
|
||||
|
||||
opts_parser.reset(new OptionDataListParser(entry, options_, family));
|
||||
parser = opts_parser;
|
||||
OptionDataListParser opts_parser(family);
|
||||
opts_parser.parse(options_, param.second);
|
||||
|
||||
// OptionDataListParser is converted to SimpleParser already,
|
||||
// no need to go through build/commit phases.
|
||||
continue;
|
||||
} else if (entry == "next-server") {
|
||||
StringParserPtr str_parser(new StringParser(entry, string_values_));
|
||||
parser = str_parser;
|
||||
|
@@ -376,75 +376,30 @@ HooksLibrariesParser::getLibraries(isc::hooks::HookLibsCollection& libraries,
|
||||
}
|
||||
|
||||
// **************************** OptionDataParser *************************
|
||||
OptionDataParser::OptionDataParser(const std::string&, const CfgOptionPtr& cfg,
|
||||
const uint16_t address_family)
|
||||
: boolean_values_(new BooleanStorage()),
|
||||
string_values_(new StringStorage()), uint32_values_(new Uint32Storage()),
|
||||
option_descriptor_(false), cfg_(cfg),
|
||||
address_family_(address_family) {
|
||||
// If configuration not specified, then it is a global configuration
|
||||
// scope.
|
||||
if (!cfg_) {
|
||||
cfg_ = CfgMgr::instance().getStagingCfg()->getCfgOption();
|
||||
}
|
||||
OptionDataParser::OptionDataParser(const uint16_t address_family)
|
||||
: address_family_(address_family) {
|
||||
}
|
||||
|
||||
void
|
||||
OptionDataParser::build(ConstElementPtr option_data_entries) {
|
||||
BOOST_FOREACH(ConfigPair param, option_data_entries->mapValue()) {
|
||||
ParserPtr parser;
|
||||
if (param.first == "name" || param.first == "data" ||
|
||||
param.first == "space") {
|
||||
StringParserPtr name_parser(new StringParser(param.first,
|
||||
string_values_));
|
||||
parser = name_parser;
|
||||
} else if (param.first == "code") {
|
||||
Uint32ParserPtr code_parser(new Uint32Parser(param.first,
|
||||
uint32_values_));
|
||||
parser = code_parser;
|
||||
} else if (param.first == "csv-format") {
|
||||
BooleanParserPtr value_parser(new BooleanParser(param.first,
|
||||
boolean_values_));
|
||||
parser = value_parser;
|
||||
} else {
|
||||
isc_throw(DhcpConfigError,
|
||||
"option-data parameter not supported: " << param.first
|
||||
<< " (" << param.second->getPosition() << ")");
|
||||
}
|
||||
|
||||
parser->build(param.second);
|
||||
// Before we can create an option we need to get the data from
|
||||
// the child parsers. The only way to do it is to invoke commit
|
||||
// on them so as they store the values in appropriate storages
|
||||
// that this class provided to them. Note that this will not
|
||||
// modify values stored in the global storages so the configuration
|
||||
// will remain consistent even parsing fails somewhere further on.
|
||||
parser->commit();
|
||||
}
|
||||
std::pair<OptionDescriptor, std::string>
|
||||
OptionDataParser::parse(isc::data::ConstElementPtr single_option) {
|
||||
|
||||
// Try to create the option instance.
|
||||
createOption(option_data_entries);
|
||||
std::pair<OptionDescriptor, std::string> opt = createOption(single_option);
|
||||
|
||||
if (!option_descriptor_.option_) {
|
||||
if (!opt.first.option_) {
|
||||
isc_throw(isc::InvalidOperation,
|
||||
"parser logic error: no option has been configured and"
|
||||
" thus there is nothing to commit. Has build() been called?");
|
||||
}
|
||||
|
||||
cfg_->add(option_descriptor_.option_, option_descriptor_.persistent_,
|
||||
option_space_);
|
||||
}
|
||||
|
||||
void
|
||||
OptionDataParser::commit() {
|
||||
// Does nothing
|
||||
return (opt);
|
||||
}
|
||||
|
||||
OptionalValue<uint32_t>
|
||||
OptionDataParser::extractCode(ConstElementPtr parent) const {
|
||||
uint32_t code;
|
||||
try {
|
||||
code = uint32_values_->getParam("code");
|
||||
code = getInteger(parent, "code");
|
||||
|
||||
} catch (const exception&) {
|
||||
// The code parameter was not found. Return an unspecified
|
||||
@@ -454,14 +409,14 @@ OptionDataParser::extractCode(ConstElementPtr parent) const {
|
||||
|
||||
if (code == 0) {
|
||||
isc_throw(DhcpConfigError, "option code must not be zero "
|
||||
"(" << uint32_values_->getPosition("code", parent) << ")");
|
||||
"(" << getPosition("code", parent) << ")");
|
||||
|
||||
} else if (address_family_ == AF_INET &&
|
||||
code > std::numeric_limits<uint8_t>::max()) {
|
||||
isc_throw(DhcpConfigError, "invalid option code '" << code
|
||||
<< "', it must not be greater than '"
|
||||
<< static_cast<int>(std::numeric_limits<uint8_t>::max())
|
||||
<< "' (" << uint32_values_->getPosition("code", parent)
|
||||
<< "' (" << getPosition("code", parent)
|
||||
<< ")");
|
||||
|
||||
} else if (address_family_ == AF_INET6 &&
|
||||
@@ -469,7 +424,7 @@ OptionDataParser::extractCode(ConstElementPtr parent) const {
|
||||
isc_throw(DhcpConfigError, "invalid option code '" << code
|
||||
<< "', it must not exceed '"
|
||||
<< std::numeric_limits<uint16_t>::max()
|
||||
<< "' (" << uint32_values_->getPosition("code", parent)
|
||||
<< "' (" << getPosition("code", parent)
|
||||
<< ")");
|
||||
|
||||
}
|
||||
@@ -481,7 +436,7 @@ OptionalValue<std::string>
|
||||
OptionDataParser::extractName(ConstElementPtr parent) const {
|
||||
std::string name;
|
||||
try {
|
||||
name = string_values_->getParam("name");
|
||||
name = getString(parent, "name");
|
||||
|
||||
} catch (...) {
|
||||
return (OptionalValue<std::string>());
|
||||
@@ -490,17 +445,17 @@ OptionDataParser::extractName(ConstElementPtr parent) const {
|
||||
if (name.find(" ") != std::string::npos) {
|
||||
isc_throw(DhcpConfigError, "invalid option name '" << name
|
||||
<< "', space character is not allowed ("
|
||||
<< string_values_->getPosition("name", parent) << ")");
|
||||
<< getPosition("name", parent) << ")");
|
||||
}
|
||||
|
||||
return (OptionalValue<std::string>(name, OptionalValueState(true)));
|
||||
}
|
||||
|
||||
std::string
|
||||
OptionDataParser::extractData() const {
|
||||
OptionDataParser::extractData(ConstElementPtr parent) const {
|
||||
std::string data;
|
||||
try {
|
||||
data = string_values_->getParam("data");
|
||||
data = getString(parent, "data");
|
||||
|
||||
} catch (...) {
|
||||
// The "data" parameter was not found. Return an empty value.
|
||||
@@ -511,10 +466,10 @@ OptionDataParser::extractData() const {
|
||||
}
|
||||
|
||||
OptionalValue<bool>
|
||||
OptionDataParser::extractCSVFormat() const {
|
||||
OptionDataParser::extractCSVFormat(ConstElementPtr parent) const {
|
||||
bool csv_format = true;
|
||||
try {
|
||||
csv_format = boolean_values_->getParam("csv-format");
|
||||
csv_format = getBoolean(parent, "csv-format");
|
||||
|
||||
} catch (...) {
|
||||
return (OptionalValue<bool>(csv_format));
|
||||
@@ -524,11 +479,11 @@ OptionDataParser::extractCSVFormat() const {
|
||||
}
|
||||
|
||||
std::string
|
||||
OptionDataParser::extractSpace() const {
|
||||
OptionDataParser::extractSpace(ConstElementPtr parent) const {
|
||||
std::string space = address_family_ == AF_INET ?
|
||||
DHCP4_OPTION_SPACE : DHCP6_OPTION_SPACE;
|
||||
try {
|
||||
space = string_values_->getParam("space");
|
||||
space = getString(parent, "space");
|
||||
|
||||
} catch (...) {
|
||||
return (space);
|
||||
@@ -556,7 +511,7 @@ OptionDataParser::extractSpace() const {
|
||||
// should never get here. Therefore, it is ok to call getPosition for
|
||||
// the space parameter here as this parameter will always be specified.
|
||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||
<< string_values_->getPosition("space") << ")");
|
||||
<< getPosition("space", parent) << ")");
|
||||
}
|
||||
|
||||
return (space);
|
||||
@@ -588,16 +543,16 @@ OptionDataParser::findOptionDefinition(const std::string& option_space,
|
||||
return (def);
|
||||
}
|
||||
|
||||
void
|
||||
std::pair<OptionDescriptor, std::string>
|
||||
OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||
const Option::Universe universe = address_family_ == AF_INET ?
|
||||
Option::V4 : Option::V6;
|
||||
|
||||
OptionalValue<uint32_t> code_param = extractCode(option_data);
|
||||
OptionalValue<std::string> name_param = extractName(option_data);
|
||||
OptionalValue<bool> csv_format_param = extractCSVFormat();
|
||||
std::string data_param = extractData();
|
||||
std::string space_param = extractSpace();
|
||||
OptionalValue<bool> csv_format_param = extractCSVFormat(option_data);
|
||||
std::string data_param = extractData(option_data);
|
||||
std::string space_param = extractSpace(option_data);
|
||||
|
||||
// Require that option code or option name is specified.
|
||||
if (!code_param.isSpecified() && !name_param.isSpecified()) {
|
||||
@@ -622,7 +577,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||
<< space_param << "." << name_param
|
||||
<< "' having code '" << code_param
|
||||
<< "' does not exist ("
|
||||
<< string_values_->getPosition("name", option_data)
|
||||
<< getPosition("name", option_data)
|
||||
<< ")");
|
||||
|
||||
// If there is no option definition and the option code is not specified
|
||||
@@ -631,7 +586,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||
isc_throw(DhcpConfigError, "definition for the option '"
|
||||
<< space_param << "." << name_param
|
||||
<< "' does not exist ("
|
||||
<< string_values_->getPosition("name", option_data)
|
||||
<< getPosition("name", option_data)
|
||||
<< ")");
|
||||
}
|
||||
}
|
||||
@@ -664,12 +619,14 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||
isc_throw(DhcpConfigError, "option data is not a valid"
|
||||
<< " string of hexadecimal digits: " << data_param
|
||||
<< " ("
|
||||
<< string_values_->getPosition("data", option_data)
|
||||
<< getPosition("data", option_data)
|
||||
<< ")");
|
||||
}
|
||||
}
|
||||
|
||||
OptionPtr option;
|
||||
OptionDescriptor desc(false);
|
||||
|
||||
if (!def) {
|
||||
// @todo We have a limited set of option definitions initalized at
|
||||
// the moment. In the future we want to initialize option definitions
|
||||
@@ -678,13 +635,9 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||
// ok to create generic option if definition does not exist.
|
||||
OptionPtr option(new Option(universe, static_cast<uint16_t>(code_param),
|
||||
binary));
|
||||
// The created option is stored in option_descriptor_ class member
|
||||
// until the commit stage when it is inserted into the main storage.
|
||||
// If an option with the same code exists in main storage already the
|
||||
// old option is replaced.
|
||||
option_descriptor_.option_ = option;
|
||||
option_descriptor_.persistent_ = false;
|
||||
|
||||
desc.option_ = option;
|
||||
desc.persistent_ = false;
|
||||
} else {
|
||||
|
||||
// Option name is specified it should match the name in the definition.
|
||||
@@ -693,7 +646,7 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||
<< name_param << "' does not match the "
|
||||
<< "option definition: '" << space_param
|
||||
<< "." << def->getName() << "' ("
|
||||
<< string_values_->getPosition("name", option_data)
|
||||
<< getPosition("name", option_data)
|
||||
<< ")");
|
||||
}
|
||||
|
||||
@@ -704,144 +657,62 @@ OptionDataParser::createOption(ConstElementPtr option_data) {
|
||||
!csv_format_param.isSpecified() || csv_format_param ?
|
||||
def->optionFactory(universe, def->getCode(), data_tokens) :
|
||||
def->optionFactory(universe, def->getCode(), binary);
|
||||
OptionDescriptor desc(option, false);
|
||||
option_descriptor_.option_ = option;
|
||||
option_descriptor_.persistent_ = false;
|
||||
|
||||
desc.option_ = option;
|
||||
desc.persistent_ = false;
|
||||
} catch (const isc::Exception& ex) {
|
||||
isc_throw(DhcpConfigError, "option data does not match"
|
||||
<< " option definition (space: " << space_param
|
||||
<< ", code: " << def->getCode() << "): "
|
||||
<< ex.what() << " ("
|
||||
<< string_values_->getPosition("data", option_data)
|
||||
<< getPosition("data", option_data)
|
||||
<< ")");
|
||||
}
|
||||
}
|
||||
|
||||
// All went good, so we can set the option space name.
|
||||
option_space_ = space_param;
|
||||
return make_pair(desc, space_param);
|
||||
}
|
||||
|
||||
// **************************** OptionDataListParser *************************
|
||||
OptionDataListParser::OptionDataListParser(const std::string&,
|
||||
const CfgOptionPtr& cfg,
|
||||
OptionDataListParser::OptionDataListParser(//const std::string&,
|
||||
//const CfgOptionPtr& cfg,
|
||||
const uint16_t address_family)
|
||||
: cfg_(cfg), address_family_(address_family) {
|
||||
: address_family_(address_family) {
|
||||
}
|
||||
|
||||
void
|
||||
OptionDataListParser::build(ConstElementPtr option_data_list) {
|
||||
BOOST_FOREACH(ConstElementPtr option_value, option_data_list->listValue()) {
|
||||
boost::shared_ptr<OptionDataParser>
|
||||
parser(new OptionDataParser("option-data", cfg_, address_family_));
|
||||
|
||||
parser->build(option_value);
|
||||
parsers_.push_back(parser);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OptionDataListParser::commit() {
|
||||
BOOST_FOREACH(ParserPtr parser, parsers_) {
|
||||
parser->commit();
|
||||
}
|
||||
// Append suboptions to the top-level options
|
||||
if (cfg_) {
|
||||
cfg_->encapsulate();
|
||||
} else {
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOption()->encapsulate();
|
||||
void OptionDataListParser::parse(const CfgOptionPtr& cfg,
|
||||
isc::data::ConstElementPtr option_data_list) {
|
||||
OptionDataParser option_parser(address_family_);
|
||||
BOOST_FOREACH(ConstElementPtr data, option_data_list->listValue()) {
|
||||
std::pair<OptionDescriptor, std::string> option =
|
||||
option_parser.parse(data);
|
||||
cfg->add(option.first.option_, option.first.persistent_, option.second);
|
||||
cfg->encapsulate();
|
||||
}
|
||||
}
|
||||
|
||||
// ******************************** OptionDefParser ****************************
|
||||
OptionDefParser::OptionDefParser(const std::string&,
|
||||
ParserContextPtr global_context)
|
||||
: boolean_values_(new BooleanStorage()),
|
||||
string_values_(new StringStorage()),
|
||||
uint32_values_(new Uint32Storage()),
|
||||
global_context_(global_context) {
|
||||
}
|
||||
|
||||
void
|
||||
OptionDefParser::build(ConstElementPtr option_def) {
|
||||
// Parse the elements that make up the option definition.
|
||||
BOOST_FOREACH(ConfigPair param, option_def->mapValue()) {
|
||||
std::string entry(param.first);
|
||||
ParserPtr parser;
|
||||
if (entry == "name" || entry == "type" || entry == "record-types"
|
||||
|| entry == "space" || entry == "encapsulate") {
|
||||
StringParserPtr str_parser(new StringParser(entry,
|
||||
string_values_));
|
||||
parser = str_parser;
|
||||
} else if (entry == "code") {
|
||||
Uint32ParserPtr code_parser(new Uint32Parser(entry,
|
||||
uint32_values_));
|
||||
parser = code_parser;
|
||||
} else if (entry == "array") {
|
||||
BooleanParserPtr array_parser(new BooleanParser(entry,
|
||||
boolean_values_));
|
||||
parser = array_parser;
|
||||
} else {
|
||||
isc_throw(DhcpConfigError, "invalid parameter '" << entry
|
||||
<< "' (" << param.second->getPosition() << ")");
|
||||
}
|
||||
std::pair<isc::dhcp::OptionDefinitionPtr, std::string>
|
||||
OptionDefParser::parse(ConstElementPtr option_def) {
|
||||
|
||||
parser->build(param.second);
|
||||
parser->commit();
|
||||
}
|
||||
// Create an instance of option definition.
|
||||
createOptionDef(option_def);
|
||||
// Get mandatory parameters.
|
||||
std::string name = getString(option_def, "name");
|
||||
uint32_t code = getInteger(option_def, "code");
|
||||
std::string type = getString(option_def, "type");
|
||||
|
||||
try {
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->
|
||||
add(option_definition_, option_space_name_);
|
||||
|
||||
} catch (const std::exception& ex) {
|
||||
// Append position if there is a failure.
|
||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||
<< option_def->getPosition() << ")");
|
||||
}
|
||||
|
||||
// All definitions have been prepared. Put them as runtime options into
|
||||
// the libdhcp++.
|
||||
const OptionDefSpaceContainer& container =
|
||||
CfgMgr::instance().getStagingCfg()->getCfgOptionDef()->getContainer();
|
||||
LibDHCP::setRuntimeOptionDefs(container);
|
||||
}
|
||||
|
||||
void
|
||||
OptionDefParser::commit() {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
void
|
||||
OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
|
||||
// Check if mandatory parameters have been specified.
|
||||
std::string name;
|
||||
uint32_t code;
|
||||
std::string type;
|
||||
try {
|
||||
name = string_values_->getParam("name");
|
||||
code = uint32_values_->getParam("code");
|
||||
type = string_values_->getParam("type");
|
||||
} catch (const std::exception& ex) {
|
||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||
<< option_def_element->getPosition() << ")");
|
||||
}
|
||||
|
||||
bool array_type = boolean_values_->getOptionalParam("array", false);
|
||||
std::string record_types =
|
||||
string_values_->getOptionalParam("record-types", "");
|
||||
std::string space = string_values_->getOptionalParam("space",
|
||||
global_context_->universe_ == Option::V4 ? DHCP4_OPTION_SPACE :
|
||||
DHCP6_OPTION_SPACE);
|
||||
std::string encapsulates =
|
||||
string_values_->getOptionalParam("encapsulate", "");
|
||||
// Get optional parameters. Whoever called this parser, should have
|
||||
// called SimpleParser::setDefaults first.
|
||||
bool array_type = getBoolean(option_def, "array");
|
||||
std::string record_types = getString(option_def, "record-types");
|
||||
std::string space = getString(option_def, "space");
|
||||
std::string encapsulates = getString(option_def, "encapsulate");
|
||||
|
||||
if (!OptionSpace::validateName(space)) {
|
||||
isc_throw(DhcpConfigError, "invalid option space name '"
|
||||
<< space << "' ("
|
||||
<< string_values_->getPosition("space") << ")");
|
||||
<< getPosition("space", option_def) << ")");
|
||||
}
|
||||
|
||||
// Create option definition.
|
||||
@@ -854,14 +725,14 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
|
||||
isc_throw(DhcpConfigError, "option '" << space << "."
|
||||
<< "name" << "', comprising an array of data"
|
||||
<< " fields may not encapsulate any option space ("
|
||||
<< option_def_element->getPosition() << ")");
|
||||
<< option_def->getPosition() << ")");
|
||||
|
||||
} else if (encapsulates == space) {
|
||||
isc_throw(DhcpConfigError, "option must not encapsulate"
|
||||
<< " an option space it belongs to: '"
|
||||
<< space << "." << name << "' is set to"
|
||||
<< " encapsulate '" << space << "' ("
|
||||
<< option_def_element->getPosition() << ")");
|
||||
<< option_def->getPosition() << ")");
|
||||
|
||||
} else {
|
||||
def.reset(new OptionDefinition(name, code, type,
|
||||
@@ -888,7 +759,7 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
|
||||
isc_throw(DhcpConfigError, "invalid record type values"
|
||||
<< " specified for the option definition: "
|
||||
<< ex.what() << " ("
|
||||
<< string_values_->getPosition("record-types") << ")");
|
||||
<< getPosition("record-types", option_def) << ")");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -897,38 +768,39 @@ OptionDefParser::createOptionDef(ConstElementPtr option_def_element) {
|
||||
def->validate();
|
||||
} catch (const std::exception& ex) {
|
||||
isc_throw(DhcpConfigError, ex.what()
|
||||
<< " (" << option_def_element->getPosition() << ")");
|
||||
<< " (" << option_def->getPosition() << ")");
|
||||
}
|
||||
|
||||
// Option definition has been created successfully.
|
||||
option_space_name_ = space;
|
||||
option_definition_ = def;
|
||||
return make_pair(def, space);
|
||||
}
|
||||
|
||||
// ******************************** OptionDefListParser ************************
|
||||
OptionDefListParser::OptionDefListParser(const std::string&,
|
||||
ParserContextPtr global_context)
|
||||
: global_context_(global_context) {
|
||||
}
|
||||
|
||||
void
|
||||
OptionDefListParser::build(ConstElementPtr option_def_list) {
|
||||
OptionDefListParser::parse(CfgOptionDefPtr storage, ConstElementPtr option_def_list) {
|
||||
if (!option_def_list) {
|
||||
isc_throw(DhcpConfigError, "parser error: a pointer to a list of"
|
||||
<< " option definitions is NULL ("
|
||||
<< option_def_list->getPosition() << ")");
|
||||
}
|
||||
|
||||
OptionDefParser parser;
|
||||
BOOST_FOREACH(ConstElementPtr option_def, option_def_list->listValue()) {
|
||||
boost::shared_ptr<OptionDefParser>
|
||||
parser(new OptionDefParser("single-option-def", global_context_));
|
||||
parser->build(option_def);
|
||||
OptionDefinitionTuple def;
|
||||
|
||||
def = parser.parse(option_def);
|
||||
try {
|
||||
storage->add(def.first, def.second);
|
||||
} catch (const std::exception& ex) {
|
||||
// Append position if there is a failure.
|
||||
isc_throw(DhcpConfigError, ex.what() << " ("
|
||||
<< option_def->getPosition() << ")");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OptionDefListParser::commit() {
|
||||
// Do nothing.
|
||||
// All definitions have been prepared. Put them as runtime options into
|
||||
// the libdhcp++.
|
||||
LibDHCP::setRuntimeOptionDefs(storage->getContainer());
|
||||
}
|
||||
|
||||
//****************************** RelayInfoParser ********************************
|
||||
@@ -1138,13 +1010,9 @@ PoolParser::build(ConstElementPtr pool_structure) {
|
||||
" address pools");
|
||||
}
|
||||
|
||||
OptionDataListParserPtr option_parser(new OptionDataListParser("option-data",
|
||||
options_,
|
||||
address_family_));
|
||||
option_parser->build(option_data);
|
||||
option_parser->commit();
|
||||
options_->copyTo(*pool->getCfgOption());;
|
||||
|
||||
CfgOptionPtr cfg = pool->getCfgOption();
|
||||
OptionDataListParser option_parser(address_family_);
|
||||
option_parser.parse(cfg, option_data);
|
||||
} catch (const std::exception& ex) {
|
||||
isc_throw(isc::dhcp::DhcpConfigError, ex.what()
|
||||
<< " (" << option_data->getPosition() << ")");
|
||||
@@ -1194,6 +1062,13 @@ SubnetConfigParser::build(ConstElementPtr subnet) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (param.first == "option-data") {
|
||||
uint16_t family = global_context_->universe_ == Option::V4 ? AF_INET : AF_INET6;
|
||||
OptionDataListParser opt_parser(family);
|
||||
opt_parser.parse(options_, param.second);
|
||||
continue;
|
||||
}
|
||||
|
||||
ParserPtr parser;
|
||||
// When unsupported parameter is specified, the function called
|
||||
// below will thrown an exception. We have to catch this exception
|
||||
|
@@ -15,7 +15,9 @@
|
||||
#include <dhcpsrv/cfg_iface.h>
|
||||
#include <dhcpsrv/cfg_option.h>
|
||||
#include <dhcpsrv/subnet.h>
|
||||
#include <dhcpsrv/cfg_option_def.h>
|
||||
#include <dhcpsrv/parsers/dhcp_config_parser.h>
|
||||
#include <cc/simple_parser.h>
|
||||
#include <hooks/libinfo.h>
|
||||
#include <exceptions/exceptions.h>
|
||||
#include <util/optional_value.h>
|
||||
@@ -534,39 +536,30 @@ private:
|
||||
/// an option the configuration will not be accepted. If parsing
|
||||
/// is successful then an instance of an option is created and
|
||||
/// added to the storage provided by the calling class.
|
||||
class OptionDataParser : public DhcpConfigParser {
|
||||
class OptionDataParser : public isc::data::SimpleParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param [out] cfg Pointer to the configuration object where parsed option
|
||||
/// should be stored or NULL if this is a global option.
|
||||
/// @param address_family Address family: @c AF_INET or @c AF_INET6.
|
||||
/// @throw isc::dhcp::DhcpConfigError if options or global_context are null.
|
||||
OptionDataParser(const std::string& dummy, const CfgOptionPtr& cfg,
|
||||
const uint16_t address_family);
|
||||
OptionDataParser(const uint16_t address_family);
|
||||
|
||||
/// @brief Parses the single option data.
|
||||
/// @brief Parses ElementPtr containing option definition
|
||||
///
|
||||
/// This method parses the data of a single option from the configuration.
|
||||
/// The option data includes option name, option code and data being
|
||||
/// carried by this option. Eventually it creates the instance of the
|
||||
/// option and adds it to the Configuration Manager.
|
||||
/// This method parses ElementPtr containing the option defintion,
|
||||
/// instantiates the option for it and then returns a pair
|
||||
/// of option descritor (that holds that new option) and
|
||||
/// a string that specifies the option space.
|
||||
///
|
||||
/// @param option_data_entries collection of entries that define value
|
||||
/// for a particular option.
|
||||
/// @throw DhcpConfigError if invalid parameter specified in
|
||||
/// the configuration.
|
||||
/// @throw isc::InvalidOperation if failed to set storage prior to
|
||||
/// calling build.
|
||||
virtual void build(isc::data::ConstElementPtr option_data_entries);
|
||||
|
||||
/// @brief Does nothing.
|
||||
virtual void commit();
|
||||
|
||||
/// @brief virtual destructor to ensure orderly destruction of derivations.
|
||||
virtual ~OptionDataParser(){};
|
||||
/// Note: ElementPtr is expected to contain all fields. If your
|
||||
/// ElementPtr does not have them, please use
|
||||
/// @ref isc::data::SimpleParser::setDefaults to fill the missing fields
|
||||
/// with default values.
|
||||
///
|
||||
/// @param single_option ElementPtr containing option defintion
|
||||
/// @return Option object wrapped in option description and an option
|
||||
/// space
|
||||
std::pair<OptionDescriptor, std::string>
|
||||
parse(isc::data::ConstElementPtr single_option);
|
||||
private:
|
||||
|
||||
/// @brief Finds an option definition within an option space
|
||||
@@ -594,22 +587,19 @@ private:
|
||||
/// options storage. If the option data parsed by \ref build function
|
||||
/// are invalid or insufficient this function emits an exception.
|
||||
///
|
||||
/// @warning this function does not check if options_ storage pointer
|
||||
/// is intitialized but this check is not needed here because it is done
|
||||
/// in the \ref build function.
|
||||
///
|
||||
/// @param option_data An element holding data for a single option being
|
||||
/// created.
|
||||
///
|
||||
/// @return created option descriptor
|
||||
///
|
||||
/// @throw DhcpConfigError if parameters provided in the configuration
|
||||
/// are invalid.
|
||||
void createOption(isc::data::ConstElementPtr option_data);
|
||||
std::pair<OptionDescriptor, std::string>
|
||||
createOption(isc::data::ConstElementPtr option_data);
|
||||
|
||||
/// @brief Retrieves parsed option code as an optional value.
|
||||
///
|
||||
/// @param parent A data element holding full option data configuration.
|
||||
/// It is used here to log a position if the element holding a code
|
||||
/// is not specified and its position is therefore unavailable.
|
||||
///
|
||||
/// @return Option code, possibly unspecified.
|
||||
/// @throw DhcpConfigError if option code is invalid.
|
||||
@@ -619,8 +609,6 @@ private:
|
||||
/// @brief Retrieves parsed option name as an optional value.
|
||||
///
|
||||
/// @param parent A data element holding full option data configuration.
|
||||
/// It is used here to log a position if the element holding a name
|
||||
/// is not specified and its position is therefore unavailable.
|
||||
///
|
||||
/// @return Option name, possibly unspecified.
|
||||
/// @throw DhcpConfigError if option name is invalid.
|
||||
@@ -630,13 +618,14 @@ private:
|
||||
/// @brief Retrieves csv-format parameter as an optional value.
|
||||
///
|
||||
/// @return Value of the csv-format parameter, possibly unspecified.
|
||||
util::OptionalValue<bool> extractCSVFormat() const;
|
||||
util::OptionalValue<bool> extractCSVFormat(data::ConstElementPtr parent) const;
|
||||
|
||||
/// @brief Retrieves option data as a string.
|
||||
///
|
||||
/// @param parent A data element holding full option data configuration.
|
||||
/// @return Option data as a string. It will return empty string if
|
||||
/// option data is unspecified.
|
||||
std::string extractData() const;
|
||||
std::string extractData(data::ConstElementPtr parent) const;
|
||||
|
||||
/// @brief Retrieves option space name.
|
||||
///
|
||||
@@ -644,27 +633,10 @@ private:
|
||||
/// 'dhcp4' or 'dhcp6' option space name is returned, depending on
|
||||
/// the universe specified in the parser context.
|
||||
///
|
||||
/// @param parent A data element holding full option data configuration.
|
||||
///
|
||||
/// @return Option space name.
|
||||
std::string extractSpace() const;
|
||||
|
||||
/// Storage for boolean values.
|
||||
BooleanStoragePtr boolean_values_;
|
||||
|
||||
/// Storage for string values (e.g. option name or data).
|
||||
StringStoragePtr string_values_;
|
||||
|
||||
/// Storage for uint32 values (e.g. option code).
|
||||
Uint32StoragePtr uint32_values_;
|
||||
|
||||
/// Option descriptor holds newly configured option.
|
||||
OptionDescriptor option_descriptor_;
|
||||
|
||||
/// Option space name where the option belongs to.
|
||||
std::string option_space_;
|
||||
|
||||
/// @brief Configuration holding option being parsed or NULL if the option
|
||||
/// is global.
|
||||
CfgOptionPtr cfg_;
|
||||
std::string extractSpace(data::ConstElementPtr parent) const;
|
||||
|
||||
/// @brief Address family: @c AF_INET or @c AF_INET6.
|
||||
uint16_t address_family_;
|
||||
@@ -680,97 +652,43 @@ typedef OptionDataParser *OptionDataParserFactory(const std::string&,
|
||||
/// data for a particular subnet and creates a collection of options.
|
||||
/// If parsing is successful, all these options are added to the Subnet
|
||||
/// object.
|
||||
class OptionDataListParser : public DhcpConfigParser {
|
||||
class OptionDataListParser : public isc::data::SimpleParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param dummy nominally would be param name, this is always ignored.
|
||||
/// @param [out] cfg Pointer to the configuration object where options
|
||||
/// should be stored or NULL if this is global option scope.
|
||||
/// @param address_family Address family: @c AF_INET or AF_INET6
|
||||
OptionDataListParser(const std::string& dummy, const CfgOptionPtr& cfg,
|
||||
const uint16_t address_family);
|
||||
OptionDataListParser(const uint16_t address_family);
|
||||
|
||||
/// @brief Parses entries that define options' data for a subnet.
|
||||
/// @brief Parses a list of options, instantiates them and stores in cfg
|
||||
///
|
||||
/// This method iterates over all entries that define option data
|
||||
/// for options within a single subnet and creates options' instances.
|
||||
/// This method expects to get a list of options in option_data_list,
|
||||
/// iterates over them, creates option objects, wraps them with
|
||||
/// option descriptor and stores in specified cfg.
|
||||
///
|
||||
/// @param option_data_list pointer to a list of options' data sets.
|
||||
/// @throw DhcpConfigError if option parsing failed.
|
||||
void build(isc::data::ConstElementPtr option_data_list);
|
||||
|
||||
/// @brief Commit all option values.
|
||||
///
|
||||
/// This function invokes commit for all option values
|
||||
/// and append suboptions to the top-level options.
|
||||
void commit();
|
||||
|
||||
/// @param cfg created options will be stored here
|
||||
/// @param option_data_list configuration that describes the options
|
||||
void parse(const CfgOptionPtr& cfg,
|
||||
isc::data::ConstElementPtr option_data_list);
|
||||
private:
|
||||
|
||||
/// Collection of parsers;
|
||||
ParserCollection parsers_;
|
||||
|
||||
/// @brief Pointer to a configuration where options are stored.
|
||||
CfgOptionPtr cfg_;
|
||||
|
||||
/// @brief Address family: @c AF_INET or @c AF_INET6
|
||||
uint16_t address_family_;
|
||||
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<OptionDataListParser> OptionDataListParserPtr;
|
||||
|
||||
typedef std::pair<isc::dhcp::OptionDefinitionPtr, std::string> OptionDefinitionTuple;
|
||||
|
||||
/// @brief Parser for a single option definition.
|
||||
///
|
||||
/// This parser creates an instance of a single option definition.
|
||||
class OptionDefParser : public DhcpConfigParser {
|
||||
class OptionDefParser : public isc::data::SimpleParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
OptionDefParser(const std::string& dummy, ParserContextPtr global_context);
|
||||
|
||||
/// @brief Parses an entry that describes single option definition.
|
||||
///
|
||||
/// @param option_def a configuration entry to be parsed.
|
||||
/// @return tuple (option definition, option space) of the parsed structure
|
||||
///
|
||||
/// @throw DhcpConfigError if parsing was unsuccessful.
|
||||
void build(isc::data::ConstElementPtr option_def);
|
||||
|
||||
/// @brief Stores the parsed option definition in a storage.
|
||||
void commit();
|
||||
|
||||
private:
|
||||
|
||||
/// @brief Create option definition from the parsed parameters.
|
||||
///
|
||||
/// @param option_def_element A data element holding the configuration
|
||||
/// for an option definition.
|
||||
void createOptionDef(isc::data::ConstElementPtr option_def_element);
|
||||
|
||||
/// Instance of option definition being created by this parser.
|
||||
OptionDefinitionPtr option_definition_;
|
||||
|
||||
/// Name of the space the option definition belongs to.
|
||||
std::string option_space_name_;
|
||||
|
||||
/// Storage for boolean values.
|
||||
BooleanStoragePtr boolean_values_;
|
||||
|
||||
/// Storage for string values.
|
||||
StringStoragePtr string_values_;
|
||||
|
||||
/// Storage for uint32 values.
|
||||
Uint32StoragePtr uint32_values_;
|
||||
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
OptionDefinitionTuple
|
||||
parse(isc::data::ConstElementPtr option_def);
|
||||
};
|
||||
|
||||
/// @brief Parser for a list of option definitions.
|
||||
@@ -779,37 +697,17 @@ private:
|
||||
/// option definitions and creates instances of these definitions.
|
||||
/// If the parsing is successful, the collection of created definitions
|
||||
/// is put into the provided storage.
|
||||
class OptionDefListParser : public DhcpConfigParser {
|
||||
class OptionDefListParser : public isc::data::SimpleParser {
|
||||
public:
|
||||
/// @brief Constructor.
|
||||
/// @brief Parses a list of option defintions, create them and store in cfg
|
||||
///
|
||||
/// @param dummy first argument is ignored, all Parser constructors
|
||||
/// accept string as first argument.
|
||||
/// @param global_context is a pointer to the global context which
|
||||
/// stores global scope parameters, options, option defintions.
|
||||
OptionDefListParser(const std::string& dummy,
|
||||
ParserContextPtr global_context);
|
||||
|
||||
/// @brief Parse configuration entries.
|
||||
/// This method iterates over def_list, which is a JSON list of option defintions,
|
||||
/// then creates corresponding option defintions and store them in the
|
||||
/// configuration structure.
|
||||
///
|
||||
/// This function parses configuration entries, creates instances
|
||||
/// of option definitions and tries to add them to the Configuration
|
||||
/// Manager.
|
||||
///
|
||||
/// @param option_def_list pointer to an element that holds entries
|
||||
/// that define option definitions.
|
||||
/// @throw DhcpConfigError if configuration parsing fails.
|
||||
void build(isc::data::ConstElementPtr option_def_list);
|
||||
|
||||
/// @brief Commits option definitions.
|
||||
///
|
||||
/// Currently this function is no-op, because option definitions are
|
||||
/// added to the Configuration Manager in the @c build method.
|
||||
void commit();
|
||||
|
||||
/// Parsing context which contains global values, options and option
|
||||
/// definitions.
|
||||
ParserContextPtr global_context_;
|
||||
/// @param def_list JSON list describing option definitions
|
||||
/// @param cfg parsed option definitions will be stored here
|
||||
void parse(CfgOptionDefPtr cfg, isc::data::ConstElementPtr def_list);
|
||||
};
|
||||
|
||||
/// @brief a collection of pools
|
||||
|
@@ -196,8 +196,12 @@ HostReservationParser4::build(isc::data::ConstElementPtr reservation_data) {
|
||||
// surround it with try-catch.
|
||||
if (element.first == "option-data") {
|
||||
CfgOptionPtr cfg_option = host_->getCfgOption4();
|
||||
OptionDataListParser parser(element.first, cfg_option, AF_INET);
|
||||
parser.build(element.second);
|
||||
|
||||
// This parser is converted to SimpleParser already. It
|
||||
// parses the Element structure immediately, there's no need
|
||||
// to go through build/commit phases.
|
||||
OptionDataListParser parser(AF_INET);
|
||||
parser.parse(cfg_option, element.second);
|
||||
|
||||
// Everything else should be surrounded with try-catch to append
|
||||
// position.
|
||||
@@ -255,8 +259,12 @@ HostReservationParser6::build(isc::data::ConstElementPtr reservation_data) {
|
||||
// appended).
|
||||
if (element.first == "option-data") {
|
||||
CfgOptionPtr cfg_option = host_->getCfgOption6();
|
||||
OptionDataListParser parser(element.first, cfg_option, AF_INET6);
|
||||
parser.build(element.second);
|
||||
|
||||
// This parser is converted to SimpleParser already. It
|
||||
// parses the Element structure immediately, there's no need
|
||||
// to go through build/commit phases.
|
||||
OptionDataListParser parser(AF_INET6);
|
||||
parser.parse(cfg_option, element.second);
|
||||
|
||||
} else if (element.first == "ip-addresses" || element.first == "prefixes") {
|
||||
BOOST_FOREACH(ConstElementPtr prefix_element,
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <config.h>
|
||||
#include <cc/command_interpreter.h>
|
||||
#include <cc/data.h>
|
||||
#include <cc/simple_parser.h>
|
||||
#include <dhcp/option.h>
|
||||
#include <dhcp/option_custom.h>
|
||||
#include <dhcp/option_int.h>
|
||||
@@ -310,6 +311,26 @@ public:
|
||||
const std::map<std::string, ConstElementPtr>& values_map =
|
||||
config_set->mapValue();
|
||||
BOOST_FOREACH(config_pair, values_map) {
|
||||
|
||||
// These are the simple parsers. No need to go through
|
||||
// the ParserPtr hooplas with them.
|
||||
if ( (config_pair.first == "option-data") ||
|
||||
(config_pair.first == "option-def")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We also don't care about the default values that may be been
|
||||
// inserted
|
||||
if ( (config_pair.first == "preferred-lifetime") ||
|
||||
(config_pair.first == "valid-lifetime") ||
|
||||
(config_pair.first == "renew-timer") ||
|
||||
(config_pair.first == "rebind-timer")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remaining ones are old style parsers. Need go do
|
||||
// the build/commit dance with them.
|
||||
|
||||
// Create the parser based on element name.
|
||||
ParserPtr parser(createConfigParser(config_pair.first));
|
||||
// Options must be parsed last
|
||||
@@ -322,12 +343,27 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
int family = parser_context_->universe_ == Option::V4
|
||||
? AF_INET : AF_INET6;
|
||||
|
||||
// The option definition parser is the next one to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator
|
||||
def_config = values_map.find("option-def");
|
||||
if (def_config != values_map.end()) {
|
||||
|
||||
CfgOptionDefPtr cfg_def = CfgMgr::instance().getStagingCfg()->getCfgOptionDef();
|
||||
OptionDefListParser def_list_parser;
|
||||
def_list_parser.parse(cfg_def, def_config->second);
|
||||
}
|
||||
|
||||
// The option values parser is the next one to be run.
|
||||
std::map<std::string, ConstElementPtr>::const_iterator
|
||||
option_config = values_map.find("option-data");
|
||||
if (option_config != values_map.end()) {
|
||||
option_parser->build(option_config->second);
|
||||
option_parser->commit();
|
||||
CfgOptionPtr cfg_option = CfgMgr::instance().getStagingCfg()->getCfgOption();
|
||||
|
||||
OptionDataListParser option_list_parser(family);
|
||||
option_list_parser.parse(cfg_option, option_config->second);
|
||||
}
|
||||
|
||||
// Everything was fine. Configuration is successful.
|
||||
@@ -359,17 +395,9 @@ public:
|
||||
/// @throw throws NotImplemented if element name isn't supported.
|
||||
ParserPtr createConfigParser(const std::string& config_id) {
|
||||
ParserPtr parser;
|
||||
int family = parser_context_->universe_ == Option::V4 ?
|
||||
AF_INET : AF_INET6;
|
||||
if (config_id.compare("option-data") == 0) {
|
||||
parser.reset(new OptionDataListParser(config_id, CfgOptionPtr(),
|
||||
family));
|
||||
|
||||
} else if (config_id.compare("option-def") == 0) {
|
||||
parser.reset(new OptionDefListParser(config_id,
|
||||
parser_context_));
|
||||
|
||||
} else if (config_id.compare("hooks-libraries") == 0) {
|
||||
// option-data and option-def converted to SimpleParser, so they
|
||||
// are no longer here.
|
||||
if (config_id.compare("hooks-libraries") == 0) {
|
||||
parser.reset(new HooksLibrariesParser(config_id));
|
||||
hooks_libraries_parser_ =
|
||||
boost::dynamic_pointer_cast<HooksLibrariesParser>(parser);
|
||||
@@ -384,6 +412,101 @@ public:
|
||||
return (parser);
|
||||
}
|
||||
|
||||
/// @brief DHCP-specific method that sets global, and option specific defaults
|
||||
///
|
||||
/// This method sets the defaults in the global scope, in option definitions,
|
||||
/// and in option data.
|
||||
///
|
||||
/// @param global pointer to the Element tree that holds configuration
|
||||
/// @param global_defaults array with global default values
|
||||
/// @param option_defaults array with option-data default values
|
||||
/// @param option_def_defaults array with default values for option definitions
|
||||
/// @return number of default values inserted.
|
||||
size_t setAllDefaults(isc::data::ElementPtr global,
|
||||
const SimpleDefaults& global_defaults,
|
||||
const SimpleDefaults& option_defaults,
|
||||
const SimpleDefaults& option_def_defaults) {
|
||||
size_t cnt = 0;
|
||||
// Set global defaults first.
|
||||
/// @todo: Uncomment as part of the ticket 5019 work.
|
||||
cnt = SimpleParser::setDefaults(global, global_defaults);
|
||||
|
||||
// Now set option defintion defaults for each specified option definition
|
||||
ConstElementPtr option_defs = global->get("option-def");
|
||||
if (option_defs) {
|
||||
BOOST_FOREACH(ElementPtr single_def, option_defs->listValue()) {
|
||||
cnt += SimpleParser::setDefaults(single_def, option_def_defaults);
|
||||
}
|
||||
}
|
||||
|
||||
ConstElementPtr options = global->get("option-data");
|
||||
if (options) {
|
||||
BOOST_FOREACH(ElementPtr single_option, options->listValue()) {
|
||||
cnt += SimpleParser::setDefaults(single_option, option_defaults);
|
||||
}
|
||||
}
|
||||
|
||||
return (cnt);
|
||||
}
|
||||
|
||||
/// @brief sets all default values for DHCPv4 and DHCPv6
|
||||
///
|
||||
/// This function largely duplicates what SimpleParser4 and SimpleParser6 classes
|
||||
/// provide. However, since there are tons of unit-tests in dhcpsrv that need
|
||||
/// this functionality and there are good reasons to keep those classes in
|
||||
/// src/bin/dhcp{4,6}, the most straightforward way is to simply copy the
|
||||
/// minimum code here. Hence this method.
|
||||
///
|
||||
/// @param config configuration structure to be filled with default values
|
||||
/// @param v6 true = DHCPv6, false = DHCPv4
|
||||
void setAllDefaults(ElementPtr config, bool v6) {
|
||||
/// This table defines default values for option definitions in DHCPv6
|
||||
const SimpleDefaults OPTION6_DEF_DEFAULTS = {
|
||||
{ "record-types", Element::string, ""},
|
||||
{ "space", Element::string, "dhcp6"},
|
||||
{ "array", Element::boolean, "false"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// This table defines default values for option definitions in DHCPv4
|
||||
const SimpleDefaults OPTION4_DEF_DEFAULTS = {
|
||||
{ "record-types", Element::string, ""},
|
||||
{ "space", Element::string, "dhcp4"},
|
||||
{ "array", Element::boolean, "false"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// This table defines default values for options in DHCPv6
|
||||
const SimpleDefaults OPTION6_DEFAULTS = {
|
||||
{ "space", Element::string, "dhcp6"},
|
||||
{ "csv-format", Element::boolean, "true"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// This table defines default values for options in DHCPv4
|
||||
const SimpleDefaults OPTION4_DEFAULTS = {
|
||||
{ "space", Element::string, "dhcp4"},
|
||||
{ "csv-format", Element::boolean, "true"},
|
||||
{ "encapsulate", Element::string, "" }
|
||||
};
|
||||
|
||||
/// This table defines default values for both DHCPv4 and DHCPv6
|
||||
const SimpleDefaults GLOBAL6_DEFAULTS = {
|
||||
{ "renew-timer", Element::integer, "900" },
|
||||
{ "rebind-timer", Element::integer, "1800" },
|
||||
{ "preferred-lifetime", Element::integer, "3600" },
|
||||
{ "valid-lifetime", Element::integer, "7200" }
|
||||
};
|
||||
|
||||
if (v6) {
|
||||
setAllDefaults(config, GLOBAL6_DEFAULTS, OPTION6_DEFAULTS,
|
||||
OPTION6_DEF_DEFAULTS);
|
||||
} else {
|
||||
setAllDefaults(config, GLOBAL6_DEFAULTS, OPTION4_DEFAULTS,
|
||||
OPTION4_DEF_DEFAULTS);
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Convenience method for parsing a configuration
|
||||
///
|
||||
/// Given a configuration string, convert it into Elements
|
||||
@@ -392,13 +515,15 @@ public:
|
||||
///
|
||||
/// @return retuns 0 if the configuration parsed successfully,
|
||||
/// non-zero otherwise failure.
|
||||
int parseConfiguration(const std::string& config) {
|
||||
int parseConfiguration(const std::string& config, bool v6 = false) {
|
||||
int rcode_ = 1;
|
||||
// Turn config into elements.
|
||||
// Test json just to make sure its valid.
|
||||
ElementPtr json = Element::fromJSON(config);
|
||||
EXPECT_TRUE(json);
|
||||
if (json) {
|
||||
setAllDefaults(json, v6);
|
||||
|
||||
ConstElementPtr status = parseElementSet(json);
|
||||
ConstElementPtr comment = parseAnswer(rcode_, status);
|
||||
error_text_ = comment->stringValue();
|
||||
@@ -566,7 +691,7 @@ TEST_F(ParseConfigTest, defaultSpaceOptionDefTest) {
|
||||
"}";
|
||||
|
||||
// Verify that the configuration string parses.
|
||||
int rcode = parseConfiguration(config);
|
||||
int rcode = parseConfiguration(config, true);
|
||||
ASSERT_EQ(0, rcode);
|
||||
|
||||
|
||||
@@ -828,7 +953,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
|
||||
" } ]"
|
||||
"}";
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_NE(0, rcode);
|
||||
|
||||
CfgMgr::instance().clear();
|
||||
@@ -844,7 +969,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
|
||||
" \"data\": \"0\""
|
||||
" } ]"
|
||||
"}";
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_NE(0, rcode);
|
||||
|
||||
CfgMgr::instance().clear();
|
||||
@@ -859,7 +984,7 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
|
||||
" \"data\": \"0\""
|
||||
" } ]"
|
||||
"}";
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
ASSERT_EQ(0, rcode);
|
||||
OptionPtr opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
|
||||
ASSERT_TRUE(opt);
|
||||
@@ -875,10 +1000,11 @@ TEST_F(ParseConfigTest, optionDataCSVFormatNoOptionDef) {
|
||||
" \"name\": \"foo-name\","
|
||||
" \"space\": \"dhcp6\","
|
||||
" \"code\": 25000,"
|
||||
" \"csv-format\": false,"
|
||||
" \"data\": \"123456\""
|
||||
" } ]"
|
||||
"}";
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
opt = getOptionPtr(DHCP6_OPTION_SPACE, 25000);
|
||||
ASSERT_TRUE(opt);
|
||||
@@ -899,7 +1025,7 @@ TEST_F(ParseConfigTest, optionDataNoName) {
|
||||
" } ]"
|
||||
"}";
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
|
||||
@@ -919,7 +1045,7 @@ TEST_F(ParseConfigTest, optionDataNoCode) {
|
||||
" } ]"
|
||||
"}";
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
|
||||
@@ -938,7 +1064,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
|
||||
" } ]"
|
||||
"}";
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 23));
|
||||
@@ -955,7 +1081,7 @@ TEST_F(ParseConfigTest, optionDataMinimal) {
|
||||
" } ]"
|
||||
"}";
|
||||
rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
|
||||
23));
|
||||
@@ -984,7 +1110,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
|
||||
"}";
|
||||
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, 2345));
|
||||
@@ -1010,7 +1136,7 @@ TEST_F(ParseConfigTest, optionDataMinimalWithOptionDef) {
|
||||
"}";
|
||||
|
||||
rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
opt = boost::dynamic_pointer_cast<Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE,
|
||||
2345));
|
||||
@@ -1031,7 +1157,7 @@ TEST_F(ParseConfigTest, emptyOptionData) {
|
||||
"}";
|
||||
|
||||
int rcode = 0;
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config));
|
||||
ASSERT_NO_THROW(rcode = parseConfiguration(config, true));
|
||||
EXPECT_EQ(0, rcode);
|
||||
const Option6AddrLstPtr opt = boost::dynamic_pointer_cast<
|
||||
Option6AddrLst>(getOptionPtr(DHCP6_OPTION_SPACE, D6O_DHCPV4_O_DHCPV6_SERVER));
|
||||
|
Reference in New Issue
Block a user