]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#2823] Config backend sets allocators
authorMarcin Siodelski <marcin@isc.org>
Wed, 5 Apr 2023 08:28:26 +0000 (10:28 +0200)
committerMarcin Siodelski <marcin@isc.org>
Wed, 19 Apr 2023 16:26:04 +0000 (18:26 +0200)
18 files changed:
src/lib/dhcpsrv/allocator.cc
src/lib/dhcpsrv/allocator.h
src/lib/dhcpsrv/cb_ctl_dhcp4.cc
src/lib/dhcpsrv/cb_ctl_dhcp6.cc
src/lib/dhcpsrv/cfg_subnets4.cc
src/lib/dhcpsrv/cfg_subnets6.cc
src/lib/dhcpsrv/dhcpsrv_messages.mes
src/lib/dhcpsrv/flq_allocator.cc
src/lib/dhcpsrv/flq_allocator.h
src/lib/dhcpsrv/iterative_allocator.h
src/lib/dhcpsrv/network.h
src/lib/dhcpsrv/random_allocator.h
src/lib/dhcpsrv/subnet.cc
src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc
src/lib/dhcpsrv/tests/flq_allocator_unittest.cc
src/lib/dhcpsrv/tests/iterative_allocator_unittest.cc
src/lib/dhcpsrv/tests/network_unittest.cc
src/lib/dhcpsrv/tests/random_allocator_unittest.cc

index 392c12c65daa0e2475b7458e1c6b0aa52119fbf2..5925689d20aa25e67e297af3c8bf99503aab4a65 100644 (file)
@@ -7,6 +7,7 @@
 #include <config.h>
 #include <dhcpsrv/allocator.h>
 #include <dhcpsrv/lease_mgr_factory.h>
+#include <dhcpsrv/dhcpsrv_log.h>
 
 using namespace isc::util;
 
@@ -14,7 +15,8 @@ namespace isc {
 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
@@ -63,5 +65,19 @@ Allocator::isValidPrefixPool(Allocator::PrefixLenMatchType prefix_length_match,
     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;
+}
+
 }
 }
index f8dcdf253e02ca26dcd862e19e8d063e1d02e5ee..a94bd81a2deb866b8d3dc6a98bce79b83d8223ad 100644 (file)
@@ -78,6 +78,11 @@ public:
     /// 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
@@ -156,7 +161,15 @@ public:
     /// 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:
 
@@ -203,6 +216,12 @@ 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_;
 
index 058530e54115c33aaf7076834fa8865ad0832019..ca1c9d736693d29fd523e737cf47c67f8d70a239 100644 (file)
@@ -1,4 +1,4 @@
-// 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
@@ -48,7 +48,11 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
                                      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
@@ -57,9 +61,8 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
     // 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.
@@ -85,7 +88,7 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
 
             // 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;
         }
@@ -97,7 +100,7 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
             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.
@@ -105,19 +108,19 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
             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",
@@ -126,7 +129,7 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
                 // 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;
@@ -136,7 +139,7 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
                         network->del(subnet->getID());
                     }
                     // Actually delete the subnet from the configuration.
-                    cfg->getCfgSubnets4()->del((*entry)->getObjectId());
+                    current_cfg->getCfgSubnets4()->del((*entry)->getObjectId());
                 }
             }
 
@@ -149,26 +152,28 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
     }
 
     // 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);
@@ -181,10 +186,10 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
     }
 
     // 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);
@@ -197,10 +202,10 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
     }
 
     // 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.
@@ -213,57 +218,99 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
         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
@@ -282,13 +329,14 @@ CBControlDHCPv4::databaseConfigApply(const BackendSelector& backend_selector,
                 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();
 
index 1278d9409e15fdb9719c68dc6963717d281f7747..4624bf495b038c006673df18fa022eaf1482c802 100644 (file)
@@ -1,4 +1,4 @@
-// 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
@@ -47,6 +47,10 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
                                      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
@@ -55,9 +59,8 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
     // 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.
@@ -84,7 +87,7 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
 
             // 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;
         }
@@ -96,7 +99,7 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
             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.
@@ -104,19 +107,19 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
             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",
@@ -125,7 +128,7 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
                 // 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;
@@ -135,7 +138,7 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
                         network->del(subnet->getID());
                     }
                     // Actually delete the subnet from the configuration.
-                    cfg->getCfgSubnets6()->del((*entry)->getObjectId());
+                    current_cfg->getCfgSubnets6()->del((*entry)->getObjectId());
                 }
             }
 
@@ -152,22 +155,24 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
 
     // 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);
@@ -180,10 +185,10 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
     }
 
     // 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);
@@ -196,10 +201,10 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
     }
 
     // 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.
@@ -212,51 +217,106 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
         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.
@@ -264,6 +324,7 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
         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
@@ -286,10 +347,11 @@ CBControlDHCPv6::databaseConfigApply(const db::BackendSelector& backend_selector
         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();
 
index aba88116f19b4729ff0c37365f62b0ebc5af1170..9d017426e34ae01a69684ec60e6f0d550dcfab60 100644 (file)
@@ -178,6 +178,8 @@ CfgSubnets4::merge(CfgOptionDefPtr cfg_def, CfgSharedNetworks4Ptr networks,
                           << ", network does not exist");
             }
         }
+        // Instantiate the configured allocator and its state.
+        other_subnet->createAllocators();
     }
 }
 
index a86ee9222c5db2776ad8454ff8672fc5bd294466..9632d2f88caf91630862bcae818112dab8cb5349 100644 (file)
@@ -178,6 +178,8 @@ CfgSubnets6::merge(CfgOptionDefPtr cfg_def, CfgSharedNetworks6Ptr networks,
                           << ", network does not exist");
             }
         }
+        // Instantiate the configured allocators and their states.
+        other_subnet->createAllocators();
     }
 }
 
index f9f15d034ecc40da26a543ee753ae3d0b28f6a3a..1f3dc0737c4c97cdf61b8e7916bf6a44404eb8a3 100644 (file)
@@ -265,6 +265,10 @@ specified IPv6 subnet in its current configuration. Subnet ID and result
 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.
index b1265559b48257abb2601054ca6457fe55db4a57..0ce83ff2fce39641ddb367a7cbc357d3f313dc09 100644 (file)
@@ -135,7 +135,7 @@ FreeLeaseQueueAllocator::pickPrefixInternal(const ClientClasses& client_classes,
 }
 
 void
-FreeLeaseQueueAllocator::initAfterConfigure() {
+FreeLeaseQueueAllocator::initAfterConfigureInternal() {
     auto subnet = subnet_.lock();
     auto pools = subnet->getPools(pool_type_);
     if (pools.empty()) {
index b95b4d82600c4113476f647b1ca0baa026c4f13f..e3b9cf89ab46d7b1a5dfb0864a19f3c4424ac575 100644 (file)
@@ -44,13 +44,20 @@ public:
     /// @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).
     ///
index fafcc6478902847cbeddb74610002f1446684cd6..7be2ea9725aed2fb87ee59e9fde76e28bc6e7914 100644 (file)
@@ -31,6 +31,13 @@ public:
     /// @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.
index 147f79e8267bde37442121a07fb29827092c2215..3eda94f6a36fe1ea439122f93f4495b27d813bd6 100644 (file)
@@ -219,7 +219,8 @@ public:
           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.
@@ -818,6 +819,26 @@ public:
         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.
@@ -1209,11 +1230,14 @@ protected:
     /// @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.
     ///
@@ -1395,7 +1419,8 @@ public:
 
     /// @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)
@@ -1478,6 +1503,26 @@ public:
         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.
@@ -1500,6 +1545,9 @@ private:
 
     /// @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
index 04a0ad4d78b77bf8efc7c93c96dc29d2d79b83cb..906483029766a6eb2f5750d5e770441c93017b83 100644 (file)
@@ -36,6 +36,13 @@ public:
     /// @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.
index dbd7108107af26aacebe89570581ee23309adfd0..e6038bb9320a25ef611cb7b7672c154658bb0897 100644 (file)
@@ -750,7 +750,11 @@ Subnet::toElement() const {
 
 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()));
@@ -760,7 +764,7 @@ Subnet4::createAllocators() {
             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()));
@@ -769,6 +773,9 @@ Subnet4::createAllocators() {
         }
 
     } else {
+        setAllocator(Lease::TYPE_V4,
+                     boost::make_shared<IterativeAllocator>
+                     (Lease::TYPE_V4, shared_from_this()));
         for (auto pool : pools_) {
             pool->setAllocationState(PoolIterativeAllocationState::create(pool));
         }
@@ -811,10 +818,11 @@ Subnet4::parsePrefix(const std::string& prefix) {
 
 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()));
@@ -824,26 +832,43 @@ Subnet6::createAllocators() {
         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));
@@ -851,7 +876,7 @@ Subnet6::createAllocators() {
     }
     // 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));
@@ -859,9 +884,9 @@ Subnet6::createAllocators() {
     }
     // 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));
index 7a7b0d6797778969f6d07ad6476811f318287503..8706767a0e03d9585e60fdf5c3fd9a35a7adea66 100644 (file)
@@ -14,6 +14,7 @@
 #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>
@@ -372,6 +373,7 @@ public:
         // 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);
@@ -611,6 +613,8 @@ public:
             (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);
@@ -1088,6 +1092,90 @@ TEST_F(CBControlDHCPv4Test, ipReservationsNonUniqueRefused) {
     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.
@@ -1212,6 +1300,8 @@ public:
 
         // 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(),
@@ -1435,6 +1525,10 @@ public:
         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 {
@@ -1889,4 +1983,175 @@ TEST_F(CBControlDHCPv6Test, ipReservationsNonUniqueRefused) {
     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());
+}
+
+
 }
index 5e60cf81b23c52bb5f18c238f3c598d33fe9fd6c..33ae382901a6319a92e622c62ecfb41f70ff71dc 100644 (file)
@@ -41,6 +41,12 @@ public:
     }
 };
 
+// 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_);
@@ -353,6 +359,12 @@ public:
 
 };
 
+// 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_);
index c58f4b7a9890bb0cd900da864abd98e34c8f14bf..3d28607e455a839b91adc8276fbfc01eaccf73ed 100644 (file)
@@ -21,6 +21,12 @@ namespace test {
 
 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) {
@@ -111,6 +117,12 @@ TEST_F(IterativeAllocatorTest4, manyPools) {
 
 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) {
index 7cdfc52d7b21881172f13b5143e9955e8680db03..9185d036eeffc00c088f93c2029a2f802742d6af 100644 (file)
@@ -361,6 +361,12 @@ TEST_F(NetworkTest, inheritanceSupport4) {
                                              &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,
@@ -485,6 +491,18 @@ TEST_F(NetworkTest, inheritanceSupport6) {
                                              &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,
index e24e040e022803cf0afa2502bbc26f2880511bf3..55fec78d2f1276b00955c873f74b57ea6eeb5567 100644 (file)
@@ -23,6 +23,12 @@ namespace test {
 
 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_);
@@ -178,6 +184,12 @@ TEST_F(RandomAllocatorTest4, clientClasses) {
 
 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_);