void
CfgDbAccess::createManagers() const {
- // Recreate lease manager.
- LeaseMgrFactory::destroy();
- LeaseMgrFactory::create(getLeaseDbAccessString());
+ // Recreate lease manager without preserving the registered callbacks.
+ LeaseMgrFactory::recreate(getLeaseDbAccessString(), false);
// Recreate host data source.
HostMgr::create();
namespace isc {
namespace dhcp {
-boost::scoped_ptr<LeaseMgr>&
+boost::scoped_ptr<TrackingLeaseMgr>&
LeaseMgrFactory::getLeaseMgrPtr() {
- static boost::scoped_ptr<LeaseMgr> leaseMgrPtr;
- return (leaseMgrPtr);
+ static boost::scoped_ptr<TrackingLeaseMgr> lease_mgr_ptr;
+ return (lease_mgr_ptr);
}
void
getLeaseMgrPtr().reset();
}
+void
+LeaseMgrFactory::recreate(const std::string& dbaccess, bool preserve_callbacks) {
+ TrackingLeaseMgr::CallbackContainerPtr callbacks;
+ // Preserve the callbacks if needed.
+ if (preserve_callbacks && haveInstance()) {
+ callbacks = instance().callbacks_;
+ }
+
+ // Re-create the manager.
+ destroy();
+ create(dbaccess);
+
+ if (callbacks) {
+ // Copy the callbacks to the new instance. It should be fast
+ // because we merely copy the pointer.
+ instance().callbacks_ = callbacks;
+ }
+}
+
bool
LeaseMgrFactory::haveInstance() {
return (getLeaseMgrPtr().get());
}
-LeaseMgr&
+TrackingLeaseMgr&
LeaseMgrFactory::instance() {
- LeaseMgr* lmptr = getLeaseMgrPtr().get();
+ TrackingLeaseMgr* lmptr = getLeaseMgrPtr().get();
if (lmptr == NULL) {
isc_throw(NoLeaseManager, "no current lease manager is available");
}
-// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-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
#define LEASE_MGR_FACTORY_H
#include <database/database_connection.h>
-#include <dhcpsrv/lease_mgr.h>
+#include <dhcpsrv/tracking_lease_mgr.h>
#include <exceptions/exceptions.h>
#include <boost/scoped_ptr.hpp>
/// lease manager is available.
static void destroy();
+ /// @brief Recreate an instance of a lease manager with optionally
+ /// preserving registered callbacks.
+ ///
+ /// @param dbaccess Database access parameters. These are in the form of
+ /// "keyword=value" pairs, separated by spaces. They are backend-
+ /// -end specific, although must include the "type" keyword which
+ /// gives the backend in use.
+ /// @param preserve_callbacks a boolean flag indicating if all registered
+ /// @c TrackingLeaseMgr callbacks should be copied to the new
+ /// instance.
+ static void recreate(const std::string& dbaccess,
+ bool preserve_callbacks = true);
+
/// @brief Return current lease manager
///
/// Returns an instance of the "current" lease manager. An exception
///
/// @throw isc::dhcp::NoLeaseManager No lease manager is available: use
/// create() to create one before calling this method.
- static LeaseMgr& instance();
+ static TrackingLeaseMgr& instance();
/// @brief Indicates if the lease manager has been instantiated.
///
/// Holds a pointer to the singleton lease manager. The singleton
/// is encapsulated in this method to avoid a "static initialization
/// fiasco" if defined in an external static variable.
- static boost::scoped_ptr<LeaseMgr>& getLeaseMgrPtr();
+ static boost::scoped_ptr<TrackingLeaseMgr>& getLeaseMgrPtr();
};
// At least one connection was lost.
try {
CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
- LeaseMgrFactory::destroy();
- LeaseMgrFactory::create(cfg_db->getLeaseDbAccessString());
+ LeaseMgrFactory::recreate(cfg_db->getLeaseDbAccessString());
reopened = true;
} catch (const std::exception& ex) {
LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_LEASE_DB_RECONNECT_ATTEMPT_FAILED)
// At least one connection was lost.
try {
CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
- LeaseMgrFactory::destroy();
- LeaseMgrFactory::create(cfg_db->getLeaseDbAccessString());
+ LeaseMgrFactory::recreate(cfg_db->getLeaseDbAccessString());
reopened = true;
} catch (const std::exception& ex) {
LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_LEASE_DB_RECONNECT_ATTEMPT_FAILED)
EXPECT_FALSE(lmptr_->isLocked(lease));
}
+void
+GenericLeaseMgrTest::testRecreateWithCallbacks(const std::string& access) {
+ // Register a callback.
+ lmptr_->registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&GenericLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ ph::_1,
+ ph::_2));
+
+ // Recreate the lease manager with the callbacks.
+ ASSERT_NO_THROW(LeaseMgrFactory::recreate(access, true));
+ lmptr_ = &(LeaseMgrFactory::instance());
+
+ // Add a lease. It should trigger the callback.
+ Lease4Ptr lease = initializeLease4(straddress4_[1]);
+ EXPECT_TRUE(lmptr_->addLease(lease));
+
+ // Make sure that the callback has been invoked.
+ EXPECT_EQ(1, logs_.size());
+}
+
+void
+GenericLeaseMgrTest::testRecreateWithoutCallbacks(const std::string& access) {
+ // Register a callback.
+ lmptr_->registerCallback(TrackingLeaseMgr::TRACK_ADD_LEASE, 0, "flq",
+ std::bind(&GenericLeaseMgrTest::logCallback,
+ this,
+ TrackingLeaseMgr::TRACK_ADD_LEASE,
+ 0,
+ ph::_1,
+ ph::_2));
+
+ // Recreate the lease manager without the callbacks.
+ ASSERT_NO_THROW(LeaseMgrFactory::recreate(access, false));
+ lmptr_ = &(LeaseMgrFactory::instance());
+
+ // Add a lease. It should not trigger the callback.
+ Lease4Ptr lease = initializeLease4(straddress4_[1]);
+ EXPECT_TRUE(lmptr_->addLease(lease));
+ EXPECT_TRUE(logs_.empty());
+}
+
} // namespace test
} // namespace dhcp
} // namespace isc
/// expect that the callbacks are called in the MT-safe context.
void testTrackDeleteLease6(bool expect_locked, bool expect_mt_safe);
+ /// @brief Checks that the lease manager can be recreated and its
+ /// registered callbacks preserved, if desired.
+ ///
+ /// @param access database connection string used for recreating the
+ /// lease manager.
+ void testRecreateWithCallbacks(const std::string& access);
+
+ /// @brief Checks that the lease manager can be recreated without the
+ /// previously registered callbacks.
+ ///
+ /// @param access database connection string used for recreating the
+ /// lease manager.
+ void testRecreateWithoutCallbacks(const std::string& access);
+
/// @brief String forms of IPv4 addresses
std::vector<std::string> straddress4_;
" lease database backend.\n";
throw;
}
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
}
/// @brief Runs IOService and stops after a specified time.
// Recreate Memfile_LeaseMgr.
LeaseMgrFactory::destroy();
ASSERT_NO_THROW(LeaseMgrFactory::create(dbaccess));
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
// We will store addresses here, so it will be easier to randomly
// pick a lease.
// Recreate Memfile_LeaseMgr.
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(dbaccess);
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
});
// Ok, let's check if the leases are really accessible.
// Recreate Memfile_LeaseMgr.
LeaseMgrFactory::destroy();
ASSERT_NO_THROW(LeaseMgrFactory::create(dbaccess));
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
// We will store addresses here, so it will be easier to randomly
// pick a lease.
// Recreate Memfile_LeaseMgr.
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(dbaccess);
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
});
// Ok, let's check if the leases are really accessible.
testTrackDeleteLease6(false, true);
}
+/// @brief Checks that the lease manager can be recreated and its
+/// registered callbacks preserved, if desired.
+TEST_F(MemfileLeaseMgrTest, recreateWithCallbacks) {
+ startBackend(V4);
+ testRecreateWithCallbacks(getConfigString(V4));
+}
+
+/// @brief Checks that the lease manager can be recreated without the
+/// previously registered callbacks.
+TEST_F(MemfileLeaseMgrTest, recreateWithoutCallbacks) {
+ startBackend(V4);
+ testRecreateWithoutCallbacks(getConfigString(V4));
+}
} // namespace
throw;
}
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
MultiThreadingMgr::instance().setMode(false);
}
void reopen(Universe) {
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(validMySQLConnectionString());
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
}
};
testTrackDeleteLease6(true, false);
}
+/// @brief Checks that the lease manager can be recreated and its
+/// registered callbacks preserved, if desired.
+TEST_F(MySqlLeaseMgrTest, recreateWithCallbacks) {
+ testRecreateWithCallbacks(validMySQLConnectionString());
+}
+
+/// @brief Checks that the lease manager can be recreated without the
+/// previously registered callbacks.
+TEST_F(MySqlLeaseMgrTest, recreateWithoutCallbacks) {
+ testRecreateWithoutCallbacks(validMySQLConnectionString());
+}
+
} // namespace
throw;
}
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
MultiThreadingMgr::instance().setMode(false);
}
void reopen(Universe) {
LeaseMgrFactory::destroy();
LeaseMgrFactory::create(validPgSQLConnectionString());
- lmptr_ = static_cast<TrackingLeaseMgr*>(&(LeaseMgrFactory::instance()));
+ lmptr_ = &(LeaseMgrFactory::instance());
}
};
testTrackDeleteLease6(true, false);
}
+/// @brief Checks that the lease manager can be recreated and its
+/// registered callbacks preserved, if desired.
+TEST_F(PgSqlLeaseMgrTest, recreateWithCallbacks) {
+ testRecreateWithCallbacks(validPgSQLConnectionString());
+}
+
+/// @brief Checks that the lease manager can be recreated without the
+/// previously registered callbacks.
+TEST_F(PgSqlLeaseMgrTest, recreateWithoutCallbacks) {
+ testRecreateWithoutCallbacks(validPgSQLConnectionString());
+}
+
+
} // namespace
namespace dhcp {
TrackingLeaseMgr::TrackingLeaseMgr()
- : LeaseMgr() {
+ : LeaseMgr(), callbacks_(new TrackingLeaseMgr::CallbackContainer()) {
}
bool
std::string owner,
TrackingLeaseMgr::CallbackFn callback_fn) {
// The first index filters the callbacks by type and subnet_id.
- auto& idx = callbacks_.get<0>();
+ 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.
}
}
TrackingLeaseMgr::Callback callback{type, owner, subnet_id, callback_fn};
- callbacks_.insert(callback);
+ callbacks_->insert(callback);
}
void
void
TrackingLeaseMgr::unregisterCallbacks(SubnetID subnet_id) {
// The second index filters the callbacks by the subnet identifier.
- auto& idx = callbacks_.get<1>();
+ auto& idx = callbacks_->get<1>();
idx.erase(subnet_id);
}
void
TrackingLeaseMgr::unregisterAllCallbacks() {
- callbacks_.clear();
+ callbacks_->clear();
}
bool
TrackingLeaseMgr::hasCallbacks() const {
- return (!callbacks_.empty());
+ return (!callbacks_->empty());
}
std::string
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& 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;
#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 <boost/shared_ptr.hpp>
#include <functional>
#include <string>
#include <unordered_set>
class TrackingLeaseMgr : public LeaseMgr {
public:
+ /// The @c LeaseMgrFactory manages the @c LeaseMgr instances and has
+ /// to be able to move installed callbacks between them. No other external
+ /// class can have access to the callbacks container. Thus, we can't make
+ /// the container public. The friend declaration deals with it cleanly.
+ friend class LeaseMgrFactory;
+
/// @brief An enumeration differentiating between lease write operations.
typedef enum {
TRACK_ADD_LEASE,
CallbackFn fn;
} Callback;
+protected:
+
/// @brief A multi-index container holding registered callbacks.
///
/// The callbacks are accessible via two indexes. The first composite index
>
> CallbackContainer;
-protected:
+ /// @brief Pointer to the callback container.
+ typedef boost::shared_ptr<CallbackContainer> CallbackContainerPtr;
/// @brief Constructor.
TrackingLeaseMgr();
const LeasePtr& lease, bool mt_safe);
/// @brief The multi-index container holding registered callbacks.
- CallbackContainer callbacks_;
+ CallbackContainerPtr callbacks_;
/// @brief A set of locked leases.
///