#include <config.h>
#include <dhcpsrv/allocator.h>
#include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/dhcpsrv_log.h>
using namespace isc::util;
namespace dhcp {
Allocator::Allocator(Lease::Type type, const WeakSubnetPtr& subnet)
- : pool_type_(type),
+ : inited_(false),
+ pool_type_(type),
subnet_id_(0),
subnet_(subnet) {
// Remember subnet ID in a separate variable. It may be needed in
return (true);
}
+void
+Allocator::initAfterConfigure() {
+ if (inited_) {
+ return;
+ }
+ auto subnet = subnet_.lock();
+ LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_USE_ALLOCATOR)
+ .arg(getType())
+ .arg(Lease::typeToText(pool_type_))
+ .arg(subnet->toText());
+ initAfterConfigureInternal();
+ inited_ = true;
+}
+
}
}
/// Removes all LeaseMgr callbacks it installed.
virtual ~Allocator();
+ /// @brief Returns allocator type string.
+ ///
+ /// @return allocator-specific type string.
+ virtual std::string getType() const = 0;
+
/// @brief Picks an address.
///
/// This method returns one address from the available pools in the
/// reconfiguration). Such callbacks can be installed in this function.
///
/// In this function, the allocators can also re-build their allocation states.
- virtual void initAfterConfigure() {};
+ void initAfterConfigure();
+
+protected:
+
+ /// @brief Allocator-specific initialization function.
+ ///
+ /// It is called by the @c initAfterConfigure and can be overridden in the
+ /// derived allocators.
+ virtual void initAfterConfigureInternal() {};
private:
protected:
+ /// @brief Indicates if the allocator has been initialized.
+ ///
+ /// It is set to true when @c initAfterConfigure has been called.
+ /// It prevents initializing the allocator several times.
+ bool inited_;
+
/// @brief Defines pool type allocation
Lease::Type pool_type_;
-// Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2019-2023 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
const boost::posix_time::ptime& lb_modification_time,
const AuditEntryCollection& audit_entries) {
- bool globals_fetched = false;
+ auto globals_fetched = false;
+ auto reconfig = audit_entries.empty();
+ auto cb_update = !reconfig;
+ auto current_cfg = CfgMgr::instance().getCurrentCfg();
+ auto staging_cfg = CfgMgr::instance().getStagingCfg();
// Let's first delete all the configuration elements for which DELETE audit
// entries are found. Although, this may break chronology of the audit in
// delete this object from the local configuration (which will fail because
// the object does not exist) and then we will try to fetch it from the
// database which will return no result.
- if (!audit_entries.empty()) {
+ if (cb_update) {
- auto cfg = CfgMgr::instance().getCurrentCfg();
auto external_cfg = CfgMgr::instance().createExternalCfg();
// Get audit entries for deleted global parameters.
// Now that we successfully fetched the new global parameters, let's
// remove existing ones and merge them into the current configuration.
- cfg->clearConfiguredGlobals();
+ current_cfg->clearConfiguredGlobals();
CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
globals_fetched = true;
}
range = index.equal_range(boost::make_tuple("dhcp4_option_def",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getCfgOptionDef()->del((*entry)->getObjectId());
+ current_cfg->getCfgOptionDef()->del((*entry)->getObjectId());
}
// Repeat the same for other configuration elements.
range = index.equal_range(boost::make_tuple("dhcp4_options",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getCfgOption()->del((*entry)->getObjectId());
+ current_cfg->getCfgOption()->del((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp4_client_class",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getClientClassDictionary()->removeClass((*entry)->getObjectId());
+ current_cfg->getClientClassDictionary()->removeClass((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp4_shared_network",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getCfgSharedNetworks4()->del((*entry)->getObjectId());
+ current_cfg->getCfgSharedNetworks4()->del((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp4_subnet",
// If the deleted subnet belongs to a shared network and the
// shared network is not being removed, we need to detach the
// subnet from the shared network.
- auto subnet = cfg->getCfgSubnets4()->getBySubnetId((*entry)->getObjectId());
+ auto subnet = current_cfg->getCfgSubnets4()->getBySubnetId((*entry)->getObjectId());
if (subnet) {
// Check if the subnet belongs to a shared network.
SharedNetwork4Ptr network;
network->del(subnet->getID());
}
// Actually delete the subnet from the configuration.
- cfg->getCfgSubnets4()->del((*entry)->getObjectId());
+ current_cfg->getCfgSubnets4()->del((*entry)->getObjectId());
}
}
}
// Create the external config into which we'll fetch backend config data.
- SrvConfigPtr external_cfg = CfgMgr::instance().createExternalCfg();
+ auto external_cfg = CfgMgr::instance().createExternalCfg();
// First let's fetch the globals and add them to external config.
AuditEntryCollection updated_entries;
- if (!globals_fetched && !audit_entries.empty()) {
- updated_entries = fetchConfigElement(audit_entries, "dhcp4_global_parameter");
- }
- if (!globals_fetched && (audit_entries.empty() || !updated_entries.empty())) {
- data::StampedValueCollection globals;
- globals = getMgr().getPool()->getModifiedGlobalParameters4(backend_selector, server_selector,
- lb_modification_time);
- addGlobalsToConfig(external_cfg, globals);
- globals_fetched = true;
+ if (!globals_fetched) {
+ if (cb_update) {
+ updated_entries = fetchConfigElement(audit_entries, "dhcp4_global_parameter");
+ }
+ if (reconfig || !updated_entries.empty()) {
+ data::StampedValueCollection globals;
+ globals = getMgr().getPool()->getModifiedGlobalParameters4(backend_selector, server_selector,
+ lb_modification_time);
+ addGlobalsToConfig(external_cfg, globals);
+ globals_fetched = true;
+ }
}
// Now we fetch the option definitions and add them.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp4_option_def");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
+ if (reconfig || !updated_entries.empty()) {
OptionDefContainer option_defs =
getMgr().getPool()->getModifiedOptionDefs4(backend_selector, server_selector,
lb_modification_time);
}
// Next fetch the options. They are returned as a container of OptionDescriptors.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp4_options");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
+ if (reconfig || !updated_entries.empty()) {
OptionContainer options = getMgr().getPool()->getModifiedOptions4(backend_selector,
server_selector,
lb_modification_time);
}
// Fetch client classes. They are returned in a ClientClassDictionary.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp4_client_class");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
+ if (reconfig || !updated_entries.empty()) {
ClientClassDictionary client_classes = getMgr().getPool()->getAllClientClasses4(backend_selector,
server_selector);
// Match expressions are not initialized for classes returned from the config backend.
external_cfg->setClientClassDictionary(boost::make_shared<ClientClassDictionary>(client_classes));
}
+ // Allocator selection at the global level can affect subnets and shared networks
+ // for which the allocator hasn't been specified explicitly. Let's see if the
+ // allocator has been specified at the global level.
+ std::string default_allocator;
+ auto allocator = external_cfg->getConfiguredGlobal(CfgGlobals::ALLOCATOR);
+ if (allocator && (allocator->getType() == Element::string)) {
+ default_allocator = allocator->stringValue();
+ }
+
+ // If we're fetching the changes from the config backend we also want
+ // to see if the global allocator has changed. Let's get the currently
+ // used allocator too.
+ auto allocator_changed = false;
+ // We're only affected by the allocator change if this is the update from
+ // the configuration backend.
+ if (cb_update) {
+ std::string current_default_allocator;
+ auto allocator = CfgMgr::instance().getCurrentCfg()->getConfiguredGlobal(CfgGlobals::ALLOCATOR);
+ if (allocator && (allocator->getType() == Element::string)) {
+ allocator_changed = (default_allocator != allocator->stringValue());
+ }
+ }
+
// Now fetch the shared networks.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp4_shared_network");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
- SharedNetwork4Collection networks =
- getMgr().getPool()->getModifiedSharedNetworks4(backend_selector, server_selector,
- lb_modification_time);
- for (auto network = networks.begin(); network != networks.end(); ++network) {
- if (!audit_entries.empty() && !hasObjectId(updated_entries, (*network)->getId())) {
- continue;
- }
- // In order to take advantage of the dynamic inheritance of global
- // parameters to a shared network we need to set a callback function
- // for each network to allow for fetching global parameters.
- (*network)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
- return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
- });
- external_cfg->getCfgSharedNetworks4()->add((*network));
+ SharedNetwork4Collection networks;
+ if (allocator_changed || reconfig) {
+ // A change of the allocator or the server reconfiguration can affect all
+ // shared networks. Get all shared networks.
+ networks = getMgr().getPool()->getAllSharedNetworks4(backend_selector, server_selector);
+
+ } else if (!updated_entries.empty()) {
+ // An update from the config backend when the global allocator hasn't changed
+ // means that we only need to handle the modified subnets.
+ networks = getMgr().getPool()->getModifiedSharedNetworks4(backend_selector, server_selector,
+ lb_modification_time);
+ }
+ // Iterate over all shared networks that may require reconfiguration.
+ for (auto network = networks.begin(); network != networks.end(); ++network) {
+ if (!allocator_changed && cb_update && !hasObjectId(updated_entries, (*network)->getId())) {
+ continue;
}
+ // In order to take advantage of the dynamic inheritance of global
+ // parameters to a shared network we need to set a callback function
+ // for each network to allow for fetching global parameters.
+ (*network)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
+ return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+ });
+ (*network)->setDefaultAllocatorType(default_allocator);
+ external_cfg->getCfgSharedNetworks4()->add((*network));
}
- // Next we fetch subnets.
- if (!audit_entries.empty()) {
+ // Next, fetch the subnets.
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp4_subnet");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
- Subnet4Collection subnets = getMgr().getPool()->getModifiedSubnets4(backend_selector,
- server_selector,
- lb_modification_time);
- for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
- if (!audit_entries.empty() && !hasObjectId(updated_entries, (*subnet)->getID())) {
- continue;
- }
- // In order to take advantage of the dynamic inheritance of global
- // parameters to a subnet we need to set a callback function for each
- // subnet to allow for fetching global parameters.
- (*subnet)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
- return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
- });
- external_cfg->getCfgSubnets4()->add((*subnet));
+ Subnet4Collection subnets;
+ if (allocator_changed || reconfig) {
+ // A change of the allocator or the server reconfiguration can affect all
+ // shared networks. Get all subnets.
+ subnets = getMgr().getPool()->getAllSubnets4(backend_selector, server_selector);
+
+ } else if (!updated_entries.empty()) {
+ // An update from the config backend when the global allocator hasn't changed
+ // means that we only need to handle the modified subnets.
+ subnets = getMgr().getPool()->getModifiedSubnets4(backend_selector,
+ server_selector,
+ lb_modification_time);
+ }
+ // Iterate over all subnets that may require reconfiguration.
+ for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
+ if (!allocator_changed && cb_update && !hasObjectId(updated_entries, (*subnet)->getID())) {
+ continue;
}
+ // In order to take advantage of the dynamic inheritance of global
+ // parameters to a subnet we need to set a callback function for each
+ // subnet to allow for fetching global parameters.
+ (*subnet)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
+ return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+ });
+ (*subnet)->setDefaultAllocatorType(default_allocator);
+ external_cfg->getCfgSubnets4()->add((*subnet));
}
- if (audit_entries.empty()) {
+ if (reconfig) {
// If we're configuring the server after startup, we do not apply the
// ip-reservations-unique setting here. It will be applied when the
// configuration is committed.
- auto const& cfg = CfgMgr::instance().getStagingCfg();
- external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
+ external_cfg->sanityChecksLifetime(*staging_cfg, "valid-lifetime");
CfgMgr::instance().mergeIntoStagingCfg(external_cfg->getSequence());
+
} else {
if (globals_fetched) {
// ip-reservations-unique parameter requires special handling because
external_cfg->addConfiguredGlobal("ip-reservations-unique", Element::create(true));
}
}
- auto const& cfg = CfgMgr::instance().getCurrentCfg();
- external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
+ external_cfg->sanityChecksLifetime(*current_cfg, "valid-lifetime");
CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets4()->initAllocatorsAfterConfigure();
}
+
LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_CONFIG4_MERGED);
- if (!audit_entries.empty() &&
+ if (cb_update &&
HooksManager::calloutsPresent(hooks_.hook_index_cb4_updated_)) {
CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
-// Copyright (C) 2019-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2019-2023 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
const boost::posix_time::ptime& lb_modification_time,
const db::AuditEntryCollection& audit_entries) {
bool globals_fetched = false;
+ auto reconfig = audit_entries.empty();
+ auto cb_update = !reconfig;
+ auto current_cfg = CfgMgr::instance().getCurrentCfg();
+ auto staging_cfg = CfgMgr::instance().getStagingCfg();
// Let's first delete all the configuration elements for which DELETE audit
// entries are found. Although, this may break chronology of the audit in
// delete this object from the local configuration (which will fail because
// the object does not exist) and then we will try to fetch it from the
// database which will return no result.
- if (!audit_entries.empty()) {
+ if (cb_update) {
- auto cfg = CfgMgr::instance().getCurrentCfg();
auto external_cfg = CfgMgr::instance().createExternalCfg();
// Get audit entries for deleted global parameters.
// Now that we successfully fetched the new global parameters, let's
// remove existing ones and merge them into the current configuration.
- cfg->clearConfiguredGlobals();
+ current_cfg->clearConfiguredGlobals();
CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
globals_fetched = true;
}
range = index.equal_range(boost::make_tuple("dhcp6_option_def",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getCfgOptionDef()->del((*entry)->getObjectId());
+ current_cfg->getCfgOptionDef()->del((*entry)->getObjectId());
}
// Repeat the same for other configuration elements.
range = index.equal_range(boost::make_tuple("dhcp6_options",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getCfgOption()->del((*entry)->getObjectId());
+ current_cfg->getCfgOption()->del((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp6_client_class",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getClientClassDictionary()->removeClass((*entry)->getObjectId());
+ current_cfg->getClientClassDictionary()->removeClass((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp6_shared_network",
AuditEntry::ModificationType::DELETE));
for (auto entry = range.first; entry != range.second; ++entry) {
- cfg->getCfgSharedNetworks6()->del((*entry)->getObjectId());
+ current_cfg->getCfgSharedNetworks6()->del((*entry)->getObjectId());
}
range = index.equal_range(boost::make_tuple("dhcp6_subnet",
// If the deleted subnet belongs to a shared network and the
// shared network is not being removed, we need to detach the
// subnet from the shared network.
- auto subnet = cfg->getCfgSubnets6()->getBySubnetId((*entry)->getObjectId());
+ auto subnet = current_cfg->getCfgSubnets6()->getBySubnetId((*entry)->getObjectId());
if (subnet) {
// Check if the subnet belongs to a shared network.
SharedNetwork6Ptr network;
network->del(subnet->getID());
}
// Actually delete the subnet from the configuration.
- cfg->getCfgSubnets6()->del((*entry)->getObjectId());
+ current_cfg->getCfgSubnets6()->del((*entry)->getObjectId());
}
}
// First let's fetch the globals and add them to external config.
AuditEntryCollection updated_entries;
- if (!globals_fetched && !audit_entries.empty()) {
- updated_entries = fetchConfigElement(audit_entries, "dhcp6_global_parameter");
- }
- if (!globals_fetched && (audit_entries.empty() || !updated_entries.empty())) {
- data::StampedValueCollection globals;
- globals = getMgr().getPool()->getModifiedGlobalParameters6(backend_selector, server_selector,
- lb_modification_time);
- addGlobalsToConfig(external_cfg, globals);
- globals_fetched = true;
+ if (!globals_fetched) {
+ if (cb_update) {
+ updated_entries = fetchConfigElement(audit_entries, "dhcp6_global_parameter");
+ }
+ if (reconfig || !updated_entries.empty()) {
+ data::StampedValueCollection globals;
+ globals = getMgr().getPool()->getModifiedGlobalParameters6(backend_selector, server_selector,
+ lb_modification_time);
+ addGlobalsToConfig(external_cfg, globals);
+ globals_fetched = true;
+ }
}
// Now we fetch the option definitions and add them.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp6_option_def");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
+ if (reconfig || !updated_entries.empty()) {
OptionDefContainer option_defs =
getMgr().getPool()->getModifiedOptionDefs6(backend_selector, server_selector,
lb_modification_time);
}
// Next fetch the options. They are returned as a container of OptionDescriptors.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp6_options");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
+ if (reconfig || !updated_entries.empty()) {
OptionContainer options = getMgr().getPool()->getModifiedOptions6(backend_selector,
server_selector,
lb_modification_time);
}
// Fetch client classes. They are returned in a ClientClassDictionary.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp6_client_class");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
+ if (reconfig || !updated_entries.empty()) {
ClientClassDictionary client_classes = getMgr().getPool()->getAllClientClasses6(backend_selector,
server_selector);
// Match expressions are not initialized for classes returned from the config backend.
external_cfg->setClientClassDictionary(boost::make_shared<ClientClassDictionary>(client_classes));
}
+ // Allocator selection at the global level can affect subnets and shared networks
+ // for which the allocator hasn't been specified explicitly. Let's see if the
+ // allocator has been specified at the global level.
+ std::string default_allocator;
+ auto allocator = external_cfg->getConfiguredGlobal(CfgGlobals::ALLOCATOR);
+ if (allocator && (allocator->getType() == Element::string)) {
+ default_allocator = allocator->stringValue();
+ }
+
+ // Also, get the PD allocator.
+ std::string default_pd_allocator;
+ allocator = external_cfg->getConfiguredGlobal(CfgGlobals::PD_ALLOCATOR);
+ if (allocator && (allocator->getType() == Element::string)) {
+ default_pd_allocator = allocator->stringValue();
+ }
+
+ // If we're fetching the changes from the config backend we also want
+ // to see if the global allocator has changed. Let's get the currently
+ // used allocator too.
+ auto allocator_changed = false;
+ // We're only affected by the allocator change if this is the update from
+ // the configuration backend.
+ if (cb_update) {
+ auto allocator = CfgMgr::instance().getCurrentCfg()->getConfiguredGlobal(CfgGlobals::ALLOCATOR);
+ if (allocator && (allocator->getType() == Element::string)) {
+ allocator_changed = (default_allocator != allocator->stringValue());
+ }
+
+ // The address allocator hasn't changed. So, let's check if the PD allocator
+ // has changed.
+ if (!allocator_changed) {
+ auto allocator = CfgMgr::instance().getCurrentCfg()->getConfiguredGlobal(CfgGlobals::PD_ALLOCATOR);
+ if (allocator && (allocator->getType() == Element::string)) {
+ allocator_changed = (default_pd_allocator != allocator->stringValue());
+ }
+ }
+ }
+
// Now fetch the shared networks.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp6_shared_network");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
- SharedNetwork6Collection networks =
- getMgr().getPool()->getModifiedSharedNetworks6(backend_selector, server_selector,
- lb_modification_time);
- for (auto network = networks.begin(); network != networks.end(); ++network) {
- if (!audit_entries.empty() && !hasObjectId(updated_entries, (*network)->getId())) {
- continue;
- }
- // In order to take advantage of the dynamic inheritance of global
- // parameters to a shared network we need to set a callback function
- // for each network to allow for fetching global parameters.
- (*network)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
- return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
- });
- external_cfg->getCfgSharedNetworks6()->add((*network));
+ SharedNetwork6Collection networks;
+ if (allocator_changed || reconfig) {
+ // A change of the allocator or the server reconfiguration can affect all
+ // shared networks. Get all shared networks.
+ networks = getMgr().getPool()->getAllSharedNetworks6(backend_selector, server_selector);
+ } else if (!updated_entries.empty()) {
+ networks = getMgr().getPool()->getModifiedSharedNetworks6(backend_selector, server_selector,
+ lb_modification_time);
+ }
+ for (auto network = networks.begin(); network != networks.end(); ++network) {
+ if (!allocator_changed && cb_update && !hasObjectId(updated_entries, (*network)->getId())) {
+ continue;
}
+ // In order to take advantage of the dynamic inheritance of global
+ // parameters to a shared network we need to set a callback function
+ // for each network to allow for fetching global parameters.
+ (*network)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
+ return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+ });
+ (*network)->setDefaultAllocatorType(default_allocator);
+ (*network)->setDefaultPdAllocatorType(default_pd_allocator);
+ external_cfg->getCfgSharedNetworks6()->add((*network));
}
// Next we fetch subnets.
- if (!audit_entries.empty()) {
+ if (cb_update) {
updated_entries = fetchConfigElement(audit_entries, "dhcp6_subnet");
}
- if (audit_entries.empty() || !updated_entries.empty()) {
- Subnet6Collection subnets = getMgr().getPool()->getModifiedSubnets6(backend_selector,
- server_selector,
- lb_modification_time);
- for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
- if (!audit_entries.empty() && !hasObjectId(updated_entries, (*subnet)->getID())) {
- continue;
- }
- // In order to take advantage of the dynamic inheritance of global
- // parameters to a subnet we need to set a callback function for each
- // subnet to allow for fetching global parameters.
- (*subnet)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
- return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
- });
- external_cfg->getCfgSubnets6()->add((*subnet));
+ Subnet6Collection subnets;
+ if (allocator_changed || reconfig) {
+ // A change of the allocator or the server reconfiguration can affect all
+ // shared networks. Get all subnets.
+ subnets = getMgr().getPool()->getAllSubnets6(backend_selector, server_selector);
+
+ } else if (!updated_entries.empty()) {
+ // An update from the config backend when the global allocator hasn't changed
+ // means that we only need to handle the modified subnets.
+ subnets = getMgr().getPool()->getModifiedSubnets6(backend_selector,
+ server_selector,
+ lb_modification_time);
+ }
+ // Iterate over all subnets that may require reconfiguration.
+ for (auto subnet = subnets.begin(); subnet != subnets.end(); ++subnet) {
+ if (!audit_entries.empty() && !hasObjectId(updated_entries, (*subnet)->getID())) {
+ continue;
}
+ // In order to take advantage of the dynamic inheritance of global
+ // parameters to a subnet we need to set a callback function for each
+ // subnet to allow for fetching global parameters.
+ (*subnet)->setFetchGlobalsFn([] () -> ConstCfgGlobalsPtr {
+ return (CfgMgr::instance().getCurrentCfg()->getConfiguredGlobals());
+ });
+ (*subnet)->setDefaultAllocatorType(default_allocator);
+ (*subnet)->setDefaultPdAllocatorType(default_pd_allocator);
+ external_cfg->getCfgSubnets6()->add((*subnet));
}
- if (audit_entries.empty()) {
+ if (reconfig) {
// If we're configuring the server after startup, we do not apply the
// ip-reservations-unique setting here. It will be applied when the
// configuration is committed.
external_cfg->sanityChecksLifetime(*cfg, "preferred-lifetime");
external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
CfgMgr::instance().mergeIntoStagingCfg(external_cfg->getSequence());
+
} else {
if (globals_fetched) {
// ip-reservations-unique parameter requires special handling because
external_cfg->sanityChecksLifetime(*cfg, "preferred-lifetime");
external_cfg->sanityChecksLifetime(*cfg, "valid-lifetime");
CfgMgr::instance().mergeIntoCurrentCfg(external_cfg->getSequence());
+ CfgMgr::instance().getCurrentCfg()->getCfgSubnets6()->initAllocatorsAfterConfigure();
}
LOG_INFO(dhcpsrv_logger, DHCPSRV_CFGMGR_CONFIG6_MERGED);
- if (!audit_entries.empty() &&
+ if (cb_update &&
HooksManager::calloutsPresent(hooks_.hook_index_cb6_updated_)) {
CalloutHandlePtr callout_handle = HooksManager::createCalloutHandle();
<< ", network does not exist");
}
}
+ // Instantiate the configured allocator and its state.
+ other_subnet->createAllocators();
}
}
<< ", network does not exist");
}
}
+ // Instantiate the configured allocators and their states.
+ other_subnet->createAllocators();
}
}
A message issued when the server is configured to listen on the explicitly specified
IP address on the given interface.
+% DHCPSRV_CFGMGR_USE_ALLOCATOR using the %1 allocator for %2 leases in subnet %3
+A message issued when the configuration manager starts using a given allocator
+for a subnet.
+
% DHCPSRV_CFGMGR_USE_UNICAST listening on unicast address %1, on interface %2
An info message issued when configuring the DHCP server to listen on the unicast
address on the specific interface.
}
void
-FreeLeaseQueueAllocator::initAfterConfigure() {
+FreeLeaseQueueAllocator::initAfterConfigureInternal() {
auto subnet = subnet_.lock();
auto pools = subnet->getPools(pool_type_);
if (pools.empty()) {
/// @param subnet weak pointer to the subnet owning the allocator.
FreeLeaseQueueAllocator(Lease::Type type, const WeakSubnetPtr& subnet);
+ /// @brief Returns the allocator type string.
+ ///
+ /// @return flq string.
+ virtual std::string getType() const {
+ return ("flq");
+ }
+
+private:
+
/// @brief Performs allocator initialization after server's reconfiguration.
///
/// The allocator installs the callbacks in the lease manager to keep track of
/// the lease allocations and maintain the free leases queue.
- virtual void initAfterConfigure();
-
-private:
+ virtual void initAfterConfigureInternal();
/// @brief Populates the queue of free addresses (IPv4 and IPv6).
///
/// @param subnet weak pointer to the subnet owning the allocator.
IterativeAllocator(Lease::Type type, const WeakSubnetPtr& subnet);
+ /// @brief Returns the allocator type string.
+ ///
+ /// @return iterative string.
+ virtual std::string getType() const {
+ return ("iterative");
+ }
+
private:
/// @brief Returns the next address from the pools in the subnet.
ddns_replace_client_name_mode_(), ddns_generated_prefix_(), ddns_qualifying_suffix_(),
hostname_char_set_(), hostname_char_replacement_(), store_extended_info_(),
cache_threshold_(), cache_max_age_(), ddns_update_on_renew_(),
- ddns_use_conflict_resolution_(), ddns_ttl_percent_() {
+ ddns_use_conflict_resolution_(), ddns_ttl_percent_(), allocator_type_(),
+ default_allocator_type_() {
}
/// @brief Virtual destructor.
allocator_type_ = allocator_type;
}
+ /// @brief Returns a default allocator type.
+ ///
+ /// This allocator type is used when the allocator type is neither specified
+ /// at the shared network nor subnet level.
+ ///
+ /// @return an allocator type as a string.
+ util::Optional<std::string>
+ getDefaultAllocatorType(const Inheritance& inheritance = Inheritance::ALL) const {
+ return (getProperty<Network>(&Network::getDefaultAllocatorType,
+ default_allocator_type_,
+ inheritance));
+ }
+
+ /// @brief Sets a defalt allocator type.
+ ///
+ /// @param allocator_type a new default allocator type.
+ void setDefaultAllocatorType(const std::string& allocator_type) {
+ default_allocator_type_ = allocator_type;
+ }
+
/// @brief Unparses network object.
///
/// @return A pointer to unparsed network configuration.
/// @brief Used to to tell kea-dhcp-ddns whether or not to use conflict resolution.
util::Optional<bool> ddns_use_conflict_resolution_;
+ /// @brief Percentage of the lease lifetime to use for DNS TTL.
+ util::Optional<double> ddns_ttl_percent_;
+
/// @brief Allocator used for IP address allocations.
util::Optional<std::string> allocator_type_;
- /// @brief Percentage of the lease lifetime to use for DNS TTL.
- util::Optional<double> ddns_ttl_percent_;
+ /// @brief Default allocator type.
+ util::Optional<std::string> default_allocator_type_;
/// @brief Pointer to another network that this network belongs to.
///
/// @brief Constructor.
Network6()
- : Network(), preferred_(), interface_id_(), rapid_commit_() {
+ : Network(), preferred_(), interface_id_(), rapid_commit_(),
+ default_pd_allocator_type_(){
}
/// @brief Returns preferred lifetime (in seconds)
pd_allocator_type_ = allocator_type;
}
+ /// @brief Returns a default allocator type for prefix delegation.
+ ///
+ /// This allocator type is used when the allocator type is neither specified
+ /// at the shared network nor subnet level.
+ ///
+ /// @return an allocator type as a string.
+ util::Optional<std::string>
+ getDefaultPdAllocatorType(const Inheritance& inheritance = Inheritance::ALL) const {
+ return (getProperty<Network6>(&Network6::getDefaultPdAllocatorType,
+ default_pd_allocator_type_,
+ inheritance));
+ }
+
+ /// @brief Sets a defalt allocator type for prefix delegation.
+ ///
+ /// @param allocator_type a new default allocator type.
+ void setDefaultPdAllocatorType(const std::string& allocator_type) {
+ default_pd_allocator_type_ = allocator_type;
+ }
+
/// @brief Unparses network object.
///
/// @return A pointer to unparsed network configuration.
/// @brief Allocator used for prefix delegation.
util::Optional<std::string> pd_allocator_type_;
+
+ // @brief Default allocator type for prefix delegation.
+ util::Optional<std::string> default_pd_allocator_type_;
};
} // end of namespace isc::dhcp
/// @param subnet weak pointer to the subnet owning the allocator.
RandomAllocator(Lease::Type type, const WeakSubnetPtr& subnet);
+ /// @brief Returns the allocator type string.
+ ///
+ /// @return random string.
+ virtual std::string getType() const {
+ return ("random");
+ }
+
private:
/// @brief Returns a random address from the pools in the subnet.
void
Subnet4::createAllocators() {
- if (getAllocatorType() == "random") {
+ auto allocator_type = getAllocatorType();
+ if (allocator_type.empty()) {
+ allocator_type = getDefaultAllocatorType();
+ }
+ if (allocator_type == "random") {
setAllocator(Lease::TYPE_V4,
boost::make_shared<RandomAllocator>
(Lease::TYPE_V4, shared_from_this()));
pool->setAllocationState(PoolRandomAllocationState::create(pool));
}
- } else if (getAllocatorType() == "flq") {
+ } else if (allocator_type == "flq") {
setAllocator(Lease::TYPE_V4,
boost::make_shared<FreeLeaseQueueAllocator>
(Lease::TYPE_V4, shared_from_this()));
}
} else {
+ setAllocator(Lease::TYPE_V4,
+ boost::make_shared<IterativeAllocator>
+ (Lease::TYPE_V4, shared_from_this()));
for (auto pool : pools_) {
pool->setAllocationState(PoolIterativeAllocationState::create(pool));
}
void
Subnet6::createAllocators() {
- // If we use the random allocator we need to create its instance and
- // the state instance for it. There is no need to do it for the iterative
- // allocator because it is configured by default.
- if (getAllocatorType() == "random") {
+ auto allocator_type = getAllocatorType();
+ if (allocator_type.empty()) {
+ allocator_type = getDefaultAllocatorType();
+ }
+ if (allocator_type == "random") {
setAllocator(Lease::TYPE_NA,
boost::make_shared<RandomAllocator>
(Lease::TYPE_NA, shared_from_this()));
setAllocationState(Lease::TYPE_NA, SubnetAllocationStatePtr());
setAllocationState(Lease::TYPE_TA, SubnetAllocationStatePtr());
- } else if (getAllocatorType() == "flq") {
+ } else if (allocator_type == "flq") {
isc_throw(BadValue, "Free Lease Queue allocator is not supported for IPv6 address pools");
+
+ } else {
+ setAllocator(Lease::TYPE_NA,
+ boost::make_shared<IterativeAllocator>
+ (Lease::TYPE_NA, shared_from_this()));
+ setAllocationState(Lease::TYPE_NA, SubnetIterativeAllocationState::create(shared_from_this()));
+ setAllocationState(Lease::TYPE_TA, SubnetIterativeAllocationState::create(shared_from_this()));
}
+ auto pd_allocator_type = getPdAllocatorType();
+ if (pd_allocator_type.empty()) {
+ pd_allocator_type = getDefaultPdAllocatorType();
+ }
// Repeat the same for the delegated prefix allocator.
- if (getPdAllocatorType() == "random") {
+ if (pd_allocator_type == "random") {
setAllocator(Lease::TYPE_PD,
boost::make_shared<RandomAllocator>
(Lease::TYPE_PD, shared_from_this()));
setAllocationState(Lease::TYPE_PD, SubnetAllocationStatePtr());
- } else if (getPdAllocatorType() == "flq") {
+ } else if (pd_allocator_type == "flq") {
setAllocator(Lease::TYPE_PD,
boost::make_shared<FreeLeaseQueueAllocator>
(Lease::TYPE_PD, shared_from_this()));
setAllocationState(Lease::TYPE_PD, SubnetAllocationStatePtr());
+
+ } else {
+ setAllocator(Lease::TYPE_PD,
+ boost::make_shared<IterativeAllocator>
+ (Lease::TYPE_PD, shared_from_this()));
+ setAllocationState(Lease::TYPE_PD, SubnetIterativeAllocationState::create(shared_from_this()));
}
// Create allocation states for NA pools.
for (auto pool : pools_) {
- if (getAllocatorType() == "random") {
+ if (allocator_type == "random") {
pool->setAllocationState(PoolRandomAllocationState::create(pool));
} else {
pool->setAllocationState(PoolIterativeAllocationState::create(pool));
}
// Create allocation states for TA pools.
for (auto pool : pools_ta_) {
- if (getAllocatorType() == "random") {
+ if (allocator_type == "random") {
pool->setAllocationState(PoolRandomAllocationState::create(pool));
} else {
pool->setAllocationState(PoolIterativeAllocationState::create(pool));
}
// Create allocation states for PD pools.
for (auto pool : pools_pd_) {
- if (getPdAllocatorType() == "random") {
+ if (pd_allocator_type == "random") {
pool->setAllocationState(PoolRandomAllocationState::create(pool));
- } else if (getPdAllocatorType() == "flq") {
+ } else if (pd_allocator_type == "flq") {
pool->setAllocationState(PoolFreeLeaseQueueAllocationState::create(pool));
} else {
pool->setAllocationState(PoolIterativeAllocationState::create(pool));
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/client_class_def.h>
#include <dhcpsrv/host_mgr.h>
+#include <dhcpsrv/random_allocator.h>
#include <dhcpsrv/testutils/memory_host_data_source.h>
#include <dhcpsrv/testutils/generic_backend_unittest.h>
#include <dhcpsrv/testutils/test_config_backend_dhcp4.h>
// Insert subnets into the database.
Subnet4Ptr subnet(new Subnet4(IOAddress("192.0.3.0"), 26, 1, 2, 3, SubnetID(1)));
subnet->setModificationTime(getTimestamp("dhcp4_subnet"));
+ subnet->setAllocatorType("random");
subnet->setSharedNetworkName("one");
mgr.getPool()->createUpdateSubnet4(BackendSelector::UNSPEC(), ServerSelector::ALL(),
subnet);
(getTimestamp("dhcp4_subnet") > lb_modification_time)) {
ASSERT_TRUE(found_subnet);
EXPECT_TRUE(found_subnet->hasFetchGlobalsFn());
+ EXPECT_TRUE(boost::dynamic_pointer_cast<RandomAllocator>
+ (found_subnet->getAllocator(Lease::TYPE_V4)));
} else {
EXPECT_FALSE(found_subnet);
EXPECT_TRUE(HostMgr::instance().getIPReservationsUnique());
}
+// This test verifies that the allocator type is inherited from the global to
+// shared network to subnet level.
+TEST_F(CBControlDHCPv4Test, allocatorInheritance) {
+ remoteStoreTestConfiguration();
+
+ // Set non-default global allocator.
+ StampedValuePtr global_parameter = StampedValue::create("allocator",
+ Element::create("flq"));
+ auto& mgr = ConfigBackendDHCPv4Mgr::instance();
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter4(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ global_parameter));
+ addCreateAuditEntry("dhcp4_global_parameter", 1);
+ addCreateAuditEntry("dhcp4_shared_network", 1);
+ addCreateAuditEntry("dhcp4_subnet", 2);
+
+ // Merge the configuration including the allocator setting into the current
+ // configuration.
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet that has no allocator specification. The global allocator should
+ // be used for this subnet.
+ auto srv_cfg = CfgMgr::instance().getCurrentCfg();
+ auto subnet = srv_cfg->getCfgSubnets4()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ auto allocator = subnet->getAllocator(Lease::TYPE_V4);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("flq", allocator->getType());
+
+ // Update the shared network but use a different allocator type for it.
+ auto updated_network = boost::make_shared<SharedNetwork4>("one");
+ updated_network->setId(1);
+ updated_network->setModificationTime(getTimestamp("dhcp4_shared_network"));
+ updated_network->setAllocatorType("random");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSharedNetwork4(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_network));
+
+
+ // Include our subnet in this shared network.
+ auto updated_subnet = boost::make_shared<Subnet4>(IOAddress("192.0.4.0"), 26, 1, 2, 3,
+ SubnetID(2));
+ updated_subnet->setSharedNetworkName("one");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSubnet4(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_subnet));
+ // Merge the updated configuration.
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet again. Expect that the subnet uses the shared network-level allocator.
+ subnet = srv_cfg->getCfgSubnets4()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ allocator = subnet->getAllocator(Lease::TYPE_V4);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("random", allocator->getType());
+
+ // Override the allocator at the subnet level.
+ updated_subnet = boost::make_shared<Subnet4>(IOAddress("192.0.4.0"), 26, 1, 2, 3, SubnetID(2));
+ updated_subnet->setModificationTime(getTimestamp("dhcp4_subnet"));
+ updated_subnet->setAllocatorType("iterative");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSubnet4(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_subnet));
+
+ addCreateAuditEntry("dhcp4_subnet", 2);
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet again and expect the subnet-level allocator.
+ subnet = srv_cfg->getCfgSubnets4()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ allocator = subnet->getAllocator(Lease::TYPE_V4);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("iterative", allocator->getType());
+}
+
// ************************ V6 tests *********************
/// @brief Naked @c CBControlDHCPv6 class exposing protected methods.
// Insert subnets into the database.
Subnet6Ptr subnet(new Subnet6(IOAddress("2001:db8:1::"), 64, 1, 2, 3, 4, SubnetID(1)));
+ subnet->setAllocatorType("random");
+ subnet->setPdAllocatorType("random");
subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
subnet->setSharedNetworkName("one");
mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(), ServerSelector::ALL(),
if (hasConfigElement("dhcp6_subnet") &&
(getTimestamp("dhcp6_subnet") > lb_modification_time)) {
ASSERT_TRUE(found_subnet);
+ EXPECT_TRUE(boost::dynamic_pointer_cast<RandomAllocator>
+ (found_subnet->getAllocator(Lease::TYPE_NA)));
+ EXPECT_TRUE(boost::dynamic_pointer_cast<RandomAllocator>
+ (found_subnet->getAllocator(Lease::TYPE_PD)));
EXPECT_TRUE(found_subnet->hasFetchGlobalsFn());
} else {
EXPECT_TRUE(HostMgr::instance().getIPReservationsUnique());
}
+// This test verifies that the allocator type is inherited from the global to
+// shared network to subnet level.
+TEST_F(CBControlDHCPv6Test, allocatorInheritance) {
+ remoteStoreTestConfiguration();
+
+ // Set non-default global allocator.
+ StampedValuePtr global_parameter = StampedValue::create("allocator",
+ Element::create("random"));
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ global_parameter));
+ addCreateAuditEntry("dhcp6_global_parameter", 1);
+ addCreateAuditEntry("dhcp6_shared_network", 1);
+ addCreateAuditEntry("dhcp6_subnet", 2);
+
+ // Merge the configuration including the allocator setting into the current
+ // configuration.
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet that has no allocator specification. The global allocator should
+ // be used for this subnet.
+ auto srv_cfg = CfgMgr::instance().getCurrentCfg();
+ auto subnet = srv_cfg->getCfgSubnets6()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ auto allocator = subnet->getAllocator(Lease::TYPE_NA);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("random", allocator->getType());
+
+ // Update the shared network but use a different allocator type for it.
+ auto updated_network = boost::make_shared<SharedNetwork6>("one");
+ updated_network->setId(1);
+ updated_network->setModificationTime(getTimestamp("dhcp6_shared_network"));
+ updated_network->setAllocatorType("iterative");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_network));
+
+ // Include our subnet in this shared network.
+ auto updated_subnet = boost::make_shared<Subnet6>(IOAddress("2001:db8:2::"), 26, 1, 2, 3, 4,
+ SubnetID(2));
+ updated_subnet->setSharedNetworkName("one");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_subnet));
+ // Merge the updated configuration.
+ addCreateAuditEntry("dhcp6_shared_network", 1);
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet again. Expect that the subnet uses the shared network-level allocator.
+ subnet = srv_cfg->getCfgSubnets6()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ allocator = subnet->getAllocator(Lease::TYPE_NA);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("iterative", allocator->getType());
+
+ // Override the allocator at the subnet level.
+ updated_subnet = boost::make_shared<Subnet6>(IOAddress("2001:db8:2::"), 26, 1, 2, 3, 4,
+ SubnetID(2));
+ updated_subnet->setSharedNetworkName("one");
+ updated_subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
+ updated_subnet->setAllocatorType("random");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_subnet));
+
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet again and expect the subnet-level allocator.
+ subnet = srv_cfg->getCfgSubnets6()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ allocator = subnet->getAllocator(Lease::TYPE_NA);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("random", allocator->getType());
+}
+
+// This test verifies that the prefix delegation allocator type is inherited from the
+// global to shared network to subnet level.
+TEST_F(CBControlDHCPv6Test, pdAllocatorInheritance) {
+ remoteStoreTestConfiguration();
+
+ // Set non-default global allocator.
+ StampedValuePtr global_parameter = StampedValue::create("pd-allocator",
+ Element::create("flq"));
+ auto& mgr = ConfigBackendDHCPv6Mgr::instance();
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateGlobalParameter6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ global_parameter));
+ addCreateAuditEntry("dhcp6_global_parameter", 1);
+ addCreateAuditEntry("dhcp6_shared_network", 1);
+ addCreateAuditEntry("dhcp6_subnet", 2);
+
+ // Merge the configuration including the allocator setting into the current
+ // configuration.
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet that has no allocator specification. The global allocator should
+ // be used for this subnet.
+ auto srv_cfg = CfgMgr::instance().getCurrentCfg();
+ auto subnet = srv_cfg->getCfgSubnets6()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ auto allocator = subnet->getAllocator(Lease::TYPE_PD);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("flq", allocator->getType());
+
+ // Update the shared network but use a different allocator type for it.
+ auto updated_network = boost::make_shared<SharedNetwork6>("one");
+ updated_network->setId(1);
+ updated_network->setModificationTime(getTimestamp("dhcp6_shared_network"));
+ updated_network->setPdAllocatorType("iterative");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSharedNetwork6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_network));
+
+ // Include our subnet in this shared network.
+ auto updated_subnet = boost::make_shared<Subnet6>(IOAddress("2001:db8:2::"), 26, 1, 2, 3, 4,
+ SubnetID(2));
+ updated_subnet->setSharedNetworkName("one");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_subnet));
+ // Merge the updated configuration.
+ addCreateAuditEntry("dhcp6_shared_network", 1);
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet again. Expect that the subnet uses the shared network-level allocator.
+ subnet = srv_cfg->getCfgSubnets6()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ allocator = subnet->getAllocator(Lease::TYPE_PD);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("iterative", allocator->getType());
+
+ // Override the allocator at the subnet level.
+ updated_subnet = boost::make_shared<Subnet6>(IOAddress("2001:db8:2::"), 26, 1, 2, 3, 4,
+ SubnetID(2));
+ updated_subnet->setSharedNetworkName("one");
+ updated_subnet->setModificationTime(getTimestamp("dhcp6_subnet"));
+ updated_subnet->setPdAllocatorType("random");
+ ASSERT_NO_THROW(mgr.getPool()->createUpdateSubnet6(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ updated_subnet));
+
+ ASSERT_NO_THROW(ctl_.databaseConfigApply(BackendSelector::UNSPEC(),
+ ServerSelector::ALL(),
+ getTimestamp(-5),
+ audit_entries_));
+
+ // Get the subnet again and expect the subnet-level allocator.
+ subnet = srv_cfg->getCfgSubnets6()->getBySubnetId(SubnetID(2));
+ ASSERT_TRUE(subnet);
+ allocator = subnet->getAllocator(Lease::TYPE_PD);
+ ASSERT_TRUE(allocator);
+ EXPECT_EQ("random", allocator->getType());
+}
+
+
}
}
};
+// Test that the allocator returns the correct type.
+TEST_F(FreeLeaseQueueAllocatorTest4, getType) {
+ FreeLeaseQueueAllocator alloc(Lease::TYPE_V4, subnet_);
+ EXPECT_EQ("flq", alloc.getType());
+}
+
// Test populating free DHCPv4 leases to the queue.
TEST_F(FreeLeaseQueueAllocatorTest4, populateFreeAddressLeases) {
FreeLeaseQueueAllocator alloc(Lease::TYPE_V4, subnet_);
};
+// Test that the allocator returns the correct type.
+TEST_F(FreeLeaseQueueAllocatorTest6, getType) {
+ FreeLeaseQueueAllocator alloc(Lease::TYPE_NA, subnet_);
+ EXPECT_EQ("flq", alloc.getType());
+}
+
// Test populating free DHCPv6 address leases to the queue.
TEST_F(FreeLeaseQueueAllocatorTest6, populateFreeAddressLeases) {
FreeLeaseQueueAllocator alloc(Lease::TYPE_NA, subnet_);
using IterativeAllocatorTest4 = AllocEngine4Test;
+// Test that the allocator returns the correct type.
+TEST_F(IterativeAllocatorTest4, getType) {
+ IterativeAllocator alloc(Lease::TYPE_V4, subnet_);
+ EXPECT_EQ("iterative", alloc.getType());
+}
+
// This test verifies that the allocator picks addresses that belong to the
// pool
TEST_F(IterativeAllocatorTest4, basic) {
using IterativeAllocatorTest6 = AllocEngine6Test;
+// Test that the allocator returns the correct type.
+TEST_F(IterativeAllocatorTest6, getType) {
+ IterativeAllocator alloc(Lease::TYPE_NA, subnet_);
+ EXPECT_EQ("iterative", alloc.getType());
+}
+
// This test verifies that the allocator picks addresses that belong to the
// pool
TEST_F(IterativeAllocatorTest6, basic) {
&Network4::setAllocatorType,
"iterative", "random");
}
+ {
+ SCOPED_TRACE("default-allocator-type");
+ testNetworkInheritance<TestNetwork4>(&Network::getDefaultAllocatorType,
+ &Network::setDefaultAllocatorType,
+ "random", "flq", false);
+ }
{
SCOPED_TRACE("offer-lifetime");
testNetworkInheritance<TestNetwork4>(&Network4::getOfferLft,
&Network6::setPdAllocatorType,
"iterative", "random");
}
+ {
+ SCOPED_TRACE("default-allocator-type");
+ testNetworkInheritance<TestNetwork6>(&Network::getDefaultAllocatorType,
+ &Network::setDefaultAllocatorType,
+ "random", "iterative", false);
+ }
+ {
+ SCOPED_TRACE("default-pd-allocator-type");
+ testNetworkInheritance<TestNetwork6>(&Network6::getDefaultPdAllocatorType,
+ &Network6::setDefaultPdAllocatorType,
+ "random", "iterative", false);
+ }
{
SCOPED_TRACE("ddns-ttl-percent");
testNetworkInheritance<TestNetwork6>(&Network::getDdnsTtlPercent,
using RandomAllocatorTest4 = AllocEngine4Test;
+// Test that the allocator returns the correct type.
+TEST_F(RandomAllocatorTest4, getType) {
+ RandomAllocator alloc(Lease::TYPE_V4, subnet_);
+ EXPECT_EQ("random", alloc.getType());
+}
+
// Test allocating IPv4 addresses when a subnet has a single pool.
TEST_F(RandomAllocatorTest4, singlePool) {
RandomAllocator alloc(Lease::TYPE_V4, subnet_);
using RandomAllocatorTest6 = AllocEngine6Test;
+// Test that the allocator returns the correct type.
+TEST_F(RandomAllocatorTest6, getType) {
+ RandomAllocator alloc(Lease::TYPE_NA, subnet_);
+ EXPECT_EQ("random", alloc.getType());
+}
+
// Test allocating IPv6 addresses when a subnet has a single pool.
TEST_F(RandomAllocatorTest6, singlePool) {
RandomAllocator alloc(Lease::TYPE_NA, subnet_);