libkea_dhcpsrv_la_SOURCES += subnet_id.h
libkea_dhcpsrv_la_SOURCES += subnet_selector.h
libkea_dhcpsrv_la_SOURCES += timer_mgr.cc timer_mgr.h
+libkea_dhcpsrv_la_SOURCES += tracking_lease_mgr.cc tracking_lease_mgr.h
libkea_dhcpsrv_la_SOURCES += utils.h
libkea_dhcpsrv_la_SOURCES += writable_host_data_source.h
ncr_generator.h \
network.h \
network_state.h \
+ tracking_lease_mgr.h \
pool.h \
random_allocation_state.h \
random_allocator.h \
extern const isc::log::MessageID DHCPSRV_LEASE4_EXTENDED_INFO_UPGRADED;
extern const isc::log::MessageID DHCPSRV_LEASE6_EXTENDED_INFO_SANITY_FAIL;
extern const isc::log::MessageID DHCPSRV_LEASE6_EXTENDED_INFO_UPGRADED;
+extern const isc::log::MessageID DHCPSRV_LEASE_MGR_CALLBACK_EXCEPTION;
+extern const isc::log::MessageID DHCPSRV_LEASE_MGR_CALLBACK_UNKNOWN_EXCEPTION;
extern const isc::log::MessageID DHCPSRV_LEASE_SANITY_FAIL;
extern const isc::log::MessageID DHCPSRV_LEASE_SANITY_FAIL_DISCARD;
extern const isc::log::MessageID DHCPSRV_LEASE_SANITY_FIXED;
should be of the form 'keyword=value keyword=value...' is included in
the message.
+% DHCPSRV_LEASE_MGR_CALLBACK_EXCEPTION exception occurred in a lease manager callback for callback type %1, subnet id %2, and lease %3: %4
+This warning message is printed when one of the callback functions registered
+in the lease manager causes an error. The callback functions can serve
+different purposes and they likely log the detailed error messages. This
+error message possibly indicates an unhandled error. The first argument
+indicates a callback type. The second argument prints the subnet id.
+The third argument prints the lease for which the error has occurred.
+The last argument prints the error text.
+
+% DHCPSRV_LEASE_MGR_CALLBACK_UNKNOWN_EXCEPTION unknown exception occurred in a lease manager callback for callback type %1, subnet id %2, and lease %3
+This warning message is printed when one of the callback functions registered
+in the lease manager causes an unknown error. The callback functions can serve
+different purposes and they likely log the detailed error messages. This
+error message possibly indicates an unhandled error. The first argument
+indicates a callback type. The second argument prints the subnet id.
+The third argument prints the lease for which the error has occurred.
+This log message variant contains no error text because it is triggered
+by an unknown exception.
+
% DHCPSRV_LEASE4_EXTENDED_INFO_SANITY_FAIL extended info for lease %1 failed checks (%2)
This error message is printed when a lease extended info failed to
pass sanity checks. The detail of the found problem was displayed and
libdhcpsrv_unittests_SOURCES += subnet_unittest.cc
libdhcpsrv_unittests_SOURCES += test_get_callout_handle.cc test_get_callout_handle.h
libdhcpsrv_unittests_SOURCES += timer_mgr_unittest.cc
+libdhcpsrv_unittests_SOURCES += tracking_lease_mgr_unittest.cc
libdhcpsrv_unittests_SOURCES += network_state_unittest.cc
libdhcpsrv_unittests_SOURCES += network_unittest.cc
#include <dhcpsrv/lease_mgr.h>
#include <dhcpsrv/memfile_lease_mgr.h>
#include <dhcpsrv/testutils/test_utils.h>
+#include <dhcpsrv/testutils/concrete_lease_mgr.h>
#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
#include <testutils/gtest_utils.h>
using namespace isc::dhcp;
using namespace isc::dhcp::test;
-// This is a concrete implementation of a Lease database. It does not do
-// anything useful and is used for abstract LeaseMgr class testing.
-class ConcreteLeaseMgr : public LeaseMgr {
-public:
-
- /// @brief The sole lease manager constructor
- ///
- /// dbconfig is a generic way of passing parameters. Parameters
- /// are passed in the "name=value" format, separated by spaces.
- /// Values may be enclosed in double quotes, if needed.
- ConcreteLeaseMgr(const DatabaseConnection::ParameterMap&)
- : LeaseMgr()
- {}
-
- /// @brief Destructor
- virtual ~ConcreteLeaseMgr()
- {}
-
- /// @brief Adds an IPv4 lease.
- ///
- /// @param lease lease to be added
- virtual bool addLease(const Lease4Ptr&) override {
- return (false);
- }
-
- /// @brief Adds an IPv6 lease.
- ///
- /// @param lease lease to be added
- virtual bool addLease(const Lease6Ptr&) override {
- return (false);
- }
-
- /// @brief Returns existing IPv4 lease for specified IPv4 address.
- ///
- /// @param addr address of the searched lease
- ///
- /// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&) const override {
- return (Lease4Ptr());
- }
-
- /// @brief Returns existing IPv4 leases for specified hardware address.
- ///
- /// Although in the usual case there will be only one lease, for mobile
- /// clients or clients with multiple static/fixed/reserved leases there
- /// can be more than one. Thus return type is a container, not a single
- /// pointer.
- ///
- /// @param hwaddr hardware address of the client
- ///
- /// @return lease collection
- virtual Lease4Collection getLease4(const HWAddr&) const override {
- return (Lease4Collection());
- }
-
- /// @brief Returns existing IPv4 leases for specified hardware address
- /// and a subnet
- ///
- /// There can be at most one lease for a given HW address in a single
- /// pool, so this method with either return a single lease or NULL.
- ///
- /// @param hwaddr hardware address of the client
- /// @param subnet_id identifier of the subnet that lease must belong to
- ///
- /// @return a pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(const HWAddr&, SubnetID) const override {
- return (Lease4Ptr());
- }
-
- /// @brief Returns existing IPv4 lease for specified client-id
- ///
- /// @param clientid client identifier
- ///
- /// @return lease collection
- virtual Lease4Collection getLease4(const ClientId&) const override {
- return (Lease4Collection());
- }
-
- /// @brief Returns existing IPv4 lease for specified client-id
- ///
- /// There can be at most one lease for a given HW address in a single
- /// pool, so this method with either return a single lease or NULL.
- ///
- /// @param clientid client identifier
- /// @param subnet_id identifier of the subnet that lease must belong to
- ///
- /// @return a pointer to the lease (or NULL if a lease is not found)
- virtual Lease4Ptr getLease4(const ClientId&, SubnetID) const override {
- return (Lease4Ptr());
- }
-
- /// @brief Returns all IPv4 leases for the particular subnet identifier.
- ///
- /// @param subnet_id subnet identifier.
- ///
- /// @return Lease collection (may be empty if no IPv4 lease found).
- virtual Lease4Collection getLeases4(SubnetID) const override {
- return (Lease4Collection());
- }
-
- /// @brief Returns all IPv4 leases for the particular hostname.
- ///
- /// @param hostname hostname in lower case.
- ///
- /// @return Lease collection (may be empty if no IPv4 lease found).
- virtual Lease4Collection getLeases4(const std::string&) const override {
- return (Lease4Collection());
- }
-
- /// @brief Returns all IPv4 leases.
- ///
- /// @return Lease collection (may be empty if no IPv4 lease found).
- virtual Lease4Collection getLeases4() const override {
- return (Lease4Collection());
- }
-
- /// @brief Returns range of IPv4 leases using paging.
- ///
- /// This method implements paged browsing of the lease database. The first
- /// parameter specifies a page size. The second parameter is optional and
- /// specifies the starting address of the range. This address is excluded
- /// from the returned range. The IPv4 zero address (default) denotes that
- /// the first page should be returned. There is no guarantee about the
- /// order of returned leases.
- ///
- /// The typical usage of this method is as follows:
- /// - Get the first page of leases by specifying IPv4 zero address as the
- /// beginning of the range.
- /// - Last address of the returned range should be used as a starting
- /// address for the next page in the subsequent call.
- /// - If the number of leases returned is lower than the page size, it
- /// indicates that the last page has been retrieved.
- /// - If there are no leases returned it indicates that the previous page
- /// was the last page.
- ///
- /// @param lower_bound_address IPv4 address used as lower bound for the
- /// returned range.
- /// @param page_size maximum size of the page returned.
- ///
- /// @return Lease collection (may be empty if no IPv4 lease found).
- virtual Lease4Collection
- getLeases4(const asiolink::IOAddress& /* lower_bound_address */,
- const LeasePageSize& /* page_size */) const override {
- return (Lease4Collection());
- }
-
- /// @brief Returns existing IPv6 lease for a given IPv6 address.
- ///
- /// @param addr address of the searched lease
- ///
- /// @return smart pointer to the lease (or NULL if a lease is not found)
- virtual Lease6Ptr getLease6(Lease::Type /* not used yet */,
- const isc::asiolink::IOAddress&) const override {
- return (Lease6Ptr());
- }
-
- /// @brief Returns existing IPv6 lease for a given DUID+IA combination
- ///
- /// @param duid ignored
- /// @param iaid ignored
- ///
- /// @return whatever is set in leases6_ field
- virtual Lease6Collection getLeases6(Lease::Type /* not used yet */,
- const DUID&, uint32_t) const override {
- return (leases6_);
- }
-
- /// @brief Returns existing IPv6 lease for a given DUID+IA+subnet-id combination
- ///
- /// @param duid ignored
- /// @param iaid ignored
- /// @param subnet_id ignored
- ///
- /// @return whatever is set in leases6_ field
- virtual Lease6Collection getLeases6(Lease::Type /* not used yet */,
- const DUID&, uint32_t, SubnetID) const override {
- return (leases6_);
- }
-
- /// @brief Returns collection of lease for matching DUID
- ///
- /// @param duid ignored
- /// @return whatever is set in leases6_ field
- virtual Lease6Collection getLeases6(const DUID&) const override {
- return (leases6_);
- }
-
- /// @brief Returns all IPv6 leases for the particular subnet identifier.
- ///
- /// @param subnet_id subnet identifier.
- ///
- /// @return Lease collection (may be empty if no IPv6 lease found).
- virtual Lease6Collection getLeases6(SubnetID) const override {
- return (Lease6Collection());
- }
-
- /// @brief Returns all IPv6 leases for the particular hostname.
- ///
- /// @param hostname hostname in lower case.
- ///
- /// @return Lease collection (may be empty if no IPv6 lease found).
- virtual Lease6Collection getLeases6(const std::string&) const override {
- return (Lease6Collection());
- }
-
- /// @brief Returns all IPv6 leases.
- ///
- /// @return Lease collection (may be empty if no IPv6 lease found).
- virtual Lease6Collection getLeases6() const override {
- return (Lease6Collection());
- }
-
- /// @brief Returns range of IPv6 leases using paging.
- ///
- /// This method implements paged browsing of the lease database. The first
- /// parameter specifies a page size. The second parameter is optional and
- /// specifies the starting address of the range. This address is excluded
- /// from the returned range. The IPv6 zero address (default) denotes that
- /// the first page should be returned. There is no guarantee about the
- /// order of returned leases.
- ///
- /// The typical usage of this method is as follows:
- /// - Get the first page of leases by specifying IPv6 zero address as the
- /// beginning of the range.
- /// - Last address of the returned range should be used as a starting
- /// address for the next page in the subsequent call.
- /// - If the number of leases returned is lower than the page size, it
- /// indicates that the last page has been retrieved.
- /// - If there are no leases returned it indicates that the previous page
- /// was the last page.
- ///
- /// @param lower_bound_address IPv4 address used as lower bound for the
- /// returned range.
- /// @param page_size maximum size of the page returned.
- ///
- /// @return Lease collection (may be empty if no IPv6 lease found).
- virtual Lease6Collection
- getLeases6(const asiolink::IOAddress& /* lower_bound_address */,
- const LeasePageSize& /* page_size */) const override {
- return (Lease6Collection());
- };
-
- /// @brief Returns expired DHCPv6 leases.
- ///
- /// This method is not implemented.
- virtual void getExpiredLeases6(Lease6Collection&, const size_t) const override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases6 is not"
- " implemented");
- }
-
- /// @brief Returns expired DHCPv4 leases.
- ///
- /// This method is not implemented.
- virtual void getExpiredLeases4(Lease4Collection&, const size_t) const override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases4 is not"
- " implemented");
- }
-
- /// @brief Updates IPv4 lease.
- ///
- /// @param lease4 The lease to be updated.
- ///
- /// If no such lease is present, an exception will be thrown.
- virtual void updateLease4(const Lease4Ptr&) override {}
-
- /// @brief Updates IPv4 lease.
- ///
- /// @param lease4 The lease to be updated.
- ///
- /// If no such lease is present, an exception will be thrown.
- virtual void updateLease6(const Lease6Ptr&) override {}
-
- /// @brief Deletes an IPv4 lease.
- ///
- /// @param lease IPv4 lease to be deleted.
- ///
- /// @return true if deletion was successful, false if no such lease exists.
- virtual bool deleteLease(const Lease4Ptr&) override {
- return (false);
- }
-
- /// @brief Deletes an IPv6 lease.
- ///
- /// @param lease IPv6 lease to be deleted.
- ///
- /// @return true if deletion was successful, false if no such lease exists.
- virtual bool deleteLease(const Lease6Ptr&) override {
- return (false);
- }
-
- /// @brief Deletes all expired and reclaimed DHCPv4 leases.
- ///
- /// @param secs Number of seconds since expiration of leases before
- /// they can be removed. Leases which have expired later than this
- /// time will not be deleted.
- virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases4"
- " is not implemented");
- }
-
- /// @brief Deletes all expired and reclaimed DHCPv6 leases.
- ///
- /// @param secs Number of seconds since expiration of leases before
- /// they can be removed. Leases which have expired later than this
- /// time will not be deleted.
- virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases6"
- " is not implemented");
- }
-
- /// @brief Pretends to wipe all IPv4 leases from a subnet
- /// @param subnet_id (ignored, but one day may specify the subnet)
- virtual size_t wipeLeases4(const SubnetID&) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::wipeLeases4 not implemented");
- }
-
- /// @brief Pretends to wipe all IPv4 leases from a subnet
- /// @param subnet_id (ignored, but one day may specify the subnet)
- virtual size_t wipeLeases6(const SubnetID&) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::wipeLeases6 not implemented");
- }
-
- /// @brief Pretends to check if the IPv4 lease limits set in the given user
- /// context are exceeded.
- virtual std::string
- checkLimits4(isc::data::ConstElementPtr const& /* user_context */) const override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::checkLimits4() not implemented");
- }
-
- /// @brief Pretends to check if the IPv6 lease limits set in the given user
- /// context are exceeded.
- virtual std::string
- checkLimits6(isc::data::ConstElementPtr const& /* user_context */) const override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::checkLimits6() not implemented");
- }
-
- /// @brief Pretends to check if JSON support is enabled in the database.
- ///
- /// @return true if there is JSON support, false otherwise
- virtual bool isJsonSupported() const override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::isJsonSupported() not implemented");
- }
-
- /// @brief Pretends to return the class lease count for a given class and lease type.
- ///
- /// @param client_class client class for which the count is desired
- /// @param ltype type of lease for which the count is desired. Defaults to
- /// Lease::TYPE_V4.
- ///
- /// @return number of leases
- virtual size_t getClassLeaseCount(const ClientClass& /* client_class */,
- const Lease::Type& /* ltype = Lease::TYPE_V4 */) const override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getClassLeaseCount() not implemented");
- }
-
- /// @brief Pretends to recount the leases per class for V4 leases.
- virtual void recountClassLeases4() override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::recountClassLeases4() not implemented");
- }
-
- /// @brief Pretends to recount the leases per class for V6 leases.
- virtual void recountClassLeases6() override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::recountClassLeases6() not implemented");
- }
-
- /// @brief Pretends to clear the class-lease count map.
- virtual void clearClassLeaseCounts() override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::clearClassLeaseCounts() not implemented");
- }
-
- /// @brief Import addExtendedInfo6.
- using LeaseMgr::addExtendedInfo6;
-
- /// @brief Delete lease6 extended info from tables.
- ///
- /// @param addr The address of the lease.
- void
- deleteExtendedInfo6(const IOAddress& addr) override {
- auto relay_id_it = relay_id6_.begin();
- while (relay_id_it != relay_id6_.end()) {
- if ((*relay_id_it)->lease_addr_ == addr) {
- relay_id_it = relay_id6_.erase(relay_id_it);
- } else {
- ++relay_id_it;
- }
- }
- auto remote_id_it = remote_id6_.begin();
- while (remote_id_it != remote_id6_.end()) {
- if ((*remote_id_it)->lease_addr_ == addr) {
- remote_id_it = remote_id6_.erase(remote_id_it);
- } else {
- ++remote_id_it;
- }
- }
- }
-
- /// @brief Add lease6 extended info into by-relay-id table.
- ///
- /// @param lease_addr The address of the lease.
- /// @param relay_id The relay id from the relay header options.
- void
- addRelayId6(const IOAddress& lease_addr,
- const vector<uint8_t>& relay_id) override {
- Lease6ExtendedInfoPtr ex_info;
- ex_info.reset(new Lease6ExtendedInfo(lease_addr, relay_id));
- relay_id6_.push_back(ex_info);
- }
-
- /// @brief Add lease6 extended info into by-remote-id table.
- ///
- /// @param lease_addr The address of the lease.
- void
- addRemoteId6(const IOAddress& lease_addr,
- const vector<uint8_t>& remote_id) override {
- Lease6ExtendedInfoPtr ex_info;
- ex_info.reset(new Lease6ExtendedInfo(lease_addr, remote_id));
- remote_id6_.push_back(ex_info);
- }
-
- /// @brief Stub implementation.
- Lease4Collection
- getLeases4ByRelayId(const OptionBuffer& /* relay_id */,
- const IOAddress& /* lower_bound_address */,
- const LeasePageSize& /* page_size */,
- const time_t& /* qry_start_time = 0 */,
- const time_t& /* qry_end_time = 0 */) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases4ByRelayId not implemented");
- }
-
- /// @brief Stub implementation.
- Lease4Collection
- getLeases4ByRemoteId(const OptionBuffer& /* remote_id */,
- const IOAddress& /* lower_bound_address */,
- const LeasePageSize& /* page_size */,
- const time_t& /* qry_start_time = 0 */,
- const time_t& /* qry_end_time = 0 */) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases4ByRemoteId not implemented");
- }
-
- /// @brief Stub implementation.
- Lease6Collection
- getLeases6ByRelayId(const DUID& /* relay_id */,
- const IOAddress& /* link_addr */,
- uint8_t /* link_len */,
- const IOAddress& /* lower_bound_address */,
- const LeasePageSize& /* page_size */) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases6ByRelayId not implemented");
- }
-
- /// @brief Stub implementation.
- Lease6Collection
- getLeases6ByRemoteId(const OptionBuffer& /* remote_id */,
- const IOAddress& /* link_addr */,
- uint8_t /* link_len */,
- const IOAddress& /* lower_bound_address */,
- const LeasePageSize& /* page_size*/) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases6ByRemoteId not implemented");
- }
-
- /// @brief Stub implementation.
- Lease6Collection
- getLeases6ByLink(const IOAddress& /* link_addr */,
- uint8_t /* link_len */,
- const IOAddress& /* lower_bound_address */,
- const LeasePageSize& /* page_size */) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases6ByLink not implemented");
- }
-
- /// @brief Stub implementation.
- virtual size_t buildExtendedInfoTables6(bool /* update */,
- bool /* current */) override {
- isc_throw(isc::NotImplemented, "ConcreteLeaseMgr:buildExtendedInfoTables6 not implemented");
- }
-
- /// @brief Pretends to write V4 leases to a file.
- virtual void writeLeases4(const std::string&) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::writeLeases4() not implemented");
- }
-
- /// @brief Pretends to write V6 leases to a file.
- virtual void writeLeases6(const std::string&) override {
- isc_throw(NotImplemented, "ConcreteLeaseMgr::writeLeases6() not implemented");
- }
-
- /// @brief Returns backend type.
- ///
- /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
- ///
- /// @return Type of the backend.
- virtual std::string getType() const override {
- return (std::string("concrete"));
- }
-
- /// @brief Returns backend name.
- ///
- /// If the backend is a database, this is the name of the database or the
- /// file. Otherwise it is just the same as the type.
- ///
- /// @return Name of the backend.
- virtual std::string getName() const override {
- return (std::string("concrete"));
- }
-
- /// @brief Returns description of the backend.
- ///
- /// This description may be multiline text that describes the backend.
- ///
- /// @return Description of the backend.
- virtual std::string getDescription() const override {
- return (std::string("This is a dummy concrete backend implementation."));
- }
-
- /// @brief Returns backend version.
- virtual std::pair<uint32_t, uint32_t> getVersion() const override {
- return (make_pair(uint32_t(0), uint32_t(0)));
- }
-
- /// @brief Commit transactions
- virtual void commit() override {
- }
-
- /// @brief Rollback transactions
- virtual void rollback() override {
- }
-
- // We need to use it in ConcreteLeaseMgr
- using LeaseMgr::getLease6;
-
- Lease6Collection leases6_; ///< getLease6 methods return this as is
-
- // List supports easier erase.
- list<Lease6ExtendedInfoPtr> relay_id6_;
- list<Lease6ExtendedInfoPtr> remote_id6_;
-};
-
class LeaseMgrTest : public GenericLeaseMgrTest {
public:
LeaseMgrTest() {
--- /dev/null
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/testutils/concrete_lease_mgr.h>
+#include <dhcpsrv/tests/generic_lease_mgr_unittest.h>
+#include <exceptions/exceptions.h>
+#include <functional>
+#include <gtest/gtest.h>
+#include <vector>
+
+using namespace isc;
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::dhcp;
+using namespace isc::dhcp::test;
+using namespace std::placeholders;
+
+namespace {
+
+/// @brief Test fixture class for @c TrackingLeaseMgr.
+class TrackingLeaseMgrTest : public GenericLeaseMgrTest {
+public:
+
+ /// @brief A structure holding a single callback log entry.
+ ///
+ /// The @c logCallback function inserts logs of this type into the
+ /// @c logs vector. The vector can be later examined to see what
+ /// callbacks have been invoked.
+ typedef struct {
+ TrackingLeaseMgr::CallbackType type;
+ SubnetID subnet_id;
+ LeasePtr lease;
+ } Log;
+
+ /// @brief Reopen the database
+ ///
+ /// No-op implementation. We need to provide concrete implementation,
+ /// as this is a pure virtual method in GenericLeaseMgrTest.
+ virtual void reopen(Universe) {}
+
+ /// @brief Convenience function creating a lease instance.
+ ///
+ /// @param subnet_id subnet identifier for the lease.
+ /// @param address leased address.
+ /// @tparam LeaseType type of the lease: @c Lease4 or Lease6.
+ template<typename LeaseType>
+ boost::shared_ptr<LeaseType> createLease(int subnet_id, std::string address) const {
+ auto lease = boost::make_shared<LeaseType>();
+ lease->subnet_id_ = SubnetID(subnet_id);
+ lease->addr_ = IOAddress(address);
+ return (lease);
+ }
+
+ /// @brief Callback function recording its parameters.
+ ///
+ /// It is used in the unit tests that verify that appropriate callbacks
+ /// have been invoked.
+ ///
+ /// @param type callback type.
+ /// @param subnet_id subnet identifier.
+ /// @param lease lease instance.
+ void logCallback(const TrackingLeaseMgr::CallbackType type, SubnetID subnet_id, const LeasePtr& lease) {
+ logs_.push_back(Log{type, subnet_id, lease});
+ }
+
+ /// @brief Counts log entries.
+ ///
+ /// It counts the logs associated with the specific callback type and subnet id.
+ ///
+ /// @param type callback type.
+ /// @param subnet_id subnet identifier.
+ /// @return The number of callback logs associated with the specific type and
+ /// the subnet id.
+ int countLogs(TrackingLeaseMgr::CallbackType type, SubnetID subnet_id) const {
+ int count = 0;
+ for (auto log : logs_) {
+ if ((log.type == type) && (log.subnet_id == subnet_id)) {
+ ++count;
+ }
+ }
+ return (count);
+ }
+
+ /// Recorded callback logs.
+ std::vector<Log> logs_;
+};
+
+/// Tests that leases can be locked and unlocked. When a lease is locked
+/// an attempt to lock it again fails.
+TEST_F(TrackingLeaseMgrTest, tryLock) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+
+ // An attempt to lock an already locked lease should fail.
+ EXPECT_TRUE(mgr.tryLock(createLease<Lease4>(1, "192.0.2.1")));
+ EXPECT_FALSE(mgr.tryLock(createLease<Lease4>(1, "192.0.2.1")));
+
+ // We can lock another lease but we cannot lock an already locked one.
+ EXPECT_TRUE(mgr.tryLock(createLease<Lease4>(1, "192.0.2.2")));
+ EXPECT_FALSE(mgr.tryLock(createLease<Lease4>(1, "192.0.2.1")));
+ EXPECT_FALSE(mgr.tryLock(createLease<Lease4>(2, "192.0.2.2")));
+
+ // If we unlock the lease, it can be locked again. However, unlocking
+ // the lease should not affect other locks.
+ mgr.unlock(createLease<Lease4>(1, "192.0.2.1"));
+ EXPECT_FALSE(mgr.tryLock(createLease<Lease4>(2, "192.0.2.2")));
+ EXPECT_TRUE(mgr.tryLock(createLease<Lease4>(1, "192.0.2.1")));
+}
+
+/// Tests registering the callbacks.
+TEST_F(TrackingLeaseMgrTest, registerCallbacks) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+
+ // Callback for lease add and subnet id 0.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+ // Callback for lease add and subnet id 1.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 1, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 1,
+ _1)));
+ // Callback for lease add and subnet id 2.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 2, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 1,
+ _1)));
+ // Callback for lease update and subnet id 0.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_UPDATE_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_UPDATE_LEASE,
+ 0,
+ _1)));
+ // Callback for lease delete and subnet id 0.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_DELETE_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_DELETE_LEASE,
+ 1,
+ _1)));
+
+ // This call should trigger the lease add callbacks for subnet id 0 and 1.
+ EXPECT_NO_THROW(mgr.trackAddLease(createLease<Lease4>(1, "192.0.2.1"), false));
+ EXPECT_EQ(2, logs_.size());
+ EXPECT_EQ(1, countLogs(TrackingLeaseMgr::TRACK_ADD_LEASE, 0));
+ EXPECT_EQ(1, countLogs(TrackingLeaseMgr::TRACK_ADD_LEASE, 1));
+
+ // This call should trigger the lease add callback for subnet id 0 only. That's
+ // because we have no callback for the subnet id 3.
+ EXPECT_NO_THROW(mgr.trackAddLease(createLease<Lease4>(3, "192.0.2.1"), false));
+ EXPECT_EQ(3, logs_.size());
+ EXPECT_EQ(2, countLogs(TrackingLeaseMgr::TRACK_ADD_LEASE, 0));
+}
+
+/// Test that registering the callbacks of the same type, for the same subnet id by the
+/// same owner fails.
+TEST_F(TrackingLeaseMgrTest, registerCallbacksConflicts) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+
+ // Add the callback for lease add and subnet id 0.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+
+ // Another attempt should fail.
+ EXPECT_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)),
+ InvalidOperation);
+
+ // It should succeed for a different owner.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "qlf",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+
+ // It should also succeed for a different subnet id.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 5, "qlf",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 5,
+ _1)));
+
+ // But, another attempt for the subnet id should fail.
+ EXPECT_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 5, "qlf",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 5,
+ _1)),
+ InvalidOperation);
+}
+
+/// Test invoking the registered lease update callbacks.
+TEST_F(TrackingLeaseMgrTest, trackUpdateLease) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_UPDATE_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_UPDATE_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_DELETE_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_DELETE_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.trackUpdateLease(createLease<Lease4>(1, "192.0.2.1"), false));
+ EXPECT_EQ(1, logs_.size());
+ EXPECT_EQ(1, countLogs(TrackingLeaseMgr::TRACK_UPDATE_LEASE, 0));
+}
+
+/// Test invoking the registered lease delete callbacks.
+TEST_F(TrackingLeaseMgrTest, trackDeleteLease) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_UPDATE_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_UPDATE_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_DELETE_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_DELETE_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.trackDeleteLease(createLease<Lease4>(1, "192.0.2.1"), false));
+ EXPECT_EQ(1, logs_.size());
+ EXPECT_EQ(1, countLogs(TrackingLeaseMgr::TRACK_DELETE_LEASE, 0));
+}
+
+// Test unregistering the callbacks by subnet id.
+TEST_F(TrackingLeaseMgrTest, unregisterCallbacksBySubnetID) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+
+ // Register different callback types for different subnet identifiers.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 1, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 1,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 2, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 2,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_UPDATE_LEASE, 1, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_UPDATE_LEASE,
+ 1,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_UPDATE_LEASE, 2, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_UPDATE_LEASE,
+ 2,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_DELETE_LEASE, 1, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_DELETE_LEASE,
+ 1,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_DELETE_LEASE, 2, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_DELETE_LEASE,
+ 2,
+ _1)));
+
+ // Unregister the callbacks for subnet id 1.
+ EXPECT_NO_THROW(mgr.unregisterCallbacks(SubnetID(1)));
+
+ // Invoke the remaining callbacksm for the subnet id 1.
+ EXPECT_NO_THROW(mgr.trackAddLease(createLease<Lease4>(1, "192.0.2.1"), false));
+ EXPECT_NO_THROW(mgr.trackUpdateLease(createLease<Lease4>(1, "192.0.2.1"), false));
+ EXPECT_NO_THROW(mgr.trackDeleteLease(createLease<Lease4>(1, "192.0.2.1"), false));
+
+ // It should only run the callback for the subnet id 0 that is still
+ // registered.
+ EXPECT_EQ(1, logs_.size());
+ EXPECT_EQ(1, countLogs(TrackingLeaseMgr::TRACK_ADD_LEASE, 0));
+
+ // Unregister this callback.
+ EXPECT_NO_THROW(mgr.unregisterCallbacks(SubnetID(0)));
+
+ // Make sure it is no longer invoked.
+ EXPECT_NO_THROW(mgr.trackAddLease(createLease<Lease4>(1, "192.0.2.1"), false));
+ EXPECT_EQ(1, logs_.size());
+
+ // Unregistering it again should be no-op.
+ EXPECT_NO_THROW(mgr.unregisterCallbacks(SubnetID(0)));
+}
+
+/// Test unregistering all callbacks.
+TEST_F(TrackingLeaseMgrTest, unregisterAllCallbacks) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+
+ // Register some callbacks.
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_UPDATE_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_UPDATE_LEASE,
+ 0,
+ _1)));
+ // Make sure they have been registered.
+ EXPECT_TRUE(mgr.hasCallbacks());
+
+ // Unregister them and make sure it was successful.
+ EXPECT_NO_THROW(mgr.unregisterAllCallbacks());
+ EXPECT_FALSE(mgr.hasCallbacks());
+}
+
+/// Test the function checking if any callbacks have been registered.
+TEST_F(TrackingLeaseMgrTest, hasCallbacks) {
+ DatabaseConnection::ParameterMap pmap;
+ ConcreteLeaseMgr mgr(pmap);
+ EXPECT_FALSE(mgr.hasCallbacks());
+
+ EXPECT_NO_THROW(mgr.registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&TrackingLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ _1)));
+ EXPECT_TRUE(mgr.hasCallbacks());
+}
+
+/// Test conversion of the callback type to string.
+TEST_F(TrackingLeaseMgrTest, callbackTypeToString) {
+ EXPECT_EQ("add_lease", ConcreteLeaseMgr::callbackTypeToString(TrackingLeaseMgr::TRACK_ADD_LEASE));
+ EXPECT_EQ("update_lease", ConcreteLeaseMgr::callbackTypeToString(TrackingLeaseMgr::TRACK_UPDATE_LEASE));
+ EXPECT_EQ("delete_lease", ConcreteLeaseMgr::callbackTypeToString(TrackingLeaseMgr::TRACK_DELETE_LEASE));
+}
+
+} // end of anonymous namespace
noinst_LTLIBRARIES = libdhcpsrvtest.la
-libdhcpsrvtest_la_SOURCES = config_result_check.cc config_result_check.h
+libdhcpsrvtest_la_SOURCES = concrete_lease_mgr.cc concrete_lease_mgr.h
+libdhcpsrvtest_la_SOURCES += config_result_check.cc config_result_check.h
libdhcpsrvtest_la_SOURCES += dhcp4o6_test_ipc.cc dhcp4o6_test_ipc.h
libdhcpsrvtest_la_SOURCES += host_data_source_utils.cc host_data_source_utils.h
libdhcpsrvtest_la_SOURCES += memory_host_data_source.cc memory_host_data_source.h
--- /dev/null
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <asiolink/io_address.h>
+#include <concrete_lease_mgr.h>
+
+using namespace isc::asiolink;
+using namespace isc::db;
+using namespace isc::dhcp;
+using namespace std;
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+ConcreteLeaseMgr::ConcreteLeaseMgr(const DatabaseConnection::ParameterMap&)
+ : TrackingLeaseMgr() {
+}
+
+ConcreteLeaseMgr::~ConcreteLeaseMgr() {
+}
+
+bool
+ConcreteLeaseMgr::addLease(const Lease4Ptr&) {
+ return (false);
+}
+
+bool
+ConcreteLeaseMgr::addLease(const Lease6Ptr&) {
+ return (false);
+}
+
+Lease4Ptr
+ConcreteLeaseMgr::getLease4(const IOAddress&) const {
+ return (Lease4Ptr());
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLease4(const HWAddr&) const {
+ return (Lease4Collection());
+}
+
+Lease4Ptr
+ConcreteLeaseMgr::getLease4(const HWAddr&, SubnetID) const {
+ return (Lease4Ptr());
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLease4(const ClientId&) const {
+ return (Lease4Collection());
+}
+
+Lease4Ptr
+ConcreteLeaseMgr::getLease4(const ClientId&, SubnetID) const {
+ return (Lease4Ptr());
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLeases4(SubnetID) const {
+ return (Lease4Collection());
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLeases4(const std::string&) const {
+ return (Lease4Collection());
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLeases4() const {
+ return (Lease4Collection());
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLeases4(const IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) const {
+ return (Lease4Collection());
+}
+
+Lease6Ptr
+ConcreteLeaseMgr::getLease6(Lease::Type /* not used yet */,
+ const IOAddress&) const {
+ return (Lease6Ptr());
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6(Lease::Type /* not used yet */,
+ const DUID&, uint32_t) const {
+ return (leases6_);
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6(Lease::Type /* not used yet */,
+ const DUID&, uint32_t, SubnetID) const {
+ return (leases6_);
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6(const DUID&) const {
+ return (leases6_);
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6(SubnetID) const {
+ return (Lease6Collection());
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6(const std::string&) const {
+ return (Lease6Collection());
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6() const {
+ return (Lease6Collection());
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6(const IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) const {
+ return (Lease6Collection());
+};
+
+void
+ConcreteLeaseMgr::getExpiredLeases6(Lease6Collection&, const size_t) const {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases6 is not"
+ " implemented");
+}
+
+void
+ConcreteLeaseMgr::getExpiredLeases4(Lease4Collection&, const size_t) const {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getExpiredLeases4 is not"
+ " implemented");
+}
+
+void
+ConcreteLeaseMgr::updateLease4(const Lease4Ptr&) {}
+
+void
+ConcreteLeaseMgr::updateLease6(const Lease6Ptr&) {}
+
+bool
+ConcreteLeaseMgr::deleteLease(const Lease4Ptr&) {
+ return (false);
+}
+
+bool
+ConcreteLeaseMgr::deleteLease(const Lease6Ptr&) {
+ return (false);
+}
+
+uint64_t
+ConcreteLeaseMgr::deleteExpiredReclaimedLeases4(const uint32_t) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpir§edReclaimedLeases4"
+ " is not implemented");
+}
+
+uint64_t
+ConcreteLeaseMgr::deleteExpiredReclaimedLeases6(const uint32_t) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::deleteExpiredReclaimedLeases6"
+ " is not implemented");
+}
+
+size_t
+ConcreteLeaseMgr::wipeLeases4(const SubnetID&) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::wipeLeases4 not implemented");
+}
+
+size_t
+ConcreteLeaseMgr::wipeLeases6(const SubnetID&) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::wipeLeases6 not implemented");
+}
+
+std::string
+ConcreteLeaseMgr::checkLimits4(isc::data::ConstElementPtr const& /* user_context */) const {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::checkLimits4() not implemented");
+}
+
+std::string
+ConcreteLeaseMgr::checkLimits6(isc::data::ConstElementPtr const& /* user_context */) const {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::checkLimits6() not implemented");
+}
+
+bool
+ConcreteLeaseMgr::isJsonSupported() const {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::isJsonSupported() not implemented");
+}
+
+size_t
+ConcreteLeaseMgr::getClassLeaseCount(const ClientClass& /* client_class */,
+ const Lease::Type& /* ltype = Lease::TYPE_V4 */) const {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getClassLeaseCount() not implemented");
+}
+
+void
+ConcreteLeaseMgr::recountClassLeases4() {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::recountClassLeases4() not implemented");
+}
+
+void
+ConcreteLeaseMgr::recountClassLeases6() {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::recountClassLeases6() not implemented");
+}
+
+void
+ConcreteLeaseMgr::clearClassLeaseCounts() {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::clearClassLeaseCounts() not implemented");
+}
+
+void
+ConcreteLeaseMgr::deleteExtendedInfo6(const IOAddress& addr) {
+ auto relay_id_it = relay_id6_.begin();
+ while (relay_id_it != relay_id6_.end()) {
+ if ((*relay_id_it)->lease_addr_ == addr) {
+ relay_id_it = relay_id6_.erase(relay_id_it);
+ } else {
+ ++relay_id_it;
+ }
+ }
+ auto remote_id_it = remote_id6_.begin();
+ while (remote_id_it != remote_id6_.end()) {
+ if ((*remote_id_it)->lease_addr_ == addr) {
+ remote_id_it = remote_id6_.erase(remote_id_it);
+ } else {
+ ++remote_id_it;
+ }
+ }
+}
+
+void
+ConcreteLeaseMgr::addRelayId6(const IOAddress& lease_addr,
+ const vector<uint8_t>& relay_id) {
+ Lease6ExtendedInfoPtr ex_info;
+ ex_info.reset(new Lease6ExtendedInfo(lease_addr, relay_id));
+ relay_id6_.push_back(ex_info);
+}
+
+void
+ConcreteLeaseMgr::addRemoteId6(const IOAddress& lease_addr,
+ const vector<uint8_t>& remote_id) {
+ Lease6ExtendedInfoPtr ex_info;
+ ex_info.reset(new Lease6ExtendedInfo(lease_addr, remote_id));
+ remote_id6_.push_back(ex_info);
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLeases4ByRelayId(const OptionBuffer& /* relay_id */,
+ const IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */,
+ const time_t& /* qry_start_time = 0 */,
+ const time_t& /* qry_end_time = 0 */) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases4ByRelayId not implemented");
+}
+
+Lease4Collection
+ConcreteLeaseMgr::getLeases4ByRemoteId(const OptionBuffer& /* remote_id */,
+ const IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */,
+ const time_t& /* qry_start_time = 0 */,
+ const time_t& /* qry_end_time = 0 */) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases4ByRemoteId not implemented");
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6ByRelayId(const DUID& /* relay_id */,
+ const IOAddress& /* link_addr */,
+ uint8_t /* link_len */,
+ const IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases6ByRelayId not implemented");
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6ByRemoteId(const OptionBuffer& /* remote_id */,
+ const IOAddress& /* link_addr */,
+ uint8_t /* link_len */,
+ const IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size*/) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases6ByRemoteId not implemented");
+}
+
+Lease6Collection
+ConcreteLeaseMgr::getLeases6ByLink(const IOAddress& /* link_addr */,
+ uint8_t /* link_len */,
+ const IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::getLeases6ByLink not implemented");
+}
+
+size_t
+ConcreteLeaseMgr::buildExtendedInfoTables6(bool /* update */,
+ bool /* current */) {
+ isc_throw(isc::NotImplemented, "ConcreteLeaseMgr:buildExtendedInfoTables6 not implemented");
+}
+
+void
+ConcreteLeaseMgr::writeLeases4(const std::string&) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::writeLeases4() not implemented");
+}
+
+void
+ConcreteLeaseMgr::writeLeases6(const std::string&) {
+ isc_throw(NotImplemented, "ConcreteLeaseMgr::writeLeases6() not implemented");
+}
+
+std::string
+ConcreteLeaseMgr::getType() const {
+ return (std::string("concrete"));
+}
+
+std::string
+ConcreteLeaseMgr::getName() const {
+ return (std::string("concrete"));
+}
+
+std::string
+ConcreteLeaseMgr::getDescription() const {
+ return (std::string("This is a dummy concrete backend implementation."));
+}
+
+std::pair<uint32_t, uint32_t>
+ConcreteLeaseMgr::getVersion() const {
+ return (make_pair(uint32_t(0), uint32_t(0)));
+}
+
+void
+ConcreteLeaseMgr::commit() {
+}
+
+void
+ConcreteLeaseMgr::rollback() {
+}
+
+} // end of namespace isc::dhcp::test
+} // end of namespace isc::dhcp
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TEST_CONCRETE_LEASE_MGR_H
+#define TEST_CONCRETE_LEASE_MGR_H
+
+#include <config.h>
+
+#include <database/database_connection.h>
+#include <dhcpsrv/memfile_lease_storage.h>
+#include <dhcpsrv/tracking_lease_mgr.h>
+#include <list>
+#include <utility>
+
+namespace isc {
+namespace dhcp {
+namespace test {
+
+// This is a concrete implementation of a Lease database. It does not do
+// anything useful and is used for abstract LeaseMgr class testing.
+class ConcreteLeaseMgr : public TrackingLeaseMgr {
+public:
+
+ /// @brief The sole lease manager constructor
+ ///
+ /// dbconfig is a generic way of passing parameters. Parameters
+ /// are passed in the "name=value" format, separated by spaces.
+ /// Values may be enclosed in double quotes, if needed.
+ ConcreteLeaseMgr(const db::DatabaseConnection::ParameterMap&);
+
+ /// @brief Destructor
+ virtual ~ConcreteLeaseMgr();
+
+ /// @brief Adds an IPv4 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(const Lease4Ptr&) override;
+
+ /// @brief Adds an IPv6 lease.
+ ///
+ /// @param lease lease to be added
+ virtual bool addLease(const Lease6Ptr&) override;
+
+ /// @brief Returns existing IPv4 lease for specified IPv4 address.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const isc::asiolink::IOAddress&) const override;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address.
+ ///
+ /// Although in the usual case there will be only one lease, for mobile
+ /// clients or clients with multiple static/fixed/reserved leases there
+ /// can be more than one. Thus return type is a container, not a single
+ /// pointer.
+ ///
+ /// @param hwaddr hardware address of the client
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const HWAddr&) const override;
+
+ /// @brief Returns existing IPv4 leases for specified hardware address
+ /// and a subnet
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param hwaddr hardware address of the client
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const HWAddr&, SubnetID) const override;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// @param clientid client identifier
+ ///
+ /// @return lease collection
+ virtual Lease4Collection getLease4(const ClientId&) const override;
+
+ /// @brief Returns existing IPv4 lease for specified client-id
+ ///
+ /// There can be at most one lease for a given HW address in a single
+ /// pool, so this method with either return a single lease or NULL.
+ ///
+ /// @param clientid client identifier
+ /// @param subnet_id identifier of the subnet that lease must belong to
+ ///
+ /// @return a pointer to the lease (or NULL if a lease is not found)
+ virtual Lease4Ptr getLease4(const ClientId&, SubnetID) const override;
+
+ /// @brief Returns all IPv4 leases for the particular subnet identifier.
+ ///
+ /// @param subnet_id subnet identifier.
+ ///
+ /// @return Lease collection (may be empty if no IPv4 lease found).
+ virtual Lease4Collection getLeases4(SubnetID) const override;
+
+ /// @brief Returns all IPv4 leases for the particular hostname.
+ ///
+ /// @param hostname hostname in lower case.
+ ///
+ /// @return Lease collection (may be empty if no IPv4 lease found).
+ virtual Lease4Collection getLeases4(const std::string&) const override;
+
+ /// @brief Returns all IPv4 leases.
+ ///
+ /// @return Lease collection (may be empty if no IPv4 lease found).
+ virtual Lease4Collection getLeases4() const override;
+
+ /// @brief Returns range of IPv4 leases using paging.
+ ///
+ /// This method implements paged browsing of the lease database. The first
+ /// parameter specifies a page size. The second parameter is optional and
+ /// specifies the starting address of the range. This address is excluded
+ /// from the returned range. The IPv4 zero address (default) denotes that
+ /// the first page should be returned. There is no guarantee about the
+ /// order of returned leases.
+ ///
+ /// The typical usage of this method is as follows:
+ /// - Get the first page of leases by specifying IPv4 zero address as the
+ /// beginning of the range.
+ /// - Last address of the returned range should be used as a starting
+ /// address for the next page in the subsequent call.
+ /// - If the number of leases returned is lower than the page size, it
+ /// indicates that the last page has been retrieved.
+ /// - If there are no leases returned it indicates that the previous page
+ /// was the last page.
+ ///
+ /// @param lower_bound_address IPv4 address used as lower bound for the
+ /// returned range.
+ /// @param page_size maximum size of the page returned.
+ ///
+ /// @return Lease collection (may be empty if no IPv4 lease found).
+ virtual Lease4Collection
+ getLeases4(const asiolink::IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) const override;
+
+ /// @brief Returns existing IPv6 lease for a given IPv6 address.
+ ///
+ /// @param addr address of the searched lease
+ ///
+ /// @return smart pointer to the lease (or NULL if a lease is not found)
+ virtual Lease6Ptr getLease6(Lease::Type /* not used yet */,
+ const isc::asiolink::IOAddress&) const override;
+
+ /// @brief Returns existing IPv6 lease for a given DUID+IA combination
+ ///
+ /// @param duid ignored
+ /// @param iaid ignored
+ ///
+ /// @return whatever is set in leases6_ field
+ virtual Lease6Collection getLeases6(Lease::Type /* not used yet */,
+ const DUID&, uint32_t) const override;
+
+ /// @brief Returns existing IPv6 lease for a given DUID+IA+subnet-id combination
+ ///
+ /// @param duid ignored
+ /// @param iaid ignored
+ /// @param subnet_id ignored
+ ///
+ /// @return whatever is set in leases6_ field
+ virtual Lease6Collection getLeases6(Lease::Type /* not used yet */,
+ const DUID&, uint32_t, SubnetID) const override;
+
+ /// @brief Returns collection of lease for matching DUID
+ ///
+ /// @param duid ignored
+ /// @return whatever is set in leases6_ field
+ virtual Lease6Collection getLeases6(const DUID&) const override;
+
+ /// @brief Returns all IPv6 leases for the particular subnet identifier.
+ ///
+ /// @param subnet_id subnet identifier.
+ ///
+ /// @return Lease collection (may be empty if no IPv6 lease found).
+ virtual Lease6Collection getLeases6(SubnetID) const override;
+
+ /// @brief Returns all IPv6 leases for the particular hostname.
+ ///
+ /// @param hostname hostname in lower case.
+ ///
+ /// @return Lease collection (may be empty if no IPv6 lease found).
+ virtual Lease6Collection getLeases6(const std::string&) const override;
+
+ /// @brief Returns all IPv6 leases.
+ ///
+ /// @return Lease collection (may be empty if no IPv6 lease found).
+ virtual Lease6Collection getLeases6() const override;
+
+ /// @brief Returns range of IPv6 leases using paging.
+ ///
+ /// This method implements paged browsing of the lease database. The first
+ /// parameter specifies a page size. The second parameter is optional and
+ /// specifies the starting address of the range. This address is excluded
+ /// from the returned range. The IPv6 zero address (default) denotes that
+ /// the first page should be returned. There is no guarantee about the
+ /// order of returned leases.
+ ///
+ /// The typical usage of this method is as follows:
+ /// - Get the first page of leases by specifying IPv6 zero address as the
+ /// beginning of the range.
+ /// - Last address of the returned range should be used as a starting
+ /// address for the next page in the subsequent call.
+ /// - If the number of leases returned is lower than the page size, it
+ /// indicates that the last page has been retrieved.
+ /// - If there are no leases returned it indicates that the previous page
+ /// was the last page.
+ ///
+ /// @param lower_bound_address IPv4 address used as lower bound for the
+ /// returned range.
+ /// @param page_size maximum size of the page returned.
+ ///
+ /// @return Lease collection (may be empty if no IPv6 lease found).
+ virtual Lease6Collection
+ getLeases6(const asiolink::IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) const override;
+
+ /// @brief Returns expired DHCPv6 leases.
+ ///
+ /// This method is not implemented.
+ virtual void getExpiredLeases6(Lease6Collection&, const size_t) const override;
+
+ /// @brief Returns expired DHCPv4 leases.
+ ///
+ /// This method is not implemented.
+ virtual void getExpiredLeases4(Lease4Collection&, const size_t) const override;
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ virtual void updateLease4(const Lease4Ptr&) override;
+
+ /// @brief Updates IPv4 lease.
+ ///
+ /// @param lease4 The lease to be updated.
+ ///
+ /// If no such lease is present, an exception will be thrown.
+ virtual void updateLease6(const Lease6Ptr&) override;
+
+ /// @brief Deletes an IPv4 lease.
+ ///
+ /// @param lease IPv4 lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists.
+ virtual bool deleteLease(const Lease4Ptr&) override;
+
+ /// @brief Deletes an IPv6 lease.
+ ///
+ /// @param lease IPv6 lease to be deleted.
+ ///
+ /// @return true if deletion was successful, false if no such lease exists.
+ virtual bool deleteLease(const Lease6Ptr&) override;
+
+ /// @brief Deletes all expired and reclaimed DHCPv4 leases.
+ ///
+ /// @param secs Number of seconds since expiration of leases before
+ /// they can be removed. Leases which have expired later than this
+ /// time will not be deleted.
+ virtual uint64_t deleteExpiredReclaimedLeases4(const uint32_t) override;
+
+ /// @brief Deletes all expired and reclaimed DHCPv6 leases.
+ ///
+ /// @param secs Number of seconds since expiration of leases before
+ /// they can be removed. Leases which have expired later than this
+ /// time will not be deleted.
+ virtual uint64_t deleteExpiredReclaimedLeases6(const uint32_t) override;
+
+ /// @brief Pretends to wipe all IPv4 leases from a subnet
+ /// @param subnet_id (ignored, but one day may specify the subnet)
+ virtual size_t wipeLeases4(const SubnetID&) override;
+
+ /// @brief Pretends to wipe all IPv4 leases from a subnet
+ /// @param subnet_id (ignored, but one day may specify the subnet)
+ virtual size_t wipeLeases6(const SubnetID&) override;
+
+ /// @brief Pretends to check if the IPv4 lease limits set in the given user
+ /// context are exceeded.
+ virtual std::string
+ checkLimits4(isc::data::ConstElementPtr const& /* user_context */) const override;
+
+ /// @brief Pretends to check if the IPv6 lease limits set in the given user
+ /// context are exceeded.
+ virtual std::string
+ checkLimits6(isc::data::ConstElementPtr const& /* user_context */) const override;
+
+ /// @brief Pretends to check if JSON support is enabled in the database.
+ ///
+ /// @return true if there is JSON support, false otherwise
+ virtual bool isJsonSupported() const override;
+
+ /// @brief Pretends to return the class lease count for a given class and lease type.
+ ///
+ /// @param client_class client class for which the count is desired
+ /// @param ltype type of lease for which the count is desired. Defaults to
+ /// Lease::TYPE_V4.
+ ///
+ /// @return number of leases
+ virtual size_t getClassLeaseCount(const ClientClass& /* client_class */,
+ const Lease::Type& /* ltype = Lease::TYPE_V4 */) const override;
+
+ /// @brief Pretends to recount the leases per class for V4 leases.
+ virtual void recountClassLeases4() override;
+
+ /// @brief Pretends to recount the leases per class for V6 leases.
+ virtual void recountClassLeases6() override;
+
+ /// @brief Pretends to clear the class-lease count map.
+ virtual void clearClassLeaseCounts() override;
+
+ /// @brief Import addExtendedInfo6.
+ using LeaseMgr::addExtendedInfo6;
+
+ /// @brief Delete lease6 extended info from tables.
+ ///
+ /// @param addr The address of the lease.
+ void
+ deleteExtendedInfo6(const asiolink::IOAddress& addr) override;
+
+ /// @brief Add lease6 extended info into by-relay-id table.
+ ///
+ /// @param lease_addr The address of the lease.
+ /// @param relay_id The relay id from the relay header options.
+ void
+ addRelayId6(const asiolink::IOAddress& lease_addr,
+ const std::vector<uint8_t>& relay_id) override;
+
+ /// @brief Add lease6 extended info into by-remote-id table.
+ ///
+ /// @param lease_addr The address of the lease.
+ void
+ addRemoteId6(const asiolink::IOAddress& lease_addr,
+ const std::vector<uint8_t>& remote_id) override;
+
+ /// @brief Stub implementation.
+ Lease4Collection
+ getLeases4ByRelayId(const OptionBuffer& /* relay_id */,
+ const asiolink::IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */,
+ const time_t& /* qry_start_time = 0 */,
+ const time_t& /* qry_end_time = 0 */) override;
+
+ /// @brief Stub implementation.
+ Lease4Collection
+ getLeases4ByRemoteId(const OptionBuffer& /* remote_id */,
+ const asiolink::IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */,
+ const time_t& /* qry_start_time = 0 */,
+ const time_t& /* qry_end_time = 0 */) override;
+
+ /// @brief Stub implementation.
+ Lease6Collection
+ getLeases6ByRelayId(const DUID& /* relay_id */,
+ const asiolink::IOAddress& /* link_addr */,
+ uint8_t /* link_len */,
+ const asiolink::IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) override;
+
+ /// @brief Stub implementation.
+ Lease6Collection
+ getLeases6ByRemoteId(const OptionBuffer& /* remote_id */,
+ const asiolink::IOAddress& /* link_addr */,
+ uint8_t /* link_len */,
+ const asiolink::IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size*/) override;
+
+ /// @brief Stub implementation.
+ Lease6Collection
+ getLeases6ByLink(const asiolink::IOAddress& /* link_addr */,
+ uint8_t /* link_len */,
+ const asiolink::IOAddress& /* lower_bound_address */,
+ const LeasePageSize& /* page_size */) override;
+
+ /// @brief Stub implementation.
+ virtual size_t buildExtendedInfoTables6(bool /* update */,
+ bool /* current */) override;
+
+ /// @brief Pretends to write V4 leases to a file.
+ virtual void writeLeases4(const std::string&) override;
+
+ /// @brief Pretends to write V6 leases to a file.
+ virtual void writeLeases6(const std::string&) override;
+
+ /// @brief Returns backend type.
+ ///
+ /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
+ ///
+ /// @return Type of the backend.
+ virtual std::string getType() const override;
+
+ /// @brief Returns backend name.
+ ///
+ /// If the backend is a database, this is the name of the database or the
+ /// file. Otherwise it is just the same as the type.
+ ///
+ /// @return Name of the backend.
+ virtual std::string getName() const override;
+
+ /// @brief Returns description of the backend.
+ ///
+ /// This description may be multiline text that describes the backend.
+ ///
+ /// @return Description of the backend.
+ virtual std::string getDescription() const override;
+
+ /// @brief Returns backend version.
+ virtual std::pair<uint32_t, uint32_t> getVersion() const override;
+
+ /// @brief Commit transactions
+ virtual void commit() override;
+
+ /// @brief Rollback transactions
+ virtual void rollback() override;
+
+ // We need to use them in ConcreteLeaseMgr
+ using LeaseMgr::getLease6;
+ using TrackingLeaseMgr::tryLock;
+ using TrackingLeaseMgr::unlock;
+ using TrackingLeaseMgr::trackAddLease;
+ using TrackingLeaseMgr::trackUpdateLease;
+ using TrackingLeaseMgr::trackDeleteLease;
+ using TrackingLeaseMgr::hasCallbacks;
+ using TrackingLeaseMgr::callbackTypeToString;
+
+ Lease6Collection leases6_; ///< getLease6 methods return this as is
+
+ // List supports easier erase.
+ std::list<Lease6ExtendedInfoPtr> relay_id6_;
+ std::list<Lease6ExtendedInfoPtr> remote_id6_;
+};
+
+
+} // end of namespace isc::dhcp::test
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+#endif // TEST_CONCRETE_LEASE_MGR_H
--- /dev/null
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <exceptions/exceptions.h>
+#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/tracking_lease_mgr.h>
+#include <util/multi_threading_mgr.h>
+#include <boost/tuple/tuple.hpp>
+
+using namespace isc::asiolink;
+using namespace isc::util;
+
+namespace isc {
+namespace dhcp {
+
+TrackingLeaseMgr::TrackingLeaseMgr()
+ : LeaseMgr() {
+}
+
+bool
+TrackingLeaseMgr::tryLock(const LeasePtr& lease) {
+ // Try inserting a lease. If such a lease already exists in the set, return false.
+ auto result = locked_leases_.insert(lease->addr_);
+ return (result.second);
+}
+
+void
+TrackingLeaseMgr::unlock(const LeasePtr& lease) {
+ // Remove the locked lease from the set.
+ locked_leases_.erase(lease->addr_);
+}
+
+void
+TrackingLeaseMgr::trackAddLease(const LeasePtr& lease, bool mt_safe) {
+ runCallbacks(TRACK_ADD_LEASE, lease, mt_safe);
+}
+
+void
+TrackingLeaseMgr::trackUpdateLease(const LeasePtr& lease, bool mt_safe) {
+ runCallbacks(TRACK_UPDATE_LEASE, lease, mt_safe);
+}
+
+void
+TrackingLeaseMgr::trackDeleteLease(const LeasePtr& lease, bool mt_safe) {
+ runCallbacks(TRACK_DELETE_LEASE, lease, mt_safe);
+}
+
+void
+TrackingLeaseMgr::registerCallback(TrackingLeaseMgr::CallbackType type,
+ SubnetID subnet_id,
+ std::string owner,
+ TrackingLeaseMgr::CallbackFn callback_fn) {
+ // The first index filters the callbacks by type and subnet_id.
+ auto& idx = callbacks_.get<0>();
+ auto range = idx.equal_range(boost::make_tuple(type, subnet_id));
+ if (range.first != range.second) {
+ // Make sure that the callback for this owner does not exist.
+ if (std::find_if(range.first, range.second,
+ [&owner] (const Callback& cb) -> bool {
+ return (cb.owner == owner);
+ }) != range.second) {
+ isc_throw(InvalidOperation, "the callback owned by the " << owner
+ << ", for subnet ID " << subnet_id
+ << " has already been registered in the lease manager");
+ }
+ }
+ TrackingLeaseMgr::Callback callback{type, owner, subnet_id, callback_fn};
+ callbacks_.insert(callback);
+}
+
+void
+TrackingLeaseMgr::registerCallback(TrackingLeaseMgr::CallbackType type,
+ std::string owner,
+ TrackingLeaseMgr::CallbackFn callback_fn) {
+ registerCallback(type, 0, owner, callback_fn);
+}
+
+void
+TrackingLeaseMgr::unregisterCallbacks(SubnetID subnet_id) {
+ // The second index filters the callbacks by the subnet identifier.
+ auto& idx = callbacks_.get<1>();
+ idx.erase(subnet_id);
+}
+
+void
+TrackingLeaseMgr::unregisterAllCallbacks() {
+ callbacks_.clear();
+}
+
+bool
+TrackingLeaseMgr::hasCallbacks() const {
+ return (!callbacks_.empty());
+}
+
+std::string
+TrackingLeaseMgr::callbackTypeToString(CallbackType type) {
+ switch (type) {
+ case TrackingLeaseMgr::TRACK_ADD_LEASE:
+ return ("add_lease");
+ case TrackingLeaseMgr::TRACK_UPDATE_LEASE:
+ return ("update_lease");
+ case TrackingLeaseMgr::TRACK_DELETE_LEASE:
+ return ("delete_lease");
+ default:
+ return ("unknown");
+ }
+}
+
+void
+TrackingLeaseMgr::runCallbacks(TrackingLeaseMgr::CallbackType type, const LeasePtr& lease,
+ bool mt_safe) {
+ runCallbacksForSubnetID(type, 0, lease, mt_safe);
+ runCallbacksForSubnetID(type, lease->subnet_id_, lease, mt_safe);
+}
+
+void
+TrackingLeaseMgr::runCallbacksForSubnetID(CallbackType type, SubnetID subnet_id,
+ const LeasePtr& lease, bool mt_safe) {
+ // The first index filters by callback type and subnet_id.
+ auto& idx_by_type = callbacks_.get<0>();
+ auto cbs = idx_by_type.equal_range(boost::make_tuple(type, subnet_id));
+ if (cbs.first == cbs.second) {
+ return;
+ }
+ for (auto it = cbs.first; it != cbs.second; ++it) {
+ auto cb = *it;
+ try {
+ cb.fn(lease, mt_safe);
+ } catch (const std::exception& ex) {
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_MGR_CALLBACK_EXCEPTION)
+ .arg(callbackTypeToString(type))
+ .arg(subnet_id)
+ .arg(lease->addr_.toText())
+ .arg(ex.what());
+ } catch (...) {
+ LOG_WARN(dhcpsrv_logger, DHCPSRV_LEASE_MGR_CALLBACK_UNKNOWN_EXCEPTION)
+ .arg(callbackTypeToString(type))
+ .arg(subnet_id)
+ .arg(lease->addr_.toText());
+ }
+ }
+}
+
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 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
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef TRACKING_LEASE_MGR_H
+#define TRACKING_LEASE_MGR_H
+
+#include <asiolink/io_address.h>
+#include <dhcpsrv/lease_mgr.h>
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/composite_key.hpp>
+#include <boost/multi_index/indexed_by.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/hashed_index.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <functional>
+#include <string>
+#include <unordered_set>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Introduces callbacks into the @c LeaseMgr.
+///
+/// The LeaseMgr is a central point of lease management and is aware of all
+/// lease changes within the server instance. Thus, it is a good opportunity
+/// to allow installing callbacks in the @c LeaseMgr to track all lease
+/// changes. An allocator maintaining a list of free leases (FLQ allocator)
+/// can benefit from it by/ installing the callbacks that add or remove
+/// free leases from this list, depending on the lease manager's activity.
+/// The callbacks are invoked regardless if the lease changes result from a
+/// normal lease allocation or a control command. The callbacks can also be
+/// useful for maintaining a log of lease changes. Such a log could be made
+/// available externally and consumed by another application (e.g., Stork).
+///
+/// The lease manager can track three types of calls: new lease insertion
+/// (add), an existing lease update, and lease deletion. Even though the
+/// lease reclamation is similar to deleting a lease because it becomes free,
+/// it is a lease update from the lease manager's standpoint. Currently,
+/// the lease manager cannot track the deletion of the reclaimed leases
+/// (i.e., the leases in the expired-reclaimed state).
+///
+/// The lease backends should call the suitable tracking functions:
+/// @c trackAddLease, @c trackUpdateLease, and @c trackDeleteLease.
+/// However, the backends must ensure that there are no race conditions
+/// between modifying the lease in the database and running the callbacks.
+/// Suppose that two threads modify the same lease. Thread A inserts the
+/// lease in the database, and thread B removes it. The callback for
+/// thread A should be invoked before the callback for thread B. If they
+/// are invoked in reverse order, it can result in an inconsistent state
+/// in the free lease queue allocator because the allocator should record
+/// the lease as available after the thread B callback. The reverse
+/// invocation order would result in marking the lease as unavailable for
+/// allocation after both callbacks.
+///
+/// The race condition does not occur for the Memfile backend because it
+/// guards entire functions with a mutex. However, the SQL backends rely
+/// on the database to guard against concurrent writes. In these cases,
+/// the backend must protect against the reverse order of callbacks. They
+/// should use the lease locking mechanism introduced in the
+/// @c TrackingLeaseMgr.
+///
+/// The lease locking is modeled on an @c unordered_set container holding
+/// the leases with the ongoing allocations. The leases are inserted into
+/// this container by the @c tryLock function. If another thread has already
+/// locked the lease, this function returns @c false to indicate an
+/// unsuccessful attempt. In this case, the thread should resign from updating
+/// the lease and return early. It can result in a lease allocation failure,
+/// but two concurrent threads extremely rarely work on allocating a lease for
+/// the same client. A passive wait could be another option here, but it is a
+/// much more complicated solution for a bit of gain.
+class TrackingLeaseMgr : public LeaseMgr {
+public:
+
+ /// @brief An enumeration differentiating between lease write operations.
+ typedef enum {
+ TRACK_ADD_LEASE,
+ TRACK_UPDATE_LEASE,
+ TRACK_DELETE_LEASE
+ } CallbackType;
+
+ /// @brief Type of a callback function invoked upon a lease insertion,
+ /// update or deletion.
+ ///
+ /// The first argument is a pointer to the lease for which the callback
+ /// is invoked. The second argument indicates whether or not the callback
+ /// is invoked in the thread-safe context.
+ typedef std::function<void(LeasePtr, bool)> CallbackFn;
+
+ /// @brief A structure representing a registered callback.
+ ///
+ /// It associates the callback with a type, its owner, and a subnet
+ /// identifier. The owner is a string specified by the registration
+ /// function caller. There must be at most one callback registered
+ /// for the particular owner and the subnet identifier.
+ typedef struct {
+ CallbackType type;
+ std::string owner;
+ SubnetID subnet_id;
+ CallbackFn fn;
+ } Callback;
+
+ /// @brief A multi-index container holding registered callbacks.
+ ///
+ /// The callbacks are accessible via two indexes. The first composite index
+ /// filters the callbacks by the callback type (i.e., lease add, update or delete)
+ /// and the subnet id. The second index filters the callbacks by the subnet id.
+ typedef boost::multi_index_container<
+ Callback,
+ boost::multi_index::indexed_by<
+ boost::multi_index::ordered_non_unique<
+ boost::multi_index::composite_key<
+ Callback,
+ boost::multi_index::member<Callback, CallbackType, &Callback::type>,
+ boost::multi_index::member<Callback, SubnetID, &Callback::subnet_id>
+ >
+ >,
+ boost::multi_index::hashed_non_unique<
+ boost::multi_index::member<Callback, SubnetID, &Callback::subnet_id>
+ >
+ >
+ > CallbackContainer;
+
+protected:
+
+ /// @brief Constructor.
+ TrackingLeaseMgr();
+
+ /// @brief Attempts to lock a lease.
+ ///
+ /// If a lease is successfully locked, no other thread can lock it. It protects
+ /// against running the callbacks out of order when two threads modify the same
+ /// lease. Such a locking should only be used when the lease allocation followed by
+ /// the callbacks invocation are not protected by some other synchronization
+ /// mechanism. In particular, the Memfile backend uses a mutex for locking in the
+ /// lease allocation functions. In this case, it is unnecessary to apply a lock at the
+ /// lease level. The SQL backends rely on the database locking mechanisms to prevent
+ /// the concurrent updates of the same lease. These backends must use the lease locking
+ /// to ensure the correct callbacks invocation order.
+ ///
+ /// This function is not thread-safe and must be invoked in a thread-safe context.
+ ///
+ /// @param lease a lease instance for which the lock should be attempted.
+ /// @return true when locking was successful, false otherwise. In the latter case,
+ /// the thread should stop a lease allocation or deallocation attempt.
+ bool tryLock(const LeasePtr& lease);
+
+ /// @brief Attempts to unlock a lease.
+ ///
+ /// This function is not thread-safe and must be invoked in a thread-safe context.
+ ///
+ /// @param lease a lease instance for which unlocking should be attempted.
+ void unlock(const LeasePtr& lease);
+
+ /// @brief Invokes the callbacks when a new lease is added.
+ ///
+ /// It executes all callbacks of the @c TRACK_ADD_LEASE type for a subnet id of 0
+ /// and the subnet id associated with the lease.
+ ///
+ /// The callbacks execution order is not guaranteed.
+ ///
+ /// @param lease new lease instance.
+ /// @param mt_safe a boolean flag indicating whether the callbacks are
+ /// invoked in the MT-safe context.
+ void trackAddLease(const LeasePtr& lease, bool mt_safe);
+
+ /// @brief Invokes the callbacks when a lease is updated.
+ ///
+ /// It executes all callbacks of the @c TRACK_UPDATE_LEASE type for a subnet id of 0
+ /// and the subnet id associated with the lease.
+ ///
+ /// The callbacks execution order is not guaranteed.
+ ///
+ /// @param lease updated lease instance.
+ /// @param mt_safe a boolean flag indicating whether the callbacks are
+ /// invoked in the MT-safe context.
+ void trackUpdateLease(const LeasePtr& lease, bool mt_safe);
+
+ /// @brief Invokes the callbacks when a lease is deleted.
+ ///
+ /// It executes all callbacks of the @c TRACK_DELETE_LEASE type for a subnet id of 0
+ /// and the subnet id associated with the lease.
+ ///
+ /// The callbacks execution order is not guaranteed.
+ ///
+ /// @param lease deleted lease instance.
+ /// @param mt_safe a boolean flag indicating whether the callbacks are
+ /// invoked in the MT-safe context.
+ void trackDeleteLease(const LeasePtr& lease, bool mt_safe);
+
+public:
+
+ /// @brief Registers a callback function for a subnet.
+ ///
+ /// @param type callback type.
+ /// @param subnet_id subnet identifier; it can be set to 0 if the callback should be
+ /// called for subnets.
+ /// @param owner callback owner identifier.
+ /// @param callback_fn callback function instance.
+ /// @throw InvalidOperation when the callback has been already registered for the given owner and
+ /// the subnet identifier.
+ void registerCallback(CallbackType type, SubnetID subnet_id, std::string owner, CallbackFn callback_fn);
+
+ /// @brief Registers a callback function for all subnets.
+ ///
+ /// @param type callback type.
+ /// @param owner callback owner identifier.
+ /// @param callback_fn callback function instance.
+ /// @throw InvalidOperation when the callback has been already registered for the given owner and
+ /// all subnets.
+ void registerCallback(CallbackType type, std::string owner, CallbackFn callback_fn);
+
+ /// @brief Unregisters all callbacks for a given subnet identifier.
+ ///
+ /// @param subnet_id subnet identifier.
+ void unregisterCallbacks(SubnetID subnet_id);
+
+ /// @brief Unregisters all callbacks.
+ void unregisterAllCallbacks();
+
+protected:
+
+ /// @brief Checks if any callbacks has been registered.
+ ///
+ /// It is a quick check to be performed by the backends whether or not
+ /// the callbacks mechanism is used.
+ ///
+ /// @return true if any callbacks has been registered.
+ bool hasCallbacks() const;
+
+ /// @brief Converts callback type to string for logging purposes.
+ ///
+ /// @param type callback type.
+ /// @return callback type name or the 'unknown' string.
+ static std::string callbackTypeToString(CallbackType type);
+
+ /// @brief Runs registered callbacks of the particular type.
+ ///
+ /// The specified lease instance contains the subnet identifier used to
+ /// filter the callbacks to be invoked.
+ ///
+ /// @param type callback type.
+ /// @param lease lease instance for which the callbacks are invoked.
+ /// @param mt_safe a boolean flag indicating whether the callbacks are
+ /// invoked in the MT-safe context.
+ void runCallbacks(CallbackType type, const LeasePtr& lease, bool mt_safe);
+
+ /// @brief Runs registered callbacks of the particular type for a subnet id.
+ ///
+ /// It is called internally by the @c runCallbacks function.
+ ///
+ /// @param type callback type.
+ /// @param subnet_id subnet identifier for which the callbacks are invoked.
+ /// @param lease lease instance for which the callbacks are invoked.
+ /// @param mt_safe a boolean flag indicating whether the callbacks are
+ /// invoked in the MT-safe context.
+ void runCallbacksForSubnetID(CallbackType type, SubnetID subnet_id,
+ const LeasePtr& lease, bool mt_safe);
+
+ /// @brief The multi-index container holding registered callbacks.
+ CallbackContainer callbacks_;
+
+ /// @brief A set of locked leases.
+ ///
+ /// It is empty if locking is not used (e.g. Memfile backend) or when there
+ /// are no ongoing allocations.
+ std::unordered_set<asiolink::IOAddress, asiolink::IOAddress::Hash> locked_leases_;
+};
+
+} // end of namespace isc::dhcp
+} // end of namespace isc
+
+
+#endif // TRACKING_LEASE_MGR_H