diff --git a/src/lib/dhcpsrv/allocator.cc b/src/lib/dhcpsrv/allocator.cc index 279abc9ce9..392c12c65d 100644 --- a/src/lib/dhcpsrv/allocator.cc +++ b/src/lib/dhcpsrv/allocator.cc @@ -6,13 +6,35 @@ #include #include +#include using namespace isc::util; namespace isc { namespace dhcp { -bool Allocator::isValidPrefixPool(Allocator::PrefixLenMatchType prefix_length_match, +Allocator::Allocator(Lease::Type type, const WeakSubnetPtr& subnet) + : pool_type_(type), + subnet_id_(0), + subnet_(subnet) { + // Remember subnet ID in a separate variable. It may be needed in + // the destructor where the subnet weak pointer is unavailable. + subnet_id_ = subnet_.lock()->getID(); +} + +Allocator::~Allocator() { + if (!LeaseMgrFactory::haveInstance()) { + // If there is no lease manager instance, the callbacks are + // gone already anyway. + return; + } + // Remove the callbacks. + auto& lease_mgr = LeaseMgrFactory::instance(); + lease_mgr.unregisterCallbacks(subnet_id_, pool_type_); +} + +bool +Allocator::isValidPrefixPool(Allocator::PrefixLenMatchType prefix_length_match, PoolPtr pool, uint8_t hint_prefix_length) { auto pool6 = boost::dynamic_pointer_cast(pool); if (!pool6) { diff --git a/src/lib/dhcpsrv/allocator.h b/src/lib/dhcpsrv/allocator.h index 7967832e5d..dd82ea8a43 100644 --- a/src/lib/dhcpsrv/allocator.h +++ b/src/lib/dhcpsrv/allocator.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -70,13 +71,12 @@ public: /// @param type specifies pool type (addresses, temporary addresses /// or prefixes). /// @param subnet weak pointer to the subnet owning the allocator. - Allocator(Lease::Type type, const WeakSubnetPtr& subnet) - : pool_type_(type), - subnet_(subnet) { - } + Allocator(Lease::Type type, const WeakSubnetPtr& subnet); - /// @brief Virtual destructor - virtual ~Allocator() = default; + /// @brief Virtual destructor. + /// + /// Removes all LeaseMgr callbacks it installed. + virtual ~Allocator(); /// @brief Picks an address. /// @@ -148,6 +148,16 @@ public: static bool isValidPrefixPool(Allocator::PrefixLenMatchType prefix_length_match, PoolPtr pool, uint8_t hint_prefix_length); + /// @brief Performs allocator initialization after server's reconfiguration. + /// + /// Some allocators install callbacks in the lease manager to keep track of + /// the lease allocations. These callbacks may only be installed when the + /// lease manager instance is available (i.e., when the server finishes 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() {}; + private: /// @brief Picks an address. @@ -196,6 +206,9 @@ protected: /// @brief Defines pool type allocation Lease::Type pool_type_; + /// @brief ID of a subnet to which the allocator belongs. + SubnetID subnet_id_; + /// @brief Weak pointer to the subnet owning the allocator. WeakSubnetPtr subnet_; diff --git a/src/lib/dhcpsrv/cfg_subnets4.cc b/src/lib/dhcpsrv/cfg_subnets4.cc index 9e5c7a33cc..846fbac50f 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.cc +++ b/src/lib/dhcpsrv/cfg_subnets4.cc @@ -571,6 +571,13 @@ CfgSubnets4::updateStatistics() { } } +void +CfgSubnets4::initAllocatorsAfterConfigure() { + for (auto subnet : subnets_) { + subnet->initAllocatorsAfterConfigure(); + } +} + ElementPtr CfgSubnets4::toElement() const { ElementPtr result = Element::createList(); diff --git a/src/lib/dhcpsrv/cfg_subnets4.h b/src/lib/dhcpsrv/cfg_subnets4.h index d1d3c32184..dbacf94337 100644 --- a/src/lib/dhcpsrv/cfg_subnets4.h +++ b/src/lib/dhcpsrv/cfg_subnets4.h @@ -327,6 +327,9 @@ public: /// configuration and also subnet-ids may change. void removeStatistics(); + /// @brief Calls @c initAllocatorsAfterConfigure for each subnet. + void initAllocatorsAfterConfigure(); + /// @brief Unparse a configuration object /// /// @return a pointer to unparsed configuration diff --git a/src/lib/dhcpsrv/cfg_subnets6.cc b/src/lib/dhcpsrv/cfg_subnets6.cc index dcd4db8313..6725200807 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.cc +++ b/src/lib/dhcpsrv/cfg_subnets6.cc @@ -473,6 +473,13 @@ CfgSubnets6::updateStatistics() { } } +void +CfgSubnets6::initAllocatorsAfterConfigure() { + for (auto subnet : subnets_) { + subnet->initAllocatorsAfterConfigure(); + } +} + ElementPtr CfgSubnets6::toElement() const { ElementPtr result = Element::createList(); diff --git a/src/lib/dhcpsrv/cfg_subnets6.h b/src/lib/dhcpsrv/cfg_subnets6.h index 54f3774460..72c023df44 100644 --- a/src/lib/dhcpsrv/cfg_subnets6.h +++ b/src/lib/dhcpsrv/cfg_subnets6.h @@ -277,6 +277,9 @@ public: /// configuration and also subnet-ids may change. void removeStatistics(); + /// @brief Calls @c initAllocatorsAfterConfigure for each subnet. + void initAllocatorsAfterConfigure(); + /// @brief Unparse a configuration object /// /// @return a pointer to unparsed configuration diff --git a/src/lib/dhcpsrv/dhcpsrv_messages.cc b/src/lib/dhcpsrv/dhcpsrv_messages.cc index 0f4475206d..d8b0ab3de0 100644 --- a/src/lib/dhcpsrv/dhcpsrv_messages.cc +++ b/src/lib/dhcpsrv/dhcpsrv_messages.cc @@ -69,6 +69,8 @@ extern const isc::log::MessageID DHCPSRV_LEASE4_EXTENDED_INFO_SANITY_FAIL = "DHC extern const isc::log::MessageID DHCPSRV_LEASE4_EXTENDED_INFO_UPGRADED = "DHCPSRV_LEASE4_EXTENDED_INFO_UPGRADED"; extern const isc::log::MessageID DHCPSRV_LEASE6_EXTENDED_INFO_SANITY_FAIL = "DHCPSRV_LEASE6_EXTENDED_INFO_SANITY_FAIL"; extern const isc::log::MessageID DHCPSRV_LEASE6_EXTENDED_INFO_UPGRADED = "DHCPSRV_LEASE6_EXTENDED_INFO_UPGRADED"; +extern const isc::log::MessageID DHCPSRV_LEASE_MGR_CALLBACK_EXCEPTION = "DHCPSRV_LEASE_MGR_CALLBACK_EXCEPTION"; +extern const isc::log::MessageID DHCPSRV_LEASE_MGR_CALLBACK_UNKNOWN_EXCEPTION = "DHCPSRV_LEASE_MGR_CALLBACK_UNKNOWN_EXCEPTION"; extern const isc::log::MessageID DHCPSRV_LEASE_SANITY_FAIL = "DHCPSRV_LEASE_SANITY_FAIL"; extern const isc::log::MessageID DHCPSRV_LEASE_SANITY_FAIL_DISCARD = "DHCPSRV_LEASE_SANITY_FAIL_DISCARD"; extern const isc::log::MessageID DHCPSRV_LEASE_SANITY_FIXED = "DHCPSRV_LEASE_SANITY_FIXED"; @@ -321,6 +323,8 @@ const char* values[] = { "DHCPSRV_LEASE4_EXTENDED_INFO_UPGRADED", "extended info for lease %1 was upgraded", "DHCPSRV_LEASE6_EXTENDED_INFO_SANITY_FAIL", "extended info for lease %1 failed checks (%2)", "DHCPSRV_LEASE6_EXTENDED_INFO_UPGRADED", "extended info for lease %1 was upgraded", + "DHCPSRV_LEASE_MGR_CALLBACK_EXCEPTION", "exception occurred in a lease manager callback for callback type %1, subnet id %2, and lease %3: %4", + "DHCPSRV_LEASE_MGR_CALLBACK_UNKNOWN_EXCEPTION", "unknown exception occurred in a lease manager callback for callback type %1, subnet id %2, and lease %3", "DHCPSRV_LEASE_SANITY_FAIL", "The lease %1 with subnet-id %2 failed subnet-id checks (%3).", "DHCPSRV_LEASE_SANITY_FAIL_DISCARD", "The lease %1 with subnet-id %2 failed subnet-id checks (%3) and was dropped.", "DHCPSRV_LEASE_SANITY_FIXED", "The lease %1 with subnet-id %2 failed subnet-id checks, but was corrected to subnet-id %3.", diff --git a/src/lib/dhcpsrv/subnet.cc b/src/lib/dhcpsrv/subnet.cc index 67f223c5cd..762589c0d8 100644 --- a/src/lib/dhcpsrv/subnet.cc +++ b/src/lib/dhcpsrv/subnet.cc @@ -423,6 +423,13 @@ const PoolPtr Subnet::getPool(Lease::Type type, const isc::asiolink::IOAddress& return (candidate); } +void +Subnet::initAllocatorsAfterConfigure() { + for (auto allocator : allocators_) { + allocator.second->initAfterConfigure(); + } +} + const PoolPtr Subnet::getPool(Lease::Type type, const ClientClasses& client_classes, const isc::asiolink::IOAddress& hint) const { diff --git a/src/lib/dhcpsrv/subnet.h b/src/lib/dhcpsrv/subnet.h index 3e0291a4d5..a388d5f18a 100644 --- a/src/lib/dhcpsrv/subnet.h +++ b/src/lib/dhcpsrv/subnet.h @@ -316,6 +316,9 @@ public: /// @param allocation_state allocation state instance. void setAllocationState(Lease::Type type, const SubnetAllocationStatePtr& allocation_state); + /// @brief Calls @c initAfterConfigure for each allocator. + void initAllocatorsAfterConfigure(); + protected: /// @brief Protected constructor. diff --git a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc index 2c484f46de..244fe355e2 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets4_unittest.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,30 @@ using namespace isc::util; namespace { +/// @brief An allocator recording calls to @c initAfterConfigure. +class InitRecordingAllocator : public IterativeAllocator { +public: + + /// @brief Constructor. + /// + /// @param type specifies the type of allocated leases. + /// @param subnet weak pointer to the subnet owning the allocator. + InitRecordingAllocator(Lease::Type type, const WeakSubnetPtr& subnet) + : IterativeAllocator(type, subnet), callcount_(0) { + } + + /// @brief Increases the call count of this function. + /// + /// The call count can be later examined to check whether or not + /// the function was called. + virtual void initAfterConfigure() { + ++callcount_; + }; + + /// @brief Call count of the @c initAllocatorsAfterConfigure. + int callcount_; +}; + /// @brief Verifies that a set of subnets contains a given a subnet /// /// @param cfg_subnets set of subnets in which to look @@ -2179,6 +2204,38 @@ TEST(CfgSubnets4Test, getLinks) { EXPECT_EQ(24, link_len); } +// This test verifies that for each subnet in the configuration it calls +// the initAllocatorAfterConfigure function. +TEST(CfgSubnets4Test, initAllocatorsAfterConfigure) { + CfgSubnets4 cfg; + + // Create 4 subnets. + Subnet4Ptr subnet0(new Subnet4(IOAddress("0.0.0.0"), + 24, 1, 2, 3, SubnetID(111))); + Subnet4Ptr subnet1(new Subnet4(IOAddress("192.0.1.0"), + 26, 1, 2, 3, SubnetID(1))); + Subnet4Ptr subnet2(new Subnet4(IOAddress("192.0.2.0"), + 26, 1, 2, 3, SubnetID(2))); + Subnet4Ptr subnet3(new Subnet4(IOAddress("192.0.3.0"), + 26, 1, 2, 3, SubnetID(3))); + + auto allocator0 = boost::make_shared(Lease::TYPE_V4, subnet0); + subnet0->setAllocator(Lease::TYPE_V4, allocator0); + + auto allocator2 = boost::make_shared(Lease::TYPE_V4, subnet2); + subnet2->setAllocator(Lease::TYPE_V4, allocator2); + + cfg.add(subnet0); + cfg.add(subnet1); + cfg.add(subnet2); + cfg.add(subnet3); + + cfg.initAllocatorsAfterConfigure(); + + EXPECT_EQ(1, allocator0->callcount_); + EXPECT_EQ(1, allocator2->callcount_); +} + /// @brief Test fixture for parsing v4 Subnets that can verify log output. class Subnet4ParserTest : public LogContentTest { public: diff --git a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc index ddd1e41742..c6c821af60 100644 --- a/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_subnets6_unittest.cc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,30 @@ using namespace isc::util; namespace { +/// @brief An allocator recording calls to @c initAfterConfigure. +class InitRecordingAllocator : public IterativeAllocator { +public: + + /// @brief Constructor. + /// + /// @param type specifies the type of allocated leases. + /// @param subnet weak pointer to the subnet owning the allocator. + InitRecordingAllocator(Lease::Type type, const WeakSubnetPtr& subnet) + : IterativeAllocator(type, subnet), callcount_(0) { + } + + /// @brief Increases the call count of this function. + /// + /// The call count can be later examined to check whether or not + /// the function was called. + virtual void initAfterConfigure() { + ++callcount_; + }; + + /// @brief Call count of the @c initAllocatorsAfterConfigure. + int callcount_; +}; + /// @brief Generates interface id option. /// /// @param text Interface id in a textual format. @@ -2101,6 +2126,39 @@ TEST(CfgSubnets6Test, getLinks) { EXPECT_EQ(48, link_len); } +// This test verifies that for each subnet in the configuration it calls +// the registerLeaseMgrCallback function. +TEST(CfgSubnets6Test, initAllocatorsAfterConfigure) { + CfgSubnets6 cfg; + + // Create 4 subnets. + Subnet6Ptr subnet0(new Subnet6(IOAddress("2001:db8:1::"), + 64, 1, 2, 3, 4, SubnetID(111))); + Subnet6Ptr subnet1(new Subnet6(IOAddress("2001:db8:2::"), + 64, 1, 2, 3, 4, SubnetID(1))); + Subnet6Ptr subnet2(new Subnet6(IOAddress("2001:db8:3::"), + 64, 1, 2, 3, 4, SubnetID(2))); + Subnet6Ptr subnet3(new Subnet6(IOAddress("2001:db8:4::"), + 64, 1, 2, 3, 4, SubnetID(3))); + + auto allocator0 = boost::make_shared(Lease::TYPE_NA, subnet0); + subnet0->setAllocator(Lease::TYPE_NA, allocator0); + + auto allocator2 = boost::make_shared(Lease::TYPE_PD, subnet2); + subnet2->setAllocator(Lease::TYPE_PD, allocator2); + + cfg.add(subnet0); + cfg.add(subnet1); + cfg.add(subnet2); + cfg.add(subnet3); + + cfg.initAllocatorsAfterConfigure(); + + EXPECT_EQ(1, allocator0->callcount_); + EXPECT_EQ(1, allocator2->callcount_); +} + + /// @brief Test fixture for parsing v6 Subnets that can verify log output. class Subnet6ParserTest : public LogContentTest { public: