2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-30 21:45:37 +00:00

[#1405] added parser class

This commit is contained in:
Razvan Becheriu
2020-10-09 13:12:28 +03:00
parent 7666c4e25b
commit 02ac459451
20 changed files with 437 additions and 19 deletions

View File

@@ -4853,7 +4853,15 @@ following can be used:
"valid-lifetime": 600,
"subnet4": [ {
"subnet": "10.0.0.0/24",
"reservation-mode": "global",
# It is deprecated by the "reservation-modes" map.
# "reservation-mode": "global",
# Reservation modes specifying server's mode of operation when it
# fetches host reservations.
"reservation-modes": {
"global": True,
"in-subnet": False,
"out-of-pool": False
},
"pools": [ { "pool": "10.0.0.10-10.0.0.100" } ]
} ]
}
@@ -4956,7 +4964,15 @@ following example:
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
"reservation-mode": "global",
# It is deprecated by the "reservation-modes" map.
# "reservation-mode": "global",
# Reservation modes specifying server's mode of operation when it
# fetches host reservations.
"reservation-modes": {
"global": True,
"in-subnet": False,
"out-of-pool": False
},
"shared-networks": [{
"subnet4": [
{

View File

@@ -4294,7 +4294,15 @@ following can be used:
"valid-lifetime": 600,
"subnet4": [ {
"subnet": "2001:db8:1::/64",
"reservation-mode": "global",
# It is deprecated by the "reservation-modes" map.
# "reservation-mode": "global",
# Reservation modes specifying server's mode of operation when it
# fetches host reservations.
"reservation-modes": {
"global": True,
"in-subnet": False,
"out-of-pool": False
},
"pools": [ { "pool": "2001:db8:1::-2001:db8:1::100" } ]
} ]
}
@@ -4397,7 +4405,15 @@ following example:
"hw-address": "aa:bb:cc:dd:ee:fe",
"client-classes": [ "reserved_class" ]
}],
"reservation-mode": "global",
# It is deprecated by the "reservation-modes" map.
# "reservation-mode": "global",
# Reservation modes specifying server's mode of operation when it
# fetches host reservations.
"reservation-modes": {
"global": True,
"in-subnet": False,
"out-of-pool": False
},
"shared-networks": [{
"subnet6": [
{

View File

@@ -2655,7 +2655,15 @@ An example response could look as follows:
"ip-address": "0.0.0.0"
},
"renew-timer": 60,
"reservation-mode": "all",
# It is deprecated by the "reservation-modes" map.
# "reservation-mode": "all",
# Reservation modes specifying server's mode of operation when it
# fetches host reservations.
"reservation-modes": {
"global": False,
"in-subnet": True,
"out-of-pool": True
},
"subnet4": [
{
"subnet": "192.0.2.0/24",

View File

@@ -564,6 +564,15 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
}
}
ConstElementPtr reservation_mode = mutable_cfg->get("reservation-mode");
if (reservation_mode) {
reservation_mode = mutable_cfg->get("reservation-modes");
if (reservation_mode) {
isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
" and 'reservation-modes' parameters");
}
}
ConstElementPtr config_control = mutable_cfg->get("config-control");
if (config_control) {
parameter_name = "config-control";
@@ -628,6 +637,7 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
(config_pair.first == "boot-file-name") ||
(config_pair.first == "server-tag") ||
(config_pair.first == "reservation-mode") ||
(config_pair.first == "reservation-modes") ||
(config_pair.first == "calculate-tee-times") ||
(config_pair.first == "t1-percent") ||
(config_pair.first == "t2-percent") ||

View File

@@ -184,6 +184,8 @@ Parser4Context::contextName()
return ("subnet4");
case RESERVATION_MODE:
return ("reservation-mode");
case RESERVATION_MODES:
return ("reservation-modes");
case OPTION_DEF:
return ("option-def");
case OPTION_DATA:

View File

@@ -84,7 +84,7 @@ public:
/// This will parse the input as hooks-library.
PARSER_HOOKS_LIBRARY,
/// This will parse the input as dhcp-ddns.
/// This will parse the input as dhcp-ddns. (D2 client config)
PARSER_DHCP_DDNS,
/// This will parse the input as reservation-modes.
@@ -195,7 +195,7 @@ public:
/// Check if a required parameter is present in the map at the top
/// of the stack and raise an error when it is not.
///
/// @param name name of the parameter to check
/// @param name name of the parameter expected to be present
/// @param open_loc location of the opening curly bracket
/// @param close_loc location of the closing curly bracket
/// @throw Dhcp4ParseError

View File

@@ -688,6 +688,15 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
}
}
ConstElementPtr reservation_mode = mutable_cfg->get("reservation-mode");
if (reservation_mode) {
reservation_mode = mutable_cfg->get("reservation-modes");
if (reservation_mode) {
isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
" and 'reservation-modes' parameters");
}
}
ConstElementPtr config_control = mutable_cfg->get("config-control");
if (config_control) {
parameter_name = "config-control";
@@ -761,6 +770,7 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set,
(config_pair.first == "dhcp4o6-port") ||
(config_pair.first == "server-tag") ||
(config_pair.first == "reservation-mode") ||
(config_pair.first == "reservation-modes") ||
(config_pair.first == "calculate-tee-times") ||
(config_pair.first == "t1-percent") ||
(config_pair.first == "t2-percent") ||

View File

@@ -181,6 +181,8 @@ Parser6Context::contextName()
return ("subnet6");
case RESERVATION_MODE:
return ("reservation-mode");
case RESERVATION_MODES:
return ("reservation-modes");
case OPTION_DEF:
return ("option-def");
case OPTION_DATA:

View File

@@ -360,7 +360,7 @@
// specific options.
//
// When using reservations, it is useful to configure
// reservation-mode (subnet specific parameter) and
// reservation-modes (subnet specific parameter) and
// host-reservation-identifiers (global parameter).
{
"client-id": "01:12:23:34:45:56:67",

View File

@@ -45,6 +45,8 @@ EXTRA_DIST += parsers/ifaces_config_parser.h
EXTRA_DIST += parsers/multi_threading_config_parser.cc
EXTRA_DIST += parsers/multi_threading_config_parser.h
EXTRA_DIST += parsers/option_data_parser.h
EXTRA_DIST += parsers/reservation_modes_parser.cc
EXTRA_DIST += parsers/reservation_modes_parser.h
EXTRA_DIST += parsers/sanity_checks_parser.cc
EXTRA_DIST += parsers/sanity_checks_parser.h
EXTRA_DIST += parsers/simple_parser4.cc
@@ -178,6 +180,8 @@ libkea_dhcpsrv_la_SOURCES += parsers/option_data_parser.cc
libkea_dhcpsrv_la_SOURCES += parsers/option_data_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/dhcp_queue_control_parser.cc
libkea_dhcpsrv_la_SOURCES += parsers/dhcp_queue_control_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/reservation_modes_parser.cc
libkea_dhcpsrv_la_SOURCES += parsers/reservation_modes_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/sanity_checks_parser.cc
libkea_dhcpsrv_la_SOURCES += parsers/sanity_checks_parser.h
libkea_dhcpsrv_la_SOURCES += parsers/shared_network_parser.cc
@@ -408,6 +412,7 @@ libkea_dhcpsrv_parsers_include_HEADERS = \
parsers/multi_threading_config_parser.h \
parsers/option_data_parser.h \
parsers/dhcp_queue_control_parser.h \
parsers/reservation_modes_parser.h \
parsers/sanity_checks_parser.h \
parsers/shared_network_parser.h \
parsers/shared_networks_list_parser.h \

View File

@@ -155,7 +155,7 @@ public:
/// @brief Specifies allowed host reservation mode.
///
typedef enum {
typedef enum : uint8_t {
/// None - host reservation is disabled. No reservation types
/// are allowed.
@@ -181,7 +181,10 @@ public:
/// AllocEngine code has to check whether there are reservations, even
/// when dealing with reservations from within the dynamic pools.
HR_ALL = HR_IN_SUBNET | HR_OUT_OF_POOL
} HRMode;
} HRModeFlag;
/// @brief Bitset used to store @ref HRModeFlag flags.
typedef uint8_t HRMode;
/// @brief Inheritance "mode" used when fetching an optional @c Network
/// parameter.
@@ -811,14 +814,39 @@ protected:
template<typename ReturnType>
ReturnType getGlobalProperty(ReturnType property,
const std::string& global_name) const {
if (!global_name.empty() && fetch_globals_fn_) {
std::string member_name;
std::string search_name = global_name;
auto found = global_name.find('.');
if (found != std::string::npos) {
if (std::count(global_name.begin(), global_name.end(), '.') > 1) {
isc_throw(BadValue, "more than one level of indirection found in: "
<< global_name);
}
member_name = global_name.substr(found + 1, global_name.length() - found - 1);
search_name = global_name.substr(0, found);
}
if (!search_name.empty() && fetch_globals_fn_) {
data::ConstElementPtr globals = fetch_globals_fn_();
if (globals && (globals->getType() == data::Element::map)) {
data::ConstElementPtr global_param = globals->get(global_name);
data::ConstElementPtr global_param = globals->get(search_name);
if (global_param) {
// If there is a global parameter, convert it to the
// optional value of the given type and return.
return (data::ElementValue<typename ReturnType::ValueType>()(global_param));
if (!member_name.empty()) {
if (global_param->getType() != data::Element::map) {
isc_throw(BadValue, "the parameter: " << global_name
<< " must be a map");
}
auto member_element = global_param->get(member_name);
if (member_element) {
// If there is a global parameter with the specified
// member, convert the member to the optional value
// of the given type and return.
return (data::ElementValue<typename ReturnType::ValueType>()(member_element));
}
} else {
// If there is a global parameter, convert it to the
// optional value of the given type and return.
return (data::ElementValue<typename ReturnType::ValueType>()(global_param));
}
}
}
}

View File

@@ -7,6 +7,7 @@
#include <config.h>
#include <dhcpsrv/triplet.h>
#include <dhcpsrv/parsers/base_network_parser.h>
#include <dhcpsrv/parsers/reservation_modes_parser.h>
#include <util/optional.h>
#include <util/strutil.h>
@@ -201,6 +202,10 @@ void
BaseNetworkParser::parseHostReservationMode(const data::ConstElementPtr& network_data,
NetworkPtr& network) {
if (network_data->contains("reservation-mode")) {
if (network_data->contains("reservation-modes")) {
isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
" and 'reservation-modes' parameters");
}
try {
std::string hr_mode = getString(network_data, "reservation-mode");
network->setHostReservationMode(Network::hrModeFromString(hr_mode));
@@ -212,6 +217,27 @@ BaseNetworkParser::parseHostReservationMode(const data::ConstElementPtr& network
}
}
void
BaseNetworkParser::parseHostReservationModes(const data::ConstElementPtr& network_data,
NetworkPtr& network) {
if (network_data->contains("reservation-modes")) {
if (network_data->contains("reservation-mode")) {
isc_throw(DhcpConfigError, "invalid use of both 'reservation-mode'"
" and 'reservation-modes' parameters");
}
try {
auto reservation_modes = network_data->get("reservation-modes");
HostReservationModesParser parser;
Network::HRMode flags = parser.parse(reservation_modes);
network->setHostReservationMode(flags);
} catch (const BadValue& ex) {
isc_throw(DhcpConfigError, "invalid reservation-modes parameter: "
<< ex.what() << " (" << getPosition("reservation-modes",
network_data) << ")");
}
}
}
void
BaseNetworkParser::parseDdnsParams(const data::ConstElementPtr& network_data,
NetworkPtr& network) {

View File

@@ -80,6 +80,9 @@ protected:
/// @brief Parses host reservation mode.
///
/// @note Configuring 'reservation-mode' is deprecated. The new map
/// 'reservation-modes' should be used.
///
/// @param network_data Data element holding shared network
/// configuration to be parsed.
/// @param [out] network Pointer to a network in which parsed data is
@@ -87,6 +90,15 @@ protected:
void parseHostReservationMode(const data::ConstElementPtr& network_data,
NetworkPtr& network);
/// @brief Parses host reservation modes.
///
/// @param network_data Data element holding shared network
/// configuration to be parsed.
/// @param [out] network Pointer to a network in which parsed data is
/// to be stored.
void parseHostReservationModes(const data::ConstElementPtr& network_data,
NetworkPtr& network);
/// @brief Parses parameters pertaining to DDNS behavior.
///
/// The parsed parameters are:

View File

@@ -859,7 +859,11 @@ Subnet4ConfigParser::initSubnet(data::ConstElementPtr params,
}
}
// Let's set host reservation mode.
// reservation-modes
parseHostReservationModes(params, network);
// Let's set host reservation mode. If not specified, the default value of
// all will be used.
parseHostReservationMode(params, network);
// Try setting up client class.
@@ -1330,6 +1334,9 @@ Subnet6ConfigParser::initSubnet(data::ConstElementPtr params,
subnet6->setIface(iface);
}
// reservation-modes
parseHostReservationModes(params, network);
// Let's set host reservation mode. If not specified, the default value of
// all will be used.
parseHostReservationMode(params, network);

View File

@@ -0,0 +1,59 @@
// Copyright (C) 2015-2020 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 <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/parsers/reservation_modes_parser.h>
#include <string>
#include <sys/types.h>
using namespace isc::data;
using namespace isc::util;
namespace isc {
namespace dhcp {
Network::HRMode
HostReservationModesParser::parse(const ConstElementPtr& control_elem) {
if (control_elem->getType() != Element::map) {
isc_throw(DhcpConfigError, "reservation-modes must be a map");
}
ConstElementPtr elem;
uint8_t flags = 0;
elem = control_elem->get("global");
if (elem) {
bool value = elem->boolValue();
if (value) {
flags |= Network::HR_GLOBAL;
}
}
elem = control_elem->get("in-subnet");
if (elem) {
bool value = elem->boolValue();
if (value) {
flags |= Network::HR_IN_SUBNET;
}
}
elem = control_elem->get("out-of-pool");
if (elem) {
bool value = elem->boolValue();
if (value) {
flags |= Network::HR_OUT_OF_POOL;
}
}
return (static_cast<Network::HRMode>(flags));
}
} // end of namespace isc::dhcp
} // end of namespace isc

View File

@@ -0,0 +1,46 @@
// Copyright (C) 2018 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 RESERVATION_MODES_PARSER_H
#define RESERVATION_MODES_PARSER_H
#include <cc/data.h>
#include <cc/simple_parser.h>
#include <dhcpsrv/network.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
namespace isc {
namespace dhcp {
/// @brief Parser for the configuration of DHCP packet queue controls
///
/// This parser parses the "reservation-modes" parameter which holds the
/// the configurable parameters that tailor host reservation modes.
///
/// This parser is used in both DHCPv4 and DHCPv6, and also inside subnet and
/// shared networks.
class HostReservationModesParser : public isc::data::SimpleParser {
public:
/// @brief Constructor
///
HostReservationModesParser(){};
/// @brief Parses content of the "reservation-modes".
///
/// @param control_elem MapElement containing the host reservation modes
/// values to parse
///
/// @return Host reservation modes flags.
///
/// @throw DhcpConfigError if any of the values are invalid.
Network::HRMode parse(const isc::data::ConstElementPtr& control_elem);
};
}
} // end of namespace isc
#endif // RESERVATION_MODES_PARSER_H

View File

@@ -185,6 +185,9 @@ SharedNetwork4Parser::parse(const data::ConstElementPtr& shared_network_data) {
}
}
// reservation-modes
parseHostReservationModes(shared_network_data, network);
// reservation-mode
parseHostReservationMode(shared_network_data, network);
@@ -351,6 +354,9 @@ SharedNetwork6Parser::parse(const data::ConstElementPtr& shared_network_data) {
}
}
// reservation-modes
parseHostReservationModes(shared_network_data, network);
// reservation-mode
parseHostReservationMode(shared_network_data, network);

View File

@@ -87,6 +87,7 @@ libdhcpsrv_unittests_SOURCES += csv_lease_file6_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_client_unittest.cc
libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
libdhcpsrv_unittests_SOURCES += dhcp_queue_control_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += reservation_modes_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += dhcp4o6_ipc_unittest.cc
libdhcpsrv_unittests_SOURCES += duid_config_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += expiration_config_parser_unittest.cc

View File

@@ -48,7 +48,7 @@ DHCPQueueControlParserTest::TearDown() {
}
// Verifies that DHCPQueueControlParser handles
// expected valid dhcp-queue-control contet
// expected valid dhcp-queue-control content
TEST_F(DHCPQueueControlParserTest, validContent) {
struct Scenario {
std::string description_;
@@ -153,9 +153,8 @@ TEST_F(DHCPQueueControlParserTest, invalidContent) {
}
};
// Iterate over the valid scenarios and verify they succeed.
// Iterate over the invalid scenarios and verify they throw exception.
ConstElementPtr config_elems;
ConstElementPtr queue_control;
for (auto scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
{

View File

@@ -0,0 +1,165 @@
// Copyright (C) 2018-2020 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 <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/parsers/reservation_modes_parser.h>
#include <testutils/test_to_element.h>
#include <gtest/gtest.h>
using namespace isc::data;
using namespace isc::dhcp;
using namespace isc::test;
using namespace isc::util;
namespace {
/// @brief Test fixture class for @c HostReservationModesParser
class HostReservationModesParserTest : public ::testing::Test {
protected:
/// @brief Setup for each test.
///
/// Clears the configuration in the @c CfgMgr.
virtual void SetUp();
/// @brief Cleans up after each test.
///
/// Clears the configuration in the @c CfgMgr.
virtual void TearDown();
};
void
HostReservationModesParserTest::SetUp() {
CfgMgr::instance().clear();
}
void
HostReservationModesParserTest::TearDown() {
CfgMgr::instance().clear();
}
// Verifies that HostReservationModesParser handles
// expected valid reservation-modes content
TEST_F(HostReservationModesParserTest, validContent) {
struct Scenario {
std::string description_;
std::string json_;
};
std::vector<Scenario> scenarios = {
{
"queue disabled",
"{ \n"
" \"enable-queue\": false \n"
"} \n"
},
{
"queue disabled, arbitrary content allowed",
"{ \n"
" \"enable-queue\": false, \n"
" \"foo\": \"bogus\", \n"
" \"random-int\" : 1234 \n"
"} \n"
},
{
"queue enabled, with queue-type",
"{ \n"
" \"enable-queue\": true, \n"
" \"queue-type\": \"some-type\" \n"
"} \n"
},
{
"queue enabled with queue-type and arbitrary content",
"{ \n"
" \"enable-queue\": true, \n"
" \"queue-type\": \"some-type\", \n"
" \"foo\": \"bogus\", \n"
" \"random-int\" : 1234 \n"
"} \n"
}
};
// Iterate over the valid scenarios and verify they succeed.
ConstElementPtr config_elems;
Network::HRMode reservation_modes;
for (auto scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
{
// Construct the config JSON
ASSERT_NO_THROW(config_elems = Element::fromJSON(scenario.json_))
<< "invalid JSON, test is broken";
// Parsing config into a reservation modes parser should succeed.
HostReservationModesParser parser;
try {
reservation_modes = parser.parse(config_elems);
} catch (const std::exception& ex) {
ADD_FAILURE() << "parser threw an exception: " << ex.what();
}
// Verify the resultant reservation-modes.
ASSERT_TRUE(reservation_modes);
}
}
}
// Verifies that HostReservationModesParser correctly catches
// invalid reservation-modes content
TEST_F(HostReservationModesParserTest, invalidContent) {
struct Scenario {
std::string description_;
std::string json_;
};
std::vector<Scenario> scenarios = {
{
"enable-queue missing",
"{ \n"
" \"enable-type\": \"some-type\" \n"
"} \n"
},
{
"enable-queue not boolean",
"{ \n"
" \"enable-queue\": \"always\" \n"
"} \n"
},
{
"queue enabled, type missing",
"{ \n"
" \"enable-queue\": true \n"
"} \n"
},
{
"queue enabled, type not a string",
"{ \n"
" \"enable-queue\": true, \n"
" \"queue-type\": 7777 \n"
"} \n"
}
};
// Iterate over the invalid scenarios and verify they throw exception.
ConstElementPtr config_elems;
for (auto scenario : scenarios) {
SCOPED_TRACE(scenario.description_);
{
// Construct the config JSON
ASSERT_NO_THROW(config_elems = Element::fromJSON(scenario.json_))
<< "invalid JSON, test is broken";
// Parsing config into a reservation modes parser should succeed.
HostReservationModesParser parser;
EXPECT_THROW(parser.parse(config_elems), DhcpConfigError);
}
}
}
}; // anonymous namespace