2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-23 18:37:35 +00:00
kea/src/bin/dhcp4/json_config_parser.cc

988 lines
43 KiB
C++
Raw Normal View History

2023-01-23 09:49:20 -08:00
// Copyright (C) 2012-2023 Internet Systems Consortium, Inc. ("ISC")
2012-10-11 19:08:23 +02:00
//
// 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/.
2012-10-11 19:08:23 +02:00
#include <config.h>
#include <cc/command_interpreter.h>
#include <config/command_mgr.h>
#include <database/dbaccess_parser.h>
#include <database/backend_selector.h>
#include <database/server_selector.h>
#include <dhcp4/ctrl_dhcp4_srv.h>
2012-10-11 19:08:23 +02:00
#include <dhcp4/dhcp4_log.h>
#include <dhcp4/dhcp4_srv.h>
#include <dhcp4/json_config_parser.h>
2020-04-16 14:42:34 +03:00
#include <dhcp/libdhcp++.h>
#include <dhcp/option_definition.h>
#include <dhcpsrv/cb_ctl_dhcp4.h>
#include <dhcpsrv/cfg_multi_threading.h>
#include <dhcpsrv/cfg_option.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/config_backend_dhcp4_mgr.h>
2018-03-08 02:49:19 +01:00
#include <dhcpsrv/db_type.h>
#include <dhcpsrv/parsers/client_class_def_parser.h>
#include <dhcpsrv/parsers/dhcp_parsers.h>
#include <dhcpsrv/parsers/expiration_config_parser.h>
#include <dhcpsrv/parsers/host_reservation_parser.h>
#include <dhcpsrv/parsers/host_reservations_list_parser.h>
#include <dhcpsrv/parsers/ifaces_config_parser.h>
#include <dhcpsrv/parsers/multi_threading_config_parser.h>
#include <dhcpsrv/parsers/option_data_parser.h>
#include <dhcpsrv/parsers/dhcp_queue_control_parser.h>
#include <dhcpsrv/parsers/simple_parser4.h>
#include <dhcpsrv/parsers/shared_networks_list_parser.h>
#include <dhcpsrv/parsers/sanity_checks_parser.h>
2020-04-16 14:42:34 +03:00
#include <dhcpsrv/host_data_source_factory.h>
#include <dhcpsrv/timer_mgr.h>
2020-06-25 15:29:33 +02:00
#include <hooks/hooks_manager.h>
#include <hooks/hooks_parser.h>
#include <process/config_ctl_parser.h>
#include <util/encode/hex.h>
#include <util/multi_threading_mgr.h>
#include <util/strutil.h>
#include <boost/algorithm/string.hpp>
2012-12-12 13:02:40 +01:00
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <iomanip>
2020-04-16 14:42:34 +03:00
#include <iostream>
#include <limits>
#include <map>
#include <netinet/in.h>
#include <vector>
2012-10-11 19:08:23 +02:00
using namespace std;
using namespace isc;
2020-04-16 14:42:34 +03:00
using namespace isc::data;
using namespace isc::dhcp;
2012-10-11 19:08:23 +02:00
using namespace isc::asiolink;
using namespace isc::hooks;
using namespace isc::process;
2018-10-17 15:05:27 -04:00
using namespace isc::config;
using namespace isc::util;
2012-10-11 19:08:23 +02:00
namespace {
/// @brief Parser that takes care of global DHCPv4 parameters and utility
/// functions that work on global level.
///
/// This class is a collection of utility method that either handle
/// global parameters (see @ref parse), or conducts operations on
/// global level (see @ref sanityChecks and @ref copySubnets4).
///
/// See @ref parse method for a list of supported parameters.
class Dhcp4ConfigParser : public isc::data::SimpleParser {
public:
/// @brief Sets global parameters in staging configuration
///
/// @param global global configuration scope
/// @param cfg Server configuration (parsed parameters will be stored here)
///
/// Currently this method sets the following global parameters:
///
/// - echo-client-id
/// - decline-probation-period
/// - dhcp4o6-port
2017-11-29 08:09:49 +01:00
/// - user-context
///
/// @throw DhcpConfigError if parameters are missing or
/// or having incorrect values.
void parse(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
// Set whether v4 server is supposed to echo back client-id
// (yes = RFC6842 compatible, no = backward compatibility)
bool echo_client_id = getBoolean(global, "echo-client-id");
cfg->setEchoClientId(echo_client_id);
// Set the probation period for decline handling.
uint32_t probation_period =
getUint32(global, "decline-probation-period");
cfg->setDeclinePeriod(probation_period);
// Set the DHCPv4-over-DHCPv6 interserver port.
uint16_t dhcp4o6_port = getUint16(global, "dhcp4o6-port");
cfg->setDhcp4o6Port(dhcp4o6_port);
2017-11-29 08:09:49 +01:00
// Set the global user context.
ConstElementPtr user_context = global->get("user-context");
if (user_context) {
cfg->setContext(user_context);
}
// Set the server's logical name
std::string server_tag = getString(global, "server-tag");
cfg->setServerTag(server_tag);
}
/// @brief Sets global parameters before other parameters are parsed.
///
/// This method sets selected global parameters before other parameters
2021-01-22 01:36:41 +02:00
/// are parsed. This is important when the behavior of the parsers
/// run later depends on these global parameters.
///
/// Currently this method sets the following global parameters:
/// - ip-reservations-unique
///
/// @param global global configuration scope
/// @param cfg Server configuration (parsed parameters will be stored here)
void parseEarly(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
// Set ip-reservations-unique flag.
bool ip_reservations_unique = getBoolean(global, "ip-reservations-unique");
cfg->setIPReservationsUnique(ip_reservations_unique);
}
/// @brief Copies subnets from shared networks to regular subnets container
///
/// @param from pointer to shared networks container (copy from here)
/// @param dest pointer to cfg subnets4 (copy to here)
/// @throw BadValue if any pointer is missing
/// @throw DhcpConfigError if there are duplicates (or other subnet defects)
void
copySubnets4(const CfgSubnets4Ptr& dest, const CfgSharedNetworks4Ptr& from) {
if (!dest || !from) {
isc_throw(BadValue, "Unable to copy subnets: at least one pointer is null");
}
const SharedNetwork4Collection* networks = from->getAll();
if (!networks) {
// Nothing to copy. Technically, it should return a pointer to empty
// container, but let's handle null pointer as well.
return;
}
// Let's go through all the networks one by one
for (auto net = networks->begin(); net != networks->end(); ++net) {
// For each network go through all the subnets in it.
2022-01-02 23:18:57 +01:00
const Subnet4SimpleCollection* subnets = (*net)->getAllSubnets();
if (!subnets) {
// Shared network without subnets it weird, but we decided to
// accept such configurations.
continue;
}
// For each subnet, add it to a list of regular subnets.
for (auto subnet = subnets->begin(); subnet != subnets->end(); ++subnet) {
dest->add(*subnet);
}
}
}
/// @brief Conducts global sanity checks
///
/// This method is very simple now, but more sanity checks are expected
/// in the future.
///
/// @param cfg - the parsed structure
/// @param global global Dhcp4 scope
/// @throw DhcpConfigError in case of issues found
void
sanityChecks(const SrvConfigPtr& cfg, const ConstElementPtr& global) {
/// Global lifetime sanity checks
cfg->sanityChecksLifetime("valid-lifetime");
/// Shared network sanity checks
const SharedNetwork4Collection* networks = cfg->getCfgSharedNetworks4()->getAll();
if (networks) {
sharedNetworksSanityChecks(*networks, global->get("shared-networks"));
}
}
/// @brief Sanity checks for shared networks
///
/// This method verifies if there are no issues with shared networks.
/// @param networks pointer to shared networks being checked
/// @param json shared-networks element
/// @throw DhcpConfigError if issues are encountered
void
sharedNetworksSanityChecks(const SharedNetwork4Collection& networks,
ConstElementPtr json) {
/// @todo: in case of errors, use json to extract line numbers.
if (!json) {
// No json? That means that the shared-networks was never specified
// in the config.
return;
}
// Used for names uniqueness checks.
std::set<string> names;
// Let's go through all the networks one by one
for (auto net = networks.begin(); net != networks.end(); ++net) {
string txt;
// Let's check if all subnets have either the same interface
// or don't have the interface specified at all.
bool authoritative = (*net)->getAuthoritative();
string iface = (*net)->getIface();
2022-01-02 23:18:57 +01:00
const Subnet4SimpleCollection* subnets = (*net)->getAllSubnets();
if (subnets) {
// For each subnet, add it to a list of regular subnets.
for (auto subnet = subnets->begin(); subnet != subnets->end(); ++subnet) {
if ((*subnet)->getAuthoritative() != authoritative) {
isc_throw(DhcpConfigError, "Subnet " << boolalpha
<< (*subnet)->toText()
<< " has different authoritative setting "
<< (*subnet)->getAuthoritative()
<< " than the shared-network itself: "
<< authoritative);
}
if (iface.empty()) {
iface = (*subnet)->getIface();
continue;
}
if ((*subnet)->getIface().empty()) {
continue;
}
if ((*subnet)->getIface() != iface) {
isc_throw(DhcpConfigError, "Subnet " << (*subnet)->toText()
<< " has specified interface " << (*subnet)->getIface()
<< ", but earlier subnet in the same shared-network"
<< " or the shared-network itself used " << iface);
}
// Let's collect the subnets in case we later find out the
// subnet doesn't have a mandatory name.
txt += (*subnet)->toText() + " ";
}
}
// Next, let's check name of the shared network.
if ((*net)->getName().empty()) {
isc_throw(DhcpConfigError, "Shared-network with subnets "
<< txt << " is missing mandatory 'name' parameter");
}
// Is it unique?
if (names.find((*net)->getName()) != names.end()) {
isc_throw(DhcpConfigError, "A shared-network with "
"name " << (*net)->getName() << " defined twice.");
}
names.insert((*net)->getName());
}
}
};
} // anonymous namespace
namespace isc {
namespace dhcp {
/// @brief Initialize the command channel based on the staging configuration
///
/// Only close the current channel, if the new channel configuration is
/// different. This avoids disconnecting a client and hence not sending them
/// a command result, unless they specifically alter the channel configuration.
/// In that case the user simply has to accept they'll be disconnected.
///
void configureCommandChannel() {
// Get new socket configuration.
ConstElementPtr sock_cfg =
CfgMgr::instance().getStagingCfg()->getControlSocketInfo();
// Get current socket configuration.
ConstElementPtr current_sock_cfg =
CfgMgr::instance().getCurrentCfg()->getControlSocketInfo();
// Determine if the socket configuration has changed. It has if
// both old and new configuration is specified but respective
2017-07-23 11:23:40 -04:00
// data elements aren't equal.
bool sock_changed = (sock_cfg && current_sock_cfg &&
!sock_cfg->equals(*current_sock_cfg));
// If the previous or new socket configuration doesn't exist or
// the new configuration differs from the old configuration we
2017-07-23 12:54:55 -04:00
// close the existing socket and open a new socket as appropriate.
// Note that closing an existing socket means the client will not
// receive the configuration result.
if (!sock_cfg || !current_sock_cfg || sock_changed) {
// Close the existing socket (if any).
isc::config::CommandMgr::instance().closeCommandSocket();
if (sock_cfg) {
// This will create a control socket and install the external
// socket in IfaceMgr. That socket will be monitored when
// Dhcp4Srv::receivePacket() calls IfaceMgr::receive4() and
// callback in CommandMgr will be called, if necessary.
isc::config::CommandMgr::instance().openCommandSocket(sock_cfg);
}
}
}
/// @brief Process a DHCPv4 confguration and return an answer stating if the
/// configuration is valid, or specifying details about the error otherwise.
///
/// @param config_set the configuration being processed
2012-10-11 19:08:23 +02:00
isc::data::ConstElementPtr
processDhcp4Config(isc::data::ConstElementPtr config_set) {
// Before starting any subnet operations, let's reset the subnet-id counter,
// so newly recreated configuration starts with first subnet-id equal 1.
Subnet::resetSubnetID();
// Revert any runtime option definitions configured so far and not committed.
LibDHCP::revertRuntimeOptionDefs();
// Let's set empty container in case a user hasn't specified any configuration
2017-07-23 11:56:50 -04:00
// for option definitions. This is equivalent to committing empty container.
LibDHCP::setRuntimeOptionDefs(OptionDefSpaceContainer());
// Print the list of known backends.
HostDataSourceFactory::printRegistered();
// Answer will hold the result.
ConstElementPtr answer;
2020-05-22 10:34:36 +02:00
// Global parameter name in case of an error.
string parameter_name;
ElementPtr mutable_cfg;
SrvConfigPtr srv_config;
2012-10-11 19:08:23 +02:00
try {
// Get the staging configuration.
srv_config = CfgMgr::instance().getStagingCfg();
// 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.
mutable_cfg = boost::const_pointer_cast<Element>(config_set);
// Relocate dhcp-ddns parameters that have moved to global scope.
// Rule is that a global value overrides the dhcp-ddns value, so
// we need to do this before we apply global defaults.
// Note this is done for backward compatibility.
srv_config->moveDdnsParams(mutable_cfg);
2020-11-11 19:06:06 +02:00
// Move from reservation mode to new reservations flags.
2020-11-16 21:24:05 +02:00
// @todo add warning
BaseNetworkParser::moveReservationMode(mutable_cfg);
2020-11-11 19:06:06 +02:00
// Set all default values if not specified by the user.
SimpleParser4::setAllDefaults(mutable_cfg);
// And now derive (inherit) global parameters to subnets, if not specified.
SimpleParser4::deriveParameters(mutable_cfg);
2020-05-22 10:34:36 +02:00
// In principle we could have the following code structured as a series
// of long if else if clauses. That would give a marginal performance
// boost, but would make the code less readable. We had serious issues
// with the parser code debugability, so I decided to keep it as a
// series of independent ifs.
// This parser is used in several places.
Dhcp4ConfigParser global_parser;
// Apply global options in the staging config, e.g. ip-reservations-unique
global_parser.parseEarly(srv_config, mutable_cfg);
// We need definitions first
ConstElementPtr option_defs = mutable_cfg->get("option-def");
if (option_defs) {
2020-05-22 10:34:36 +02:00
parameter_name = "option-def";
OptionDefListParser parser(AF_INET);
CfgOptionDefPtr cfg_option_def = srv_config->getCfgOptionDef();
parser.parse(cfg_option_def, option_defs);
}
2020-05-22 10:34:36 +02:00
ConstElementPtr option_datas = mutable_cfg->get("option-data");
if (option_datas) {
parameter_name = "option-data";
OptionDataListParser parser(AF_INET);
CfgOptionPtr cfg_option = srv_config->getCfgOption();
2020-05-22 10:34:36 +02:00
parser.parse(cfg_option, option_datas);
}
2020-05-22 10:34:36 +02:00
ConstElementPtr control_socket = mutable_cfg->get("control-socket");
if (control_socket) {
parameter_name = "control-socket";
ControlSocketParser parser;
parser.parse(*srv_config, control_socket);
2020-05-22 10:34:36 +02:00
}
2020-05-22 10:34:36 +02:00
ConstElementPtr multi_threading = mutable_cfg->get("multi-threading");
if (multi_threading) {
parameter_name = "multi-threading";
MultiThreadingConfigParser parser;
parser.parse(*srv_config, multi_threading);
2020-05-22 10:34:36 +02:00
}
bool multi_threading_enabled = true;
uint32_t thread_count = 0;
uint32_t queue_size = 0;
CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
multi_threading_enabled, thread_count, queue_size);
/// depends on "multi-threading" being enabled, so it must come after.
2020-05-22 10:34:36 +02:00
ConstElementPtr queue_control = mutable_cfg->get("dhcp-queue-control");
if (queue_control) {
parameter_name = "dhcp-queue-control";
DHCPQueueControlParser parser;
srv_config->setDHCPQueueControl(parser.parse(queue_control, multi_threading_enabled));
}
/// depends on "multi-threading" being enabled, so it must come after.
ConstElementPtr reservations_lookup_first = mutable_cfg->get("reservations-lookup-first");
if (reservations_lookup_first) {
parameter_name = "reservations-lookup-first";
if (multi_threading_enabled) {
LOG_WARN(dhcp4_logger, DHCP4_RESERVATIONS_LOOKUP_FIRST_ENABLED);
}
srv_config->setReservationsLookupFirst(reservations_lookup_first->boolValue());
2020-05-22 10:34:36 +02:00
}
2020-05-22 10:34:36 +02:00
ConstElementPtr hr_identifiers =
mutable_cfg->get("host-reservation-identifiers");
if (hr_identifiers) {
parameter_name = "host-reservation-identifiers";
HostReservationIdsParser4 parser;
parser.parse(hr_identifiers);
}
2020-05-22 10:34:36 +02:00
ConstElementPtr sanity_checks = mutable_cfg->get("sanity-checks");
if (sanity_checks) {
parameter_name = "sanity-checks";
SanityChecksParser parser;
parser.parse(*srv_config, sanity_checks);
2020-05-22 10:34:36 +02:00
}
2020-05-22 10:34:36 +02:00
ConstElementPtr expiration_cfg =
mutable_cfg->get("expired-leases-processing");
if (expiration_cfg) {
parameter_name = "expired-leases-processing";
ExpirationConfigParser parser;
parser.parse(expiration_cfg);
}
2020-05-22 10:34:36 +02:00
// The hooks-libraries configuration must be parsed after parsing
// multi-threading configuration so that libraries are checked
// for multi-threading compatibility.
ConstElementPtr hooks_libraries = mutable_cfg->get("hooks-libraries");
if (hooks_libraries) {
parameter_name = "hooks-libraries";
HooksLibrariesParser hooks_parser;
HooksConfig& libraries = srv_config->getHooksConfig();
2020-05-22 10:34:36 +02:00
hooks_parser.parse(libraries, hooks_libraries);
libraries.verifyLibraries(hooks_libraries->getPosition(),
multi_threading_enabled);
2020-05-22 10:34:36 +02:00
}
2020-05-22 10:34:36 +02:00
// D2 client configuration.
D2ClientConfigPtr d2_client_cfg;
2017-01-07 07:18:40 +01:00
2020-05-22 10:34:36 +02:00
// Legacy DhcpConfigParser stuff below.
ConstElementPtr dhcp_ddns = mutable_cfg->get("dhcp-ddns");
if (dhcp_ddns) {
parameter_name = "dhcp-ddns";
// Apply defaults
D2ClientConfigParser::setAllDefaults(dhcp_ddns);
D2ClientConfigParser parser;
d2_client_cfg = parser.parse(dhcp_ddns);
}
2020-05-22 10:34:36 +02:00
ConstElementPtr client_classes = mutable_cfg->get("client-classes");
if (client_classes) {
parameter_name = "client-classes";
ClientClassDefListParser parser;
ClientClassDictionaryPtr dictionary =
parser.parse(client_classes, AF_INET);
srv_config->setClientClassDictionary(dictionary);
2020-05-22 10:34:36 +02:00
}
2020-05-22 10:34:36 +02:00
// Please move at the end when migration will be finished.
ConstElementPtr lease_database = mutable_cfg->get("lease-database");
if (lease_database) {
parameter_name = "lease-database";
db::DbAccessParser parser;
std::string access_string;
parser.parse(access_string, lease_database);
CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
2020-05-22 10:34:36 +02:00
cfg_db_access->setLeaseDbAccessString(access_string);
}
2020-05-22 10:34:36 +02:00
ConstElementPtr hosts_database = mutable_cfg->get("hosts-database");
if (hosts_database) {
parameter_name = "hosts-database";
db::DbAccessParser parser;
std::string access_string;
parser.parse(access_string, hosts_database);
CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
2020-05-22 10:34:36 +02:00
cfg_db_access->setHostDbAccessString(access_string);
}
2020-05-22 10:34:36 +02:00
ConstElementPtr hosts_databases = mutable_cfg->get("hosts-databases");
if (hosts_databases) {
parameter_name = "hosts-databases";
CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess();
for (auto const& it : hosts_databases->listValue()) {
db::DbAccessParser parser;
std::string access_string;
2020-05-22 10:34:36 +02:00
parser.parse(access_string, it);
cfg_db_access->setHostDbAccessString(access_string);
}
2020-05-22 10:34:36 +02:00
}
// Keep relative orders of shared networks and subnets.
2020-05-22 10:34:36 +02:00
ConstElementPtr shared_networks = mutable_cfg->get("shared-networks");
if (shared_networks) {
parameter_name = "shared-networks";
/// We need to create instance of SharedNetworks4ListParser
/// and parse the list of the shared networks into the
/// CfgSharedNetworks4 object. One additional step is then to
/// add subnets from the CfgSharedNetworks4 into CfgSubnets4
/// as well.
SharedNetworks4ListParser parser;
CfgSharedNetworks4Ptr cfg = srv_config->getCfgSharedNetworks4();
2020-05-22 10:34:36 +02:00
parser.parse(cfg, shared_networks);
// We also need to put the subnets it contains into normal
// subnets list.
global_parser.copySubnets4(srv_config->getCfgSubnets4(), cfg);
2020-05-22 10:34:36 +02:00
}
ConstElementPtr subnet4 = mutable_cfg->get("subnet4");
if (subnet4) {
parameter_name = "subnet4";
Subnets4ListConfigParser subnets_parser;
// parse() returns number of subnets parsed. We may log it one day.
subnets_parser.parse(srv_config, subnet4);
}
2020-05-22 10:34:36 +02:00
ConstElementPtr reservations = mutable_cfg->get("reservations");
if (reservations) {
parameter_name = "reservations";
HostCollection hosts;
HostReservationsListParser<HostReservationParser4> parser;
parser.parse(SUBNET_ID_GLOBAL, reservations, hosts);
for (auto h = hosts.begin(); h != hosts.end(); ++h) {
srv_config->getCfgHosts()->add(*h);
}
2020-05-22 10:34:36 +02:00
}
2020-05-22 10:34:36 +02:00
ConstElementPtr config_control = mutable_cfg->get("config-control");
if (config_control) {
parameter_name = "config-control";
ConfigControlParser parser;
ConfigControlInfoPtr config_ctl_info = parser.parse(config_control);
CfgMgr::instance().getStagingCfg()->setConfigControlInfo(config_ctl_info);
}
[5704] host backends and kea-dhcp4/6 support global HR storage - Added constants for special SubnetIDs: SUBNET_ID_GLOBAL, SUBNET_ID_MAX, SUBNET_ID_UNUSED - Modified code throughout to use these constants, rather than hard-coded values. Note, MySQL and PostgreSQL host backends convert from NULL to UNUSED and back. - kea-dhcp4/6 servers will now parse a "reservations" element at the global level. src/lib/dhcpsrv/subnet_id.h Added constants SubnetID SUBNET_ID_GLOBAL, SUBNET_ID_MAX, SUBNET_ID_UNUSED src/bin/dhcp4/dhcp4_lexer.ll src/bin/dhcp4/dhcp4_parser.yy src/bin/dhcp4/json_config_parser.cc kea-dhcp4 parsing now handles reservations as a global element src/bin/dhcp4/tests/config_parser_unittest.cc TEST_F(Dhcp4ParserTest, globalReservations) - new test to verify global HR parsing src/bin/dhcp4/tests/dora_unittest.cc src/lib/dhcpsrv/cfg_hosts.cc src/lib/dhcpsrv/host.cc src/lib/dhcpsrv/host_mgr.cc src/lib/dhcpsrv/mysql_host_data_source.cc src/lib/dhcpsrv/parsers/host_reservation_parser.cc src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc src/lib/dhcpsrv/tests/alloc_engine_utils.cc src/lib/dhcpsrv/tests/host_mgr_unittest.cc src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc src/lib/dhcpsrv/tests/host_reservations_list_parser_unittest.cc src/lib/dhcpsrv/tests/host_unittest.cc Replaced SubnetID 0 with SUBNET_ID_UNUSED src/lib/dhcpsrv/srv_config.cc SrvConfig::toElement() - added global reservations output src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc TEST_F(CfgHostsTest, globalSubnetIDs) TEST_F(CfgHostsTest, unusedSubnetIDs) - new tests src/lib/dhcpsrv/tests/host_unittest.cc Replaced SubnetID 0 with SUBNET_ID_UNUSED TEST_F(HostTest, toText) - updated to verify global ID output src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc TEST_F(MySqlHostDataSourceTest, globalSubnetId4) TEST_F(MySqlHostDataSourceTest, globalSubnetId6) - new tests src/lib/dhcpsrv/tests/srv_config_unittest.cc TEST_F(SrvConfigTest, unparseHR) - added global HRs src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.* GenericHostDataSourceTest::testGlobalSubnetId4() GenericHostDataSourceTest::testGlobalSubnetId6() src/bin/dhcp6/dhcp6_lexer.ll src/bin/dhcp6/dhcp6_parser.yy src/bin/dhcp6/json_config_parser.cc kea-dhcp6 now parses reservations as a global element src/bin/dhcp6/tests/config_parser_unittest.cc TEST_F(Dhcp6ParserTest, globalReservations) - new test
2018-08-07 06:46:30 -04:00
ConstElementPtr compatibility = mutable_cfg->get("compatibility");
if (compatibility) {
for (auto const& kv : compatibility->mapValue()) {
2023-03-11 00:32:09 +01:00
if (!kv.second || (kv.second->getType() != Element::boolean)) {
isc_throw(DhcpConfigError,
"compatibility parameter values must be "
<< "boolean (" << kv.first << " at "
<< kv.second->getPosition() << ")");
}
if (kv.first == "lenient-option-parsing") {
CfgMgr::instance().getStagingCfg()->setLenientOptionParsing(
kv.second->boolValue());
2023-03-21 12:06:34 +01:00
} else if (kv.first == "ignore-dhcp-server-identifier") {
CfgMgr::instance().getStagingCfg()->setIgnoreServerIdentifier(
kv.second->boolValue());
2023-03-09 18:29:10 +01:00
} else if (kv.first == "ignore-rai-link-selection") {
CfgMgr::instance().getStagingCfg()->setIgnoreRAILinkSelection(
kv.second->boolValue());
2023-03-09 18:29:10 +01:00
} else if (kv.first == "exclude-first-last-24") {
CfgMgr::instance().getStagingCfg()->setExcludeFirstLast24(
kv.second->boolValue());
} else {
isc_throw(DhcpConfigError,
"unsupported compatibility parameter: "
<< kv.first << " (" << kv.second->getPosition()
<< ")");
}
}
}
2020-05-22 10:34:36 +02:00
// Make parsers grouping.
ConfigPair config_pair;
const std::map<std::string, ConstElementPtr>& values_map =
mutable_cfg->mapValue();
2020-05-22 10:34:36 +02:00
BOOST_FOREACH(config_pair, values_map) {
[5704] host backends and kea-dhcp4/6 support global HR storage - Added constants for special SubnetIDs: SUBNET_ID_GLOBAL, SUBNET_ID_MAX, SUBNET_ID_UNUSED - Modified code throughout to use these constants, rather than hard-coded values. Note, MySQL and PostgreSQL host backends convert from NULL to UNUSED and back. - kea-dhcp4/6 servers will now parse a "reservations" element at the global level. src/lib/dhcpsrv/subnet_id.h Added constants SubnetID SUBNET_ID_GLOBAL, SUBNET_ID_MAX, SUBNET_ID_UNUSED src/bin/dhcp4/dhcp4_lexer.ll src/bin/dhcp4/dhcp4_parser.yy src/bin/dhcp4/json_config_parser.cc kea-dhcp4 parsing now handles reservations as a global element src/bin/dhcp4/tests/config_parser_unittest.cc TEST_F(Dhcp4ParserTest, globalReservations) - new test to verify global HR parsing src/bin/dhcp4/tests/dora_unittest.cc src/lib/dhcpsrv/cfg_hosts.cc src/lib/dhcpsrv/host.cc src/lib/dhcpsrv/host_mgr.cc src/lib/dhcpsrv/mysql_host_data_source.cc src/lib/dhcpsrv/parsers/host_reservation_parser.cc src/lib/dhcpsrv/tests/alloc_engine4_unittest.cc src/lib/dhcpsrv/tests/alloc_engine_utils.cc src/lib/dhcpsrv/tests/host_mgr_unittest.cc src/lib/dhcpsrv/tests/host_reservation_parser_unittest.cc src/lib/dhcpsrv/tests/host_reservations_list_parser_unittest.cc src/lib/dhcpsrv/tests/host_unittest.cc Replaced SubnetID 0 with SUBNET_ID_UNUSED src/lib/dhcpsrv/srv_config.cc SrvConfig::toElement() - added global reservations output src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc TEST_F(CfgHostsTest, globalSubnetIDs) TEST_F(CfgHostsTest, unusedSubnetIDs) - new tests src/lib/dhcpsrv/tests/host_unittest.cc Replaced SubnetID 0 with SUBNET_ID_UNUSED TEST_F(HostTest, toText) - updated to verify global ID output src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc TEST_F(MySqlHostDataSourceTest, globalSubnetId4) TEST_F(MySqlHostDataSourceTest, globalSubnetId6) - new tests src/lib/dhcpsrv/tests/srv_config_unittest.cc TEST_F(SrvConfigTest, unparseHR) - added global HRs src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.* GenericHostDataSourceTest::testGlobalSubnetId4() GenericHostDataSourceTest::testGlobalSubnetId6() src/bin/dhcp6/dhcp6_lexer.ll src/bin/dhcp6/dhcp6_parser.yy src/bin/dhcp6/json_config_parser.cc kea-dhcp6 now parses reservations as a global element src/bin/dhcp6/tests/config_parser_unittest.cc TEST_F(Dhcp6ParserTest, globalReservations) - new test
2018-08-07 06:46:30 -04:00
2020-05-22 10:34:36 +02:00
parameter_name = config_pair.first;
// These are converted to SimpleParser and are handled already above.
if ((config_pair.first == "option-def") ||
(config_pair.first == "option-data") ||
(config_pair.first == "control-socket") ||
(config_pair.first == "multi-threading") ||
(config_pair.first == "dhcp-queue-control") ||
(config_pair.first == "host-reservation-identifiers") ||
(config_pair.first == "interfaces-config") ||
(config_pair.first == "sanity-checks") ||
(config_pair.first == "expired-leases-processing") ||
(config_pair.first == "hooks-libraries") ||
(config_pair.first == "dhcp-ddns") ||
(config_pair.first == "client-classes") ||
(config_pair.first == "lease-database") ||
(config_pair.first == "hosts-database") ||
(config_pair.first == "hosts-databases") ||
(config_pair.first == "subnet4") ||
(config_pair.first == "shared-networks") ||
(config_pair.first == "reservations") ||
(config_pair.first == "config-control") ||
2022-01-21 19:44:16 +02:00
(config_pair.first == "loggers") ||
(config_pair.first == "compatibility")) {
continue;
}
// As of Kea 1.6.0 we have two ways of inheriting the global parameters.
// The old method is used in JSON configuration parsers when the global
// parameters are derived into the subnets and shared networks and are
// being treated as explicitly specified. The new way used by the config
// backend is the dynamic inheritance whereby each subnet and shared
// network uses a callback function to return global parameter if it
// is not specified at lower level. This callback uses configured globals.
// We deliberately include both default and explicitly specified globals
// so as the callback can access the appropriate global values regardless
// whether they are set to a default or other value.
if ( (config_pair.first == "renew-timer") ||
(config_pair.first == "rebind-timer") ||
(config_pair.first == "valid-lifetime") ||
(config_pair.first == "min-valid-lifetime") ||
(config_pair.first == "max-valid-lifetime") ||
(config_pair.first == "decline-probation-period") ||
(config_pair.first == "dhcp4o6-port") ||
(config_pair.first == "echo-client-id") ||
(config_pair.first == "match-client-id") ||
2018-10-19 16:34:29 +02:00
(config_pair.first == "authoritative") ||
(config_pair.first == "next-server") ||
(config_pair.first == "server-hostname") ||
(config_pair.first == "boot-file-name") ||
(config_pair.first == "server-tag") ||
(config_pair.first == "reservation-mode") ||
(config_pair.first == "reservations-global") ||
2020-11-11 19:06:06 +02:00
(config_pair.first == "reservations-in-subnet") ||
(config_pair.first == "reservations-out-of-pool") ||
(config_pair.first == "calculate-tee-times") ||
(config_pair.first == "t1-percent") ||
(config_pair.first == "t2-percent") ||
(config_pair.first == "cache-threshold") ||
2020-10-02 17:10:31 +02:00
(config_pair.first == "cache-max-age") ||
(config_pair.first == "hostname-char-set") ||
(config_pair.first == "hostname-char-replacement") ||
(config_pair.first == "ddns-send-updates") ||
(config_pair.first == "ddns-override-no-update") ||
(config_pair.first == "ddns-override-client-update") ||
(config_pair.first == "ddns-replace-client-name") ||
(config_pair.first == "ddns-generated-prefix") ||
(config_pair.first == "ddns-qualifying-suffix") ||
(config_pair.first == "ddns-update-on-renew") ||
(config_pair.first == "ddns-use-conflict-resolution") ||
[#2276] Initial impl ddns-resolution-conflict-mode New Files: src/bin/d2/check_exists_add.cc src/bin/d2/check_exists_add.h src/bin/d2/check_exists_remove.cc src/bin/d2/check_exists_remove.h src/bin/d2/simple_add_without_dhcid.cc src/bin/d2/simple_add_without_dhcid.h src/bin/d2/simple_remove_without_dhcid.cc src/bin/d2/simple_remove_without_dhcid.h src/bin/d2/tests/check_exists_add_unittests.cc src/bin/d2/tests/check_exists_remove_unittests.cc src/bin/d2/tests/simple_add_without_dhcid_unittests.cc src/bin/d2/tests/simple_remove_without_dhcid_unittests.cc Modified: doc/examples/kea4/all-keys.json doc/examples/kea4/with-ddns.json doc/examples/kea6/all-keys.json doc/examples/kea6/with-ddns.json src/bin/d2/Makefile.am src/bin/d2/check_exists_add.cc src/bin/d2/check_exists_add.h src/bin/d2/check_exists_remove.cc src/bin/d2/check_exists_remove.h src/bin/d2/d2_update_mgr.cc src/bin/d2/simple_add_without_dhcid.cc src/bin/d2/simple_add_without_dhcid.h src/bin/d2/simple_remove_without_dhcid.cc src/bin/d2/simple_remove_without_dhcid.h src/bin/d2/tests/Makefile.am src/bin/d2/tests/check_exists_add_unittests.cc src/bin/d2/tests/check_exists_remove_unittests.cc src/bin/d2/tests/d2_process_unittests.cc src/bin/d2/tests/d2_queue_mgr_unittests.cc src/bin/d2/tests/d2_update_mgr_unittests.cc src/bin/d2/tests/nc_add_unittests.cc src/bin/d2/tests/nc_remove_unittests.cc src/bin/d2/tests/simple_add_unittests.cc src/bin/d2/tests/simple_add_without_dhcid_unittests.cc src/bin/d2/tests/simple_remove_unittests.cc src/bin/d2/tests/simple_remove_without_dhcid_unittests.cc src/bin/dhcp4/dhcp4_lexer.cc src/bin/dhcp4/dhcp4_lexer.ll src/bin/dhcp4/dhcp4_parser.cc src/bin/dhcp4/dhcp4_parser.h src/bin/dhcp4/dhcp4_parser.yy src/bin/dhcp4/json_config_parser.cc src/bin/dhcp4/location.hh src/bin/dhcp4/parser_context.h src/bin/dhcp4/tests/config_parser_unittest.cc src/bin/dhcp4/tests/d2_unittest.cc src/bin/dhcp4/tests/fqdn_unittest.cc src/bin/dhcp4/tests/get_config_unittest.cc src/bin/dhcp6/dhcp6_lexer.cc src/bin/dhcp6/dhcp6_lexer.ll src/bin/dhcp6/dhcp6_parser.cc src/bin/dhcp6/dhcp6_parser.h src/bin/dhcp6/dhcp6_parser.yy src/bin/dhcp6/dhcp6_srv.cc src/bin/dhcp6/json_config_parser.cc src/bin/dhcp6/location.hh src/bin/dhcp6/parser_context.h src/bin/dhcp6/tests/config_parser_unittest.cc src/bin/dhcp6/tests/d2_unittest.cc src/bin/dhcp6/tests/fqdn_unittest.cc src/bin/dhcp6/tests/get_config_unittest.cc src/lib/d2srv/testutils/nc_test_utils.cc src/lib/d2srv/testutils/nc_test_utils.h src/lib/dhcp_ddns/ncr_msg.cc src/lib/dhcp_ddns/ncr_msg.h src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc src/lib/dhcp_ddns/tests/ncr_unittests.cc src/lib/dhcpsrv/cfg_globals.cc src/lib/dhcpsrv/cfg_globals.h src/lib/dhcpsrv/ncr_generator.cc src/lib/dhcpsrv/network.cc src/lib/dhcpsrv/network.h src/lib/dhcpsrv/parsers/base_network_parser.cc src/lib/dhcpsrv/parsers/base_network_parser.h src/lib/dhcpsrv/parsers/simple_parser4.cc src/lib/dhcpsrv/parsers/simple_parser6.cc src/lib/dhcpsrv/srv_config.cc src/lib/dhcpsrv/srv_config.h src/lib/dhcpsrv/tests/d2_udp_unittest.cc src/lib/dhcpsrv/tests/dhcp_parsers_unittest.cc src/lib/dhcpsrv/tests/ncr_generator_unittest.cc src/lib/dhcpsrv/tests/network_unittest.cc src/lib/dhcpsrv/tests/srv_config_unittest.cc
2023-05-31 13:51:19 -04:00
(config_pair.first == "ddns-conflict-resolution-mode") ||
(config_pair.first == "ddns-ttl-percent") ||
2020-04-05 13:22:19 +02:00
(config_pair.first == "store-extended-info") ||
(config_pair.first == "statistic-default-sample-count") ||
(config_pair.first == "statistic-default-sample-age") ||
2022-02-04 21:34:10 +01:00
(config_pair.first == "early-global-reservations-lookup") ||
(config_pair.first == "ip-reservations-unique") ||
(config_pair.first == "reservations-lookup-first") ||
2022-11-18 10:21:20 +01:00
(config_pair.first == "parked-packet-limit") ||
(config_pair.first == "allocator") ||
(config_pair.first == "offer-lifetime") ) {
CfgMgr::instance().getStagingCfg()->addConfiguredGlobal(config_pair.first,
config_pair.second);
continue;
2019-03-27 13:19:05 +01:00
}
// Nothing to configure for the user-context.
if (config_pair.first == "user-context") {
continue;
}
// If we got here, no code handled this parameter, so we bail out.
isc_throw(DhcpConfigError,
"unsupported global configuration parameter: " << config_pair.first
<< " (" << config_pair.second->getPosition() << ")");
2012-10-11 19:08:23 +02:00
}
2020-05-22 10:34:36 +02:00
// Reset parameter name.
parameter_name = "<post parsing>";
// Apply global options in the staging config.
global_parser.parse(srv_config, mutable_cfg);
// This method conducts final sanity checks and tweaks. In particular,
// it checks that there is no conflict between plain subnets and those
// defined as part of shared networks.
global_parser.sanityChecks(srv_config, mutable_cfg);
// Validate D2 client configuration.
if (!d2_client_cfg) {
d2_client_cfg.reset(new D2ClientConfig());
}
d2_client_cfg->validateContents();
srv_config->setD2ClientConfig(d2_client_cfg);
2012-10-11 19:08:23 +02:00
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
2020-05-22 10:34:36 +02:00
.arg(parameter_name).arg(ex.what());
2018-10-17 15:05:27 -04:00
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
2012-10-11 19:08:23 +02:00
} catch (...) {
// For things like bad_cast in boost::lexical_cast
2020-05-22 10:34:36 +02:00
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration "
"processing error");
}
if (!answer) {
answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration seems sane. "
"Control-socket, hook-libraries, and D2 configuration "
"were sanity checked, but not applied.");
2012-10-11 19:08:23 +02:00
}
return (answer);
}
isc::data::ConstElementPtr
configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set,
bool check_only, bool extra_checks) {
if (!config_set) {
ConstElementPtr answer = isc::config::createAnswer(CONTROL_RESULT_ERROR,
"Can't parse NULL config");
return (answer);
}
LOG_DEBUG(dhcp4_logger, DBG_DHCP4_COMMAND, DHCP4_CONFIG_START)
.arg(server.redactConfig(config_set)->str());
auto answer = processDhcp4Config(config_set);
int status_code = CONTROL_RESULT_SUCCESS;
isc::config::parseAnswer(status_code, answer);
SrvConfigPtr srv_config;
if (status_code == CONTROL_RESULT_SUCCESS) {
2023-03-20 19:00:21 +02:00
if (check_only) {
if (extra_checks) {
// Re-open lease and host database with new parameters.
try {
// Get the staging configuration.
srv_config = CfgMgr::instance().getStagingCfg();
CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
string params = "universe=4 persist=false";
if (cfg_db->getExtendedInfoTablesEnabled()) {
params += " extended-info-tables=true";
}
cfg_db->setAppendedParameters(params);
cfg_db->createManagers();
} catch (const std::exception& ex) {
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
}
if (status_code == CONTROL_RESULT_SUCCESS) {
std::ostringstream err;
// Configure DHCP packet queueing
try {
data::ConstElementPtr qc;
qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_PACKET_QUEUE)
.arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
}
} catch (const std::exception& ex) {
err << "Error setting packet queue controls after server reconfiguration: "
<< ex.what();
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
status_code = CONTROL_RESULT_ERROR;
}
}
}
} else {
2023-03-17 13:02:23 +02:00
// disable multi-threading (it will be applied by new configuration)
// this must be done in order to properly handle MT to ST transition
// when 'multi-threading' structure is missing from new config and
// to properly drop any task items stored in the thread pool which
// might reference some handles to loaded hooks, preventing them
// from being unloaded.
MultiThreadingMgr::instance().apply(false, 0, 0);
// Close DHCP sockets and remove any existing timers.
IfaceMgr::instance().closeSockets();
TimerMgr::instance()->unregisterTimers();
server.discardPackets();
server.getCBControl()->reset();
}
if (status_code == CONTROL_RESULT_SUCCESS) {
string parameter_name;
ElementPtr mutable_cfg;
try {
// Get the staging configuration.
srv_config = CfgMgr::instance().getStagingCfg();
// 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.
mutable_cfg = boost::const_pointer_cast<Element>(config_set);
ConstElementPtr ifaces_config = mutable_cfg->get("interfaces-config");
if (ifaces_config) {
parameter_name = "interfaces-config";
IfacesConfigParser parser(AF_INET, check_only);
CfgIfacePtr cfg_iface = srv_config->getCfgIface();
cfg_iface->reset();
parser.parse(cfg_iface, ifaces_config);
}
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_FAIL)
.arg(parameter_name).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_EXCEPTION).arg(parameter_name);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" processing error");
status_code = CONTROL_RESULT_ERROR;
}
}
}
// So far so good, there was no parsing error so let's commit the
// configuration. This will add created subnets and option values into
// the server's configuration.
// This operation should be exception safe but let's make sure.
if (status_code == CONTROL_RESULT_SUCCESS && !check_only) {
try {
// Setup the command channel.
configureCommandChannel();
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
status_code = CONTROL_RESULT_ERROR;
}
}
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// No need to commit interface names as this is handled by the
// CfgMgr::commit() function.
2013-08-13 13:14:38 +01:00
// Apply the staged D2ClientConfig, used to be done by parser commit
D2ClientConfigPtr cfg;
cfg = CfgMgr::instance().getStagingCfg()->getD2ClientConfig();
CfgMgr::instance().setD2ClientConfig(cfg);
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
status_code = CONTROL_RESULT_ERROR;
}
}
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
// This occurs last as if it succeeds, there is no easy way to
// revert it. As a result, the failure to commit a subsequent
// change causes problems when trying to roll back.
2020-06-25 15:29:33 +02:00
HooksManager::prepareUnloadLibraries();
static_cast<void>(HooksManager::unloadLibraries());
const HooksConfig& libraries =
CfgMgr::instance().getStagingCfg()->getHooksConfig();
bool multi_threading_enabled = true;
uint32_t thread_count = 0;
uint32_t queue_size = 0;
CfgMultiThreading::extract(CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading(),
multi_threading_enabled, thread_count, queue_size);
libraries.loadLibraries(multi_threading_enabled);
2022-01-21 19:44:16 +02:00
} catch (const isc::Exception& ex) {
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(ex.what());
2018-10-17 15:05:27 -04:00
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, ex.what());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_EXCEPTION);
2018-10-17 15:05:27 -04:00
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, "undefined configuration"
" parsing error");
status_code = CONTROL_RESULT_ERROR;
2012-10-11 19:08:23 +02:00
}
}
// Moved from the commit block to add the config backend indication.
if (status_code == CONTROL_RESULT_SUCCESS && (!check_only || extra_checks)) {
try {
2023-03-20 19:00:21 +02:00
// If there are config backends, fetch and merge into staging config
server.getCBControl()->databaseConfigFetch(srv_config,
CBControlDHCPv4::FetchMode::FETCH_ALL);
2022-01-21 19:44:16 +02:00
} catch (const isc::Exception& ex) {
std::ostringstream err;
err << "during update from config backend database: " << ex.what();
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(err.str());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
status_code = CONTROL_RESULT_ERROR;
} catch (...) {
// For things like bad_cast in boost::lexical_cast
std::ostringstream err;
err << "during update from config backend database: "
<< "undefined configuration parsing error";
LOG_ERROR(dhcp4_logger, DHCP4_PARSER_COMMIT_FAIL).arg(err.str());
answer = isc::config::createAnswer(CONTROL_RESULT_ERROR, err.str());
status_code = CONTROL_RESULT_ERROR;
}
}
// Rollback changes as the configuration parsing failed.
if (check_only || status_code != CONTROL_RESULT_SUCCESS) {
// Revert to original configuration of runtime option definitions
// in the libdhcp++.
LibDHCP::revertRuntimeOptionDefs();
if (status_code == CONTROL_RESULT_SUCCESS && extra_checks) {
auto notify_libraries = ControlledDhcpv4Srv::finishConfigHookLibraries(config_set);
if (notify_libraries) {
return (notify_libraries);
}
}
2012-10-11 19:08:23 +02:00
return (answer);
}
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_COMPLETE)
.arg(CfgMgr::instance().getStagingCfg()->
getConfigSummary(SrvConfig::CFGSEL_ALL4));
2012-10-11 19:08:23 +02:00
2023-06-28 16:51:57 +02:00
// Also calculate SHA256 hash of the config that was just set and
// append it to the response.
ConstElementPtr config = CfgMgr::instance().getStagingCfg()->toElement();
string hash = BaseCommandMgr::getHash(config);
ElementPtr hash_map = Element::createMap();
hash_map->set("hash", Element::create(hash));
// Everything was fine. Configuration is successful.
answer = isc::config::createAnswer(CONTROL_RESULT_SUCCESS, "Configuration successful.", hash_map);
2012-10-11 19:08:23 +02:00
return (answer);
}
2020-03-16 17:42:45 +02:00
} // namespace dhcp
} // namespace isc