#include <sstream>
+using namespace isc::asiolink;
using namespace isc::config;
using namespace isc::data;
using namespace isc::db;
// Re-open lease and host database with new parameters.
try {
- DatabaseConnection::db_lost_callback =
+ DatabaseConnection::db_lost_callback_ =
std::bind(&ControlledDhcpv4Srv::dbLostCallback, srv, ph::_1);
+
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&ControlledDhcpv4Srv::dbRecoveredCallback, srv, ph::_1);
+
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&ControlledDhcpv4Srv::dbFailedCallback, srv, ph::_1);
+
CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
cfg_db->setAppendedParameters("universe=4");
- cfg_db->createManagers();
+ cfg_db->createManagers(server_->io_service_);
} catch (const std::exception& ex) {
err << "Unable to open database: " << ex.what();
return (isc::config::createAnswer(1, err.str()));
return (isc::config::createAnswer(1, err.str()));
}
- // Configure DHCP packet queueing
+ // Configure DHCP packet queuing.
try {
data::ConstElementPtr qc;
- qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
+ qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET, qc)) {
LOG_INFO(dhcp4_logger, DHCP4_CONFIG_PACKET_QUEUE)
.arg(IfaceMgr::instance().getPacketQueue4()->getInfoStr());
ControlledDhcpv4Srv::ControlledDhcpv4Srv(uint16_t server_port /*= DHCP4_SERVER_PORT*/,
uint16_t client_port /*= 0*/)
- : Dhcpv4Srv(server_port, client_port), io_service_(),
+ : Dhcpv4Srv(server_port, client_port), io_service_(boost::make_shared<IOService>()),
timer_mgr_(TimerMgr::instance()) {
if (getInstance()) {
isc_throw(InvalidOperation,
void ControlledDhcpv4Srv::shutdownServer(int exit_value) {
setExitValue(exit_value);
- io_service_.stop(); // Stop ASIO transmissions
+ io_service_->stop(); // Stop ASIO transmissions
shutdown(); // Initiate DHCPv4 shutdown procedure.
}
// The closure captures either a shared pointer (memory leak)
// or a raw pointer (pointing to a deleted object).
- DatabaseConnection::db_lost_callback = 0;
+ DatabaseConnection::db_lost_callback_ = 0;
+ DatabaseConnection::db_recovered_callback_ = 0;
timer_mgr_->unregisterTimers();
// Process one asio event. If there are more events, iface_mgr will call
// this callback more than once.
if (getInstance()) {
- getInstance()->io_service_.run_one();
+ getInstance()->io_service_->run_one();
}
}
TimerMgr::instance()->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME);
}
-void
-ControlledDhcpv4Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
- bool reopened = false;
-
- // We lost at least one of them. Reopen all of them (lease, host, and CB databases).
- try {
- CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
- cfg_db->createManagers();
-
- auto ctl_info = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo();
- if (ctl_info) {
- auto srv_cfg = CfgMgr::instance().getCurrentCfg();
- server_->getCBControl()->databaseConfigConnect(srv_cfg);
- }
-
- reopened = true;
- } catch (const std::exception& ex) {
- LOG_ERROR(dhcp4_logger, DHCP4_DB_RECONNECT_ATTEMPT_FAILED).arg(ex.what());
- }
-
- if (reopened) {
- // Cancel the timer.
- if (TimerMgr::instance()->isTimerRegistered("Dhcp4DbReconnectTimer")) {
- TimerMgr::instance()->cancel("Dhcp4DbReconnectTimer");
- }
-
- // Set network state to service enabled
- network_state_->enableService();
-
- // Toss the reconnect control, we're done with it
- db_reconnect_ctl.reset();
- } else {
- if (!db_reconnect_ctl->checkRetries()) {
- // We're out of retries, log it and initiate shutdown.
- LOG_ERROR(dhcp4_logger, DHCP4_DB_RECONNECT_RETRIES_EXHAUSTED)
- .arg(db_reconnect_ctl->maxRetries());
- shutdownServer(EXIT_FAILURE);
- return;
- }
-
- LOG_INFO(dhcp4_logger, DHCP4_DB_RECONNECT_ATTEMPT_SCHEDULE)
- .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
- .arg(db_reconnect_ctl->maxRetries())
- .arg(db_reconnect_ctl->retryInterval());
-
- if (!TimerMgr::instance()->isTimerRegistered("Dhcp4DbReconnectTimer")) {
- TimerMgr::instance()->registerTimer("Dhcp4DbReconnectTimer",
- std::bind(&ControlledDhcpv4Srv::dbReconnect, this,
- db_reconnect_ctl),
- db_reconnect_ctl->retryInterval(),
- asiolink::IntervalTimer::ONE_SHOT);
- }
-
- TimerMgr::instance()->setup("Dhcp4DbReconnectTimer");
- }
-}
-
bool
ControlledDhcpv4Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
- // Disable service until we recover
+ // Disable service until the connection is recovered.
network_state_->disableService();
if (!db_reconnect_ctl) {
return (false);
}
- // If reconnect isn't enabled log it,
- // initiate a shutdown and return false.
+ // If reconnect isn't enabled log it, initiate a shutdown and return false.
if (!db_reconnect_ctl->retriesLeft() ||
!db_reconnect_ctl->retryInterval()) {
LOG_INFO(dhcp4_logger, DHCP4_DB_RECONNECT_DISABLED)
.arg(db_reconnect_ctl->retriesLeft())
.arg(db_reconnect_ctl->retryInterval());
shutdownServer(EXIT_FAILURE);
- return(false);
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+ControlledDhcpv4Srv::dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ // Enable service after the connection is recovered.
+ network_state_->enableService();
+
+ if (!db_reconnect_ctl) {
+ // This shouldn't never happen
+ LOG_ERROR(dhcp4_logger, DHCP4_DB_RECONNECT_NO_DB_CTL);
+ return (false);
}
- // Invoke reconnect method
- dbReconnect(db_reconnect_ctl);
+ db_reconnect_ctl->resetRetries();
+
+ return (true);
+}
+
+bool
+ControlledDhcpv4Srv::dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ // This shouldn't never happen
+ LOG_ERROR(dhcp4_logger, DHCP4_DB_RECONNECT_NO_DB_CTL);
+ return (false);
+ }
+
+ //LOG_INFO(dhcp4_logger, DHCP4_DB_FAILED_RECONNECT)
+ // .arg(db_reconnect_ctl->maxRetries());
+
+ db_reconnect_ctl->resetRetries();
+
+ shutdownServer(EXIT_FAILURE);
- return(true);
+ return (true);
}
void
/// deleted.
void deleteExpiredReclaimedLeases(const uint32_t secs);
- /// @brief Attempts to reconnect the server to the DB backend managers
- ///
- /// This is a self-rescheduling function that attempts to reconnect to the
- /// server's DB backends after connectivity to one or more have been
- /// lost. Upon entry it will attempt to reconnect via @ref CfgDdbAccess::
- /// createManagers. If this is succesful, DHCP servicing is re-enabled and
- /// server returns to normal operation.
- ///
- /// If reconnection fails and the maximum number of retries has not been
- /// exhausted, it will schedule a call to itself to occur at the
- /// configured retry interval. DHCP service remains disabled.
- ///
- /// If the maximum number of retries has been exhausted an error is logged
- /// and the server shuts down.
- ///
- /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
- /// configured reconnect parameters
- ///
- void dbReconnect(db::ReconnectCtlPtr db_reconnect_ctl);
-
- /// @brief Callback DB backends should invoke upon loss of connectivity
+ /// @brief Callback DB backends should invoke upon loss of the connectivity
///
/// This function is invoked by DB backends when they detect a loss of
/// connectivity. The parameter, db_reconnect_ctl, conveys the configured
///
/// If either value is zero, reconnect is presumed to be disabled and
/// the function will schedule a shutdown and return false. This instructs
- /// the DB backend layer (the caller) to treat the connectivity loss as fatal.
- ///
- /// Otherwise, the function saves db_reconnect_ctl and invokes
- /// dbReconnect to initiate the reconnect process.
+ /// the DB backend layer (the caller) to treat the connectivity loss as
+ /// fatal. It stops the DHCP service until the connection is recovered.
///
/// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
/// configured reconnect parameters
/// @return false if reconnect is not configured, true otherwise
bool dbLostCallback(db::ReconnectCtlPtr db_reconnect_ctl);
+ /// @brief Callback DB backends should invoke upon restoration of the
+ /// connectivity
+ ///
+ /// This function is invoked by DB backends when they recover the
+ /// connectivity. It starts the DHCP service after the connection is
+ /// recovered.
+ ///
+ /// @return false if reconnect is not configured, true otherwise
+ bool dbRecoveredCallback(db::ReconnectCtlPtr db_reconnect_ctl);
+
+ /// @brief Callback DB backends should invoke upon failing to restore of the
+ /// connectivity
+ ///
+ /// This function is invoked by DB backends when they fail to recover the
+ /// connectivity. It stops the server.
+ ///
+ /// @return false if reconnect is not configured, true otherwise
+ bool dbFailedCallback(db::ReconnectCtlPtr db_reconnect_ctl);
+
/// @brief Callback invoked periodically to fetch configuration updates
/// from the Config Backends.
///
static ControlledDhcpv4Srv* server_;
/// @brief IOService object, used for all ASIO operations.
- isc::asiolink::IOService io_service_;
+ isc::asiolink::IOServicePtr io_service_;
/// @brief Instance of the @c TimerMgr.
///
#include <sstream>
+using namespace isc::asiolink;
using namespace isc::config;
using namespace isc::data;
using namespace isc::db;
// Re-open lease and host database with new parameters.
try {
- DatabaseConnection::db_lost_callback =
+ DatabaseConnection::db_lost_callback_ =
std::bind(&ControlledDhcpv6Srv::dbLostCallback, srv, ph::_1);
+
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&ControlledDhcpv6Srv::dbRecoveredCallback, srv, ph::_1);
+
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&ControlledDhcpv6Srv::dbFailedCallback, srv, ph::_1);
+
CfgDbAccessPtr cfg_db = CfgMgr::instance().getStagingCfg()->getCfgDbAccess();
cfg_db->setAppendedParameters("universe=6");
- cfg_db->createManagers();
+ cfg_db->createManagers(server_->io_service_);
} catch (const std::exception& ex) {
err << "Unable to open database: " << ex.what();
return (isc::config::createAnswer(1, err.str()));
return (isc::config::createAnswer(1, err.str()));
}
- // Configure DHCP packet queueing
+ // Configure DHCP packet queuing.
try {
data::ConstElementPtr qc;
- qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
+ qc = CfgMgr::instance().getStagingCfg()->getDHCPQueueControl();
if (IfaceMgr::instance().configureDHCPPacketQueue(AF_INET6, qc)) {
LOG_INFO(dhcp6_logger, DHCP6_CONFIG_PACKET_QUEUE)
.arg(IfaceMgr::instance().getPacketQueue6()->getInfoStr());
ControlledDhcpv6Srv::ControlledDhcpv6Srv(uint16_t server_port,
uint16_t client_port)
- : Dhcpv6Srv(server_port, client_port), io_service_(),
+ : Dhcpv6Srv(server_port, client_port), io_service_(boost::make_shared<IOService>()),
timer_mgr_(TimerMgr::instance()) {
if (getInstance()) {
isc_throw(InvalidOperation,
void ControlledDhcpv6Srv::shutdownServer(int exit_value) {
setExitValue(exit_value);
- io_service_.stop(); // Stop ASIO transmissions
+ io_service_->stop(); // Stop ASIO transmissions
shutdown(); // Initiate DHCPv6 shutdown procedure.
}
// The closure captures either a shared pointer (memory leak)
// or a raw pointer (pointing to a deleted object).
- DatabaseConnection::db_lost_callback = 0;
+ DatabaseConnection::db_lost_callback_ = 0;
+ DatabaseConnection::db_recovered_callback_ = 0;
timer_mgr_->unregisterTimers();
// Process one asio event. If there are more events, iface_mgr will call
// this callback more than once.
if (getInstance()) {
- getInstance()->io_service_.run_one();
+ getInstance()->io_service_->run_one();
}
}
TimerMgr::instance()->setup(CfgExpiration::FLUSH_RECLAIMED_TIMER_NAME);
}
-void
-ControlledDhcpv6Srv::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
- bool reopened = false;
-
- // We lost at least one of them. Reopen all of them (lease, host, and CB databases).
- try {
- CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
- cfg_db->createManagers();
-
- auto ctl_info = CfgMgr::instance().getCurrentCfg()->getConfigControlInfo();
- if (ctl_info) {
- auto srv_cfg = CfgMgr::instance().getCurrentCfg();
- server_->getCBControl()->databaseConfigConnect(srv_cfg);
- }
-
- reopened = true;
- } catch (const std::exception& ex) {
- LOG_ERROR(dhcp6_logger, DHCP6_DB_RECONNECT_ATTEMPT_FAILED).arg(ex.what());
- }
-
- if (reopened) {
- // Cancel the timer.
- if (TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) {
- TimerMgr::instance()->cancel("Dhcp6DbReconnectTimer");
- }
-
- // Set network state to service enabled
- network_state_->enableService();
-
- // Toss the reconnect control, we're done with it
- db_reconnect_ctl.reset();
- } else {
- if (!db_reconnect_ctl->checkRetries()) {
- // We're out of retries, log it and initiate shutdown.
- LOG_ERROR(dhcp6_logger, DHCP6_DB_RECONNECT_RETRIES_EXHAUSTED)
- .arg(db_reconnect_ctl->maxRetries());
- shutdownServer(EXIT_FAILURE);
- return;
- }
-
- LOG_INFO(dhcp6_logger, DHCP6_DB_RECONNECT_ATTEMPT_SCHEDULE)
- .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
- .arg(db_reconnect_ctl->maxRetries())
- .arg(db_reconnect_ctl->retryInterval());
-
- if (!TimerMgr::instance()->isTimerRegistered("Dhcp6DbReconnectTimer")) {
- TimerMgr::instance()->registerTimer("Dhcp6DbReconnectTimer",
- std::bind(&ControlledDhcpv6Srv::dbReconnect, this,
- db_reconnect_ctl),
- db_reconnect_ctl->retryInterval(),
- asiolink::IntervalTimer::ONE_SHOT);
- }
-
- TimerMgr::instance()->setup("Dhcp6DbReconnectTimer");
- }
-}
-
bool
ControlledDhcpv6Srv::dbLostCallback(ReconnectCtlPtr db_reconnect_ctl) {
- // Disable service until we recover
+ // Disable service until the connection is recovered.
network_state_->disableService();
if (!db_reconnect_ctl) {
return (false);
}
- // If reconnect isn't enabled log it and initiate a shutdown.
+ // If reconnect isn't enabled log it, initiate a shutdown and return false.
if (!db_reconnect_ctl->retriesLeft() ||
!db_reconnect_ctl->retryInterval()) {
LOG_INFO(dhcp6_logger, DHCP6_DB_RECONNECT_DISABLED)
.arg(db_reconnect_ctl->retriesLeft())
.arg(db_reconnect_ctl->retryInterval());
shutdownServer(EXIT_FAILURE);
- return(false);
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+ControlledDhcpv6Srv::dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ // Enable service after the connection is recovered.
+ network_state_->enableService();
+
+ if (!db_reconnect_ctl) {
+ // This shouldn't never happen
+ LOG_ERROR(dhcp6_logger, DHCP6_DB_RECONNECT_NO_DB_CTL);
+ return (false);
}
- // Invoke reconnect method
- dbReconnect(db_reconnect_ctl);
+ db_reconnect_ctl->resetRetries();
+
+ return (true);
+}
+
+bool
+ControlledDhcpv6Srv::dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ // This shouldn't never happen
+ LOG_ERROR(dhcp6_logger, DHCP6_DB_RECONNECT_NO_DB_CTL);
+ return (false);
+ }
+
+ //LOG_INFO(dhcp6_logger, DHCP6_DB_FAILED_RECONNECT)
+ // .arg(db_reconnect_ctl->maxRetries());
+
+ db_reconnect_ctl->resetRetries();
+
+ shutdownServer(EXIT_FAILURE);
- return(true);
+ return (true);
}
void
/// deleted.
void deleteExpiredReclaimedLeases(const uint32_t secs);
- /// @brief Attempts to reconnect the server to the DB backend managers
- ///
- /// This is a self-rescheduling function that attempts to reconnect to the
- /// server's DB backends after connectivity to one or more have been
- /// lost. Upon entry it will attempt to reconnect via @ref CfgDdbAccess::
- /// createManagers. If this is succesful, DHCP servicing is re-enabled and
- /// server returns to normal operation.
- ///
- /// If reconnection fails and the maximum number of retries has not been
- /// exhausted, it will schedule a call to itself to occur at the
- /// configured retry interval. DHCP service remains disabled.
- ///
- /// If the maximum number of retries has been exhausted an error is logged
- /// and the server shuts down.
- ///
- /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
- /// configured reconnect parameters
- ///
- void dbReconnect(db::ReconnectCtlPtr db_reconnect_ctl);
-
- /// @brief Callback DB backends should invoke upon loss of connectivity
+ /// @brief Callback DB backends should invoke upon loss of the connectivity
///
/// This function is invoked by DB backends when they detect a loss of
/// connectivity. The parameter, db_reconnect_ctl, conveys the configured
///
/// If either value is zero, reconnect is presumed to be disabled and
/// the function will schedule a shutdown and return false. This instructs
- /// the DB backend layer (the caller) to treat the connectivity loss as fatal.
- ///
- /// Otherwise, the function saves db_reconnect_ctl and invokes
- /// dbReconnect to initiate the reconnect process.
+ /// the DB backend layer (the caller) to treat the connectivity loss as
+ /// fatal. It stops the DHCP service until the connection is recovered.
///
/// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
/// configured reconnect parameters
/// @return false if reconnect is not configured, true otherwise
bool dbLostCallback(db::ReconnectCtlPtr db_reconnect_ctl);
+ /// @brief Callback DB backends should invoke upon restoration of the
+ /// connectivity
+ ///
+ /// This function is invoked by DB backends when they recover the
+ /// connectivity. It starts the DHCP service after the connection is
+ /// recovered.
+ ///
+ /// @return false if reconnect is not configured, true otherwise
+ bool dbRecoveredCallback(db::ReconnectCtlPtr db_reconnect_ctl);
+
+ /// @brief Callback DB backends should invoke upon failing to restore of the
+ /// connectivity
+ ///
+ /// This function is invoked by DB backends when they fail to recover the
+ /// connectivity. It stops the server.
+ ///
+ /// @return false if reconnect is not configured, true otherwise
+ bool dbFailedCallback(db::ReconnectCtlPtr db_reconnect_ctl);
+
/// @brief Callback invoked periodically to fetch configuration updates
/// from the Config Backends.
///
static ControlledDhcpv6Srv* server_;
/// @brief IOService object, used for all ASIO operations.
- isc::asiolink::IOService io_service_;
+ isc::asiolink::IOServicePtr io_service_;
/// @brief Instance of the @c TimerMgr.
///
// io_service::post().
class CallbackWrapper {
public:
+
+ /// \brief Constructor
CallbackWrapper(const std::function<void()>& callback) :
- callback_(callback)
- {}
+ callback_(callback) {}
+
+ /// \brief Function operator
void operator()() {
callback_();
}
+
private:
+
+ /// \brief The callback function
std::function<void()> callback_;
};
}
/// It will eventually be removed once the wrapper interface is
/// generalized.
boost::asio::io_service& get_io_service() { return io_service_; };
+
+ /// \brief Post a callback on the IO service
+ ///
+ /// \param callback The callback to be run on the IO service.
void post(const std::function<void ()>& callback) {
const CallbackWrapper wrapper(callback);
io_service_.post(wrapper);
return (readonly_value == "true");
}
-ReconnectCtlPtr
-DatabaseConnection::makeReconnectCtl() const {
- ReconnectCtlPtr retry;
+void
+DatabaseConnection::makeReconnectCtl(const std::string& timer_name) {
string type = "unknown";
unsigned int retries = 0;
unsigned int interval = 0;
- // Assumes that parsing ensurse only valid values are present
+ // Assumes that parsing ensures only valid values are present
try {
type = getParameter("type");
} catch (...) {
// Wasn't specified so we'll use default of 0;
}
- retry.reset(new ReconnectCtl(type, retries, interval));
- return (retry);
+ reconnect_ctl_ = boost::make_shared<ReconnectCtl>(timer_name, type, retries,
+ interval);
}
bool
-DatabaseConnection::invokeDbLostCallback() const {
- if (DatabaseConnection::db_lost_callback) {
- // Invoke the callback, passing in a new instance of ReconnectCtl
- return (DatabaseConnection::db_lost_callback)(makeReconnectCtl());
+DatabaseConnection::invokeDbLostCallback(const ReconnectCtlPtr& db_reconnect_ctl) {
+ if (DatabaseConnection::db_lost_callback_) {
+ return (DatabaseConnection::db_lost_callback_(db_reconnect_ctl));
+ }
+
+ return (false);
+}
+
+bool
+DatabaseConnection::invokeDbRecoveredCallback(const ReconnectCtlPtr& db_reconnect_ctl) {
+ if (DatabaseConnection::db_recovered_callback_) {
+ return (DatabaseConnection::db_recovered_callback_(db_reconnect_ctl));
+ }
+
+ return (false);
+}
+
+bool
+DatabaseConnection::invokeDbFailedCallback(const ReconnectCtlPtr& db_reconnect_ctl) {
+ if (DatabaseConnection::db_failed_callback_) {
+ return (DatabaseConnection::db_failed_callback_(db_reconnect_ctl));
}
return (false);
return (toElement(params));
}
-DatabaseConnection::DbLostCallback
-DatabaseConnection::db_lost_callback = 0;
+DbCallback DatabaseConnection::db_lost_callback_ = 0;
+DbCallback DatabaseConnection::db_recovered_callback_ = 0;
+DbCallback DatabaseConnection::db_failed_callback_ = 0;
-};
-};
+} // namespace db
+} // namespace isc
isc::Exception(file, line, what) {}
};
-/// @brief Exception thrown when connectivity has been lost and
-/// cannot be recovered.
-class DbUnrecoverableError : public Exception {
-public:
- DbUnrecoverableError(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) {}
-};
-
/// @brief Exception thrown when a specific connection has been rendered unusable
/// either through loss of connectivity or API lib error
-class DbConnectionUnusable: public Exception {
+class DbConnectionUnusable : public Exception {
public:
DbConnectionUnusable(const char* file, size_t line, const char* what) :
isc::Exception(file, line, what) {}
class ReconnectCtl {
public:
/// @brief Constructor
+ ///
/// @param backend_type type of the caller backend.
+ /// @param timer_name timer associated to this object
/// @param max_retries maximum number of reconnect attempts to make
/// @param retry_interval amount of time to between reconnect attempts
- ReconnectCtl(const std::string& backend_type, unsigned int max_retries,
- unsigned int retry_interval)
- : backend_type_(backend_type), max_retries_(max_retries),
- retries_left_(max_retries), retry_interval_(retry_interval) {}
+ ReconnectCtl(const std::string& backend_type, const std::string& timer_name,
+ unsigned int max_retries, unsigned int retry_interval)
+ : backend_type_(backend_type), timer_name_(timer_name),
+ max_retries_(max_retries), retries_left_(max_retries),
+ retry_interval_(retry_interval) {}
/// @brief Returns the type of the caller backend.
std::string backendType() const {
return (backend_type_);
}
+ /// @brief Returns the associated timer name.
+ ///
+ /// @return the associated timer.
+ std::string timerName() const {
+ return (timer_name_);
+ }
+
/// @brief Decrements the number of retries remaining
///
/// Each call decrements the number of retries by one until zero is reached.
return (retry_interval_);
}
+ /// @brief Resets the retries count
+ void resetRetries() {
+ retries_left_ = max_retries_;
+ }
+
private:
+
/// @brief Caller backend type.
const std::string backend_type_;
+ /// @brief Timer associated to this object.
+ std::string timer_name_;
+
/// @brief Maximum number of retry attempts to make
unsigned int max_retries_;
/// @brief Pointer to an instance of ReconnectCtl
typedef boost::shared_ptr<ReconnectCtl> ReconnectCtlPtr;
+/// @brief Defines a callback prototype for propagating events upward
+typedef std::function<bool (ReconnectCtlPtr db_reconnect_ctl)> DbCallback;
+
/// @brief Common database connection class.
///
/// This class provides functions that are common for establishing
/// @param parameters A data structure relating keywords and values
/// concerned with the database.
DatabaseConnection(const ParameterMap& parameters)
- :parameters_(parameters), unusable_(false) {
+ : parameters_(parameters), unusable_(false) {
}
/// @brief Destructor
/// @brief Instantiates a ReconnectCtl based on the connection's
/// reconnect parameters
- /// @return pointer to the new ReconnectCtl object
- virtual ReconnectCtlPtr makeReconnectCtl() const;
+ ///
+ /// @param timer_name of the timer used for the ReconnectCtl object.
+ virtual void makeReconnectCtl(const std::string& timer_name);
+
+ /// @brief The reconnect settings.
+ ///
+ /// @brief return The reconnect settings.
+ ReconnectCtlPtr reconnectCtl() {
+ return (reconnect_ctl_);
+ }
/// @brief Returns value of a connection parameter.
///
/// and set to false.
bool configuredReadOnly() const;
- /// @brief Defines a callback prototype for propogating events upward
- typedef std::function<bool (ReconnectCtlPtr db_retry)> DbLostCallback;
-
/// @brief Invokes the connection's lost connectivity callback
///
- /// This function may be called by derivations when the connectivity
- /// to their data server is lost. If connectivity callback was specified,
- /// this function will instantiate a ReconnectCtl and pass it to the
+ /// @return Returns the result of the callback or false if there is no
/// callback.
+ static bool invokeDbLostCallback(const ReconnectCtlPtr& db_reconnect_ctl);
+
+ /// @brief Invokes the connection's restored connectivity callback
///
/// @return Returns the result of the callback or false if there is no
/// callback.
- bool invokeDbLostCallback() const;
+ static bool invokeDbRecoveredCallback(const ReconnectCtlPtr& db_reconnect_ctl);
+
+ /// @brief Invokes the connection's restore failed connectivity callback
+ ///
+ /// @return Returns the result of the callback or false if there is no
+ /// callback.
+ static bool invokeDbFailedCallback(const ReconnectCtlPtr& db_reconnect_ctl);
/// @brief Unparse a parameter map
///
/// @return a pointer to configuration
static isc::data::ElementPtr toElementDbAccessString(const std::string& dbaccess);
- /// @brief Optional call back function to invoke if a successfully
- /// open connection subsequently fails
- static DbLostCallback db_lost_callback;
+ /// @brief Optional callback function to invoke if an opened connection is
+ /// lost
+ static DbCallback db_lost_callback_;
+
+ /// @brief Optional callback function to invoke if an opened connection
+ /// recovery succeeded
+ static DbCallback db_recovered_callback_;
+
+ /// @brief Optional callback function to invoke if an opened connection
+ /// recovery failed
+ static DbCallback db_failed_callback_;
/// @brief Throws an exception if the connection is not usable.
/// @throw DbConnectionUnusable
/// parameters and error information. This flag can be used as a guard in
/// code to prevent inadvertent use of a broken connection.
bool unusable_;
+
+ /// @brief Reconnect settings.
+ ReconnectCtlPtr reconnect_ctl_;
};
} // namespace db
/// Constructor
DatabaseConnectionCallbackTest()
: db_reconnect_ctl_(0) {
- DatabaseConnection::db_lost_callback = 0;
+ DatabaseConnection::db_lost_callback_ = 0;
+ DatabaseConnection::db_recovered_callback_ = 0;
+ DatabaseConnection::db_failed_callback_ = 0;
+ }
+
+ /// Destructor
+ ~DatabaseConnectionCallbackTest() {
+ DatabaseConnection::db_lost_callback_ = 0;
+ DatabaseConnection::db_recovered_callback_ = 0;
+ DatabaseConnection::db_failed_callback_ = 0;
}
/// @brief Callback to register with a DatabaseConnection
return (true);
}
+ /// @brief Callback to register with a DatabaseConnection
+ ///
+ /// @param db_reconnect_ctl ReconnectCtl containing reconnect
+ /// parameters
+ bool dbRecoveredCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ isc_throw(isc::BadValue, "db_reconnect_ctl should not be null");
+ }
+
+ db_reconnect_ctl_ = db_reconnect_ctl;
+ db_reconnect_ctl_->resetRetries();
+ return (true);
+ }
+
+ /// @brief Callback to register with a DatabaseConnection
+ ///
+ /// @param db_reconnect_ctl ReconnectCtl containing reconnect
+ /// parameters
+ bool dbFailedCallback(ReconnectCtlPtr db_reconnect_ctl) {
+ if (!db_reconnect_ctl) {
+ isc_throw(isc::BadValue, "db_reconnect_ctl should not be null");
+ }
+
+ db_reconnect_ctl_ = db_reconnect_ctl;
+ db_reconnect_ctl_->resetRetries();
+ return (false);
+ }
+
/// @brief Retainer for the control passed into the callback
ReconnectCtlPtr db_reconnect_ctl_;
};
/// @brief NoDbLostCallback
///
/// This test verifies that DatabaseConnection::invokeDbLostCallback
-/// returns a false if there is connection has no registered
-/// DbLostCallback.
+/// returns false if the connection has no registered DbCallback.
TEST_F(DatabaseConnectionCallbackTest, NoDbLostCallback) {
DatabaseConnection::ParameterMap pmap;
pmap[std::string("type")] = std::string("test");
pmap[std::string("max-reconnect-tries")] = std::string("3");
pmap[std::string("reconnect-wait-time")] = std::string("60000");
DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+
+ bool ret = false;
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
+ EXPECT_FALSE(ret);
+ EXPECT_FALSE(db_reconnect_ctl_);
+}
+
+/// @brief NoDbRecoveredCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbRecoveredCallback
+/// returns false if the connection has no registered DbCallback.
+TEST_F(DatabaseConnectionCallbackTest, NoDbRecoveredCallback) {
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+
+ bool ret = false;
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbRecoveredCallback(datasrc.reconnectCtl()));
+ EXPECT_FALSE(ret);
+ EXPECT_FALSE(db_reconnect_ctl_);
+}
+
+/// @brief NoDbFailedCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbFailedCallback
+/// returns false if the connection has no registered DbCallback.
+TEST_F(DatabaseConnectionCallbackTest, NoDbFailedCallback) {
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
bool ret = false;
- ASSERT_NO_THROW(ret = datasrc.invokeDbLostCallback());
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbFailedCallback(datasrc.reconnectCtl()));
EXPECT_FALSE(ret);
EXPECT_FALSE(db_reconnect_ctl_);
}
/// @brief dbLostCallback
///
/// This test verifies that DatabaseConnection::invokeDbLostCallback
-/// safely invokes the registered DbLostCallback. It also tests
+/// safely invokes the registered DbCallback. It also tests
/// operation of DbReconnectCtl retry accounting methods.
TEST_F(DatabaseConnectionCallbackTest, dbLostCallback) {
/// Create a Database configuration that includes the reconnect
pmap[std::string("reconnect-wait-time")] = std::string("60000");
/// Install the callback.
- DatabaseConnection::db_lost_callback =
+ DatabaseConnection::db_lost_callback_ =
std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1);
/// Create the connection..
DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+ bool ret = false;
/// We should be able to invoke the callback and get
/// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ ASSERT_EQ(3, db_reconnect_ctl_->maxRetries());
+ ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Verify that checkRetries() correctly decrements
+ /// down to zero, and that retriesLeft() returns
+ /// the correct value.
+ for (int i = 3; i > 1 ; --i) {
+ ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
+ }
+
+ /// Retries are exhausted, verify that's reflected.
+ EXPECT_FALSE(db_reconnect_ctl_->checkRetries());
+ EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+}
+
+/// @brief dbRecoveredCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbRecoveredCallback
+/// safely invokes the registered DbRecoveredCallback. It also tests
+/// operation of DbReconnectCtl retry reset method.
+TEST_F(DatabaseConnectionCallbackTest, dbRecoveredCallback) {
+ /// Create a Database configuration that includes the reconnect
+ /// control parameters.
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+
+ /// Install the callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1);
+ DatabaseConnection::db_recovered_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbRecoveredCallback, this, ph::_1);
+ /// Create the connection..
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
bool ret = false;
- ASSERT_NO_THROW(ret = datasrc.invokeDbLostCallback());
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
EXPECT_TRUE(ret);
ASSERT_TRUE(db_reconnect_ctl_);
ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
ASSERT_EQ(3, db_reconnect_ctl_->maxRetries());
ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft());
EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
/// Verify that checkRetries() correctly decrements
/// down to zero, and that retriesLeft() returns
EXPECT_FALSE(db_reconnect_ctl_->checkRetries());
EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft());
EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+
+ /// Reset the reconnect ctl object to verify that it is set again.
+ db_reconnect_ctl_.reset();
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbRecoveredCallback(datasrc.reconnectCtl()));
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Retries are reset, verify that's reflected.
+ EXPECT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+}
+
+/// @brief dbFailedCallback
+///
+/// This test verifies that DatabaseConnection::invokeDbFailedCallback
+/// safely invokes the registered DbFailedCallback.
+TEST_F(DatabaseConnectionCallbackTest, dbFailedCallback) {
+ /// Create a Database configuration that includes the reconnect
+ /// control parameters.
+ DatabaseConnection::ParameterMap pmap;
+ pmap[std::string("type")] = std::string("test");
+ pmap[std::string("max-reconnect-tries")] = std::string("3");
+ pmap[std::string("reconnect-wait-time")] = std::string("60000");
+
+ /// Install the callback.
+ DatabaseConnection::db_lost_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbLostCallback, this, ph::_1);
+ DatabaseConnection::db_failed_callback_ =
+ std::bind(&DatabaseConnectionCallbackTest::dbFailedCallback, this, ph::_1);
+ /// Create the connection..
+ DatabaseConnection datasrc(pmap);
+ datasrc.makeReconnectCtl("timer");
+ bool ret = false;
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbLostCallback(datasrc.reconnectCtl()));
+ EXPECT_TRUE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ ASSERT_EQ(3, db_reconnect_ctl_->maxRetries());
+ ASSERT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
+
+ /// Verify that checkRetries() correctly decrements
+ /// down to zero, and that retriesLeft() returns
+ /// the correct value.
+ for (int i = 3; i > 1 ; --i) {
+ ASSERT_EQ(i, db_reconnect_ctl_->retriesLeft());
+ ASSERT_TRUE(db_reconnect_ctl_->checkRetries());
+ }
+
+ /// Retries are exhausted, verify that's reflected.
+ EXPECT_FALSE(db_reconnect_ctl_->checkRetries());
+ EXPECT_EQ(0, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
+
+ /// Reset the reconnect ctl object to verify that it is set again.
+ db_reconnect_ctl_.reset();
+
+ /// We should be able to invoke the callback and get
+ /// the correct reconnect control parameters from it.
+ ASSERT_NO_THROW(ret = DatabaseConnection::invokeDbFailedCallback(datasrc.reconnectCtl()));
+ EXPECT_FALSE(ret);
+ ASSERT_TRUE(db_reconnect_ctl_);
+ ASSERT_EQ("test", db_reconnect_ctl_->backendType());
+ ASSERT_EQ("timer", db_reconnect_ctl_->timerName());
+ EXPECT_EQ(60000, db_reconnect_ctl_->retryInterval());
+
+ /// Retries are reset, verify that's reflected.
+ EXPECT_EQ(3, db_reconnect_ctl_->retriesLeft());
+ EXPECT_EQ(3, db_reconnect_ctl_->maxRetries());
}
// This test checks that a database access string can be parsed correctly.
}
void
-CfgDbAccess::createManagers() const {
+CfgDbAccess::createManagers(const isc::asiolink::IOServicePtr& io_service) const {
// Recreate lease manager.
LeaseMgrFactory::destroy();
- LeaseMgrFactory::create(getLeaseDbAccessString());
+ LeaseMgrFactory::create(getLeaseDbAccessString(), io_service);
// Recreate host data source.
HostMgr::create();
// Restore the host cache.
if (HostDataSourceFactory::registeredFactory("cache")) {
- HostMgr::addBackend("type=cache");
+ HostMgr::addBackend("type=cache", io_service);
}
// Add database backends.
std::list<std::string> host_db_access_list = getHostDbAccessStringList();
for (std::string& hds : host_db_access_list) {
- HostMgr::addBackend(hds);
+ HostMgr::addBackend(hds, io_service);
}
// Check for a host cache.
#ifndef CFG_DBACCESS_H
#define CFG_DBACCESS_H
+#include <asiolink/io_service.h>
#include <cc/cfg_to_element.h>
#include <database/database_connection.h>
/// @brief Creates instance of lease manager and host data sources
/// according to the configuration specified.
- void createManagers() const;
+ ///
+ /// @param IOService object, used for all ASIO operations.
+ void createManagers(const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr()) const;
protected:
source_host->getCfgOption6()->mergeTo(*target_host->getCfgOption6());
}
-CqlHostDataSource::CqlHostDataSource(const CqlConnection::ParameterMap& parameters)
+CqlHostDataSource::CqlHostDataSource(const CqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& /*io_service*/)
: impl_(new CqlHostDataSourceImpl(parameters)) {
}
#ifndef CQL_HOST_DATA_SOURCE_H
#define CQL_HOST_DATA_SOURCE_H
+#include <asiolink/io_service.h>
#include <cql/cql_connection.h>
#include <dhcpsrv/base_host_data_source.h>
/// schema version is invalid.
/// @throw isc::db::DbOperationError An operation on the open database has
/// failed.
- explicit CqlHostDataSource(const db::DatabaseConnection::ParameterMap& parameters);
+ explicit CqlHostDataSource(const db::DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service);
/// @brief Virtual destructor.
///
#include <sstream>
#include <utility>
+using namespace isc::asiolink;
using namespace isc::db;
using namespace std;
void
HostDataSourceFactory::add(HostDataSourceList& sources,
- const string& dbaccess) {
+ const string& dbaccess,
+ const IOServicePtr& io_service) {
// Parse the access string and create a redacted string for logging.
DatabaseConnection::ParameterMap parameters =
DatabaseConnection::parse(dbaccess);
}
// Call the factory and push the pointer on sources.
- sources.push_back(boost::shared_ptr<BaseHostDataSource>(index->second(parameters)));
+ sources.push_back(index->second(parameters, io_service));
// Check the factory did not return NULL.
if (!sources.back()) {
// Factory class method
static HostDataSourcePtr
- factory(const DatabaseConnection::ParameterMap& parameters) {
+ factory(const DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service) {
LOG_INFO(hosts_logger, DHCPSRV_MYSQL_HOST_DB)
.arg(DatabaseConnection::redactedAccessString(parameters));
- return (HostDataSourcePtr(new MySqlHostDataSource(parameters)));
+ return (HostDataSourcePtr(new MySqlHostDataSource(parameters, io_service)));
}
};
// Factory class method
static HostDataSourcePtr
- factory(const DatabaseConnection::ParameterMap& parameters) {
+ factory(const DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service) {
LOG_INFO(hosts_logger, DHCPSRV_PGSQL_HOST_DB)
.arg(DatabaseConnection::redactedAccessString(parameters));
- return (HostDataSourcePtr(new PgSqlHostDataSource(parameters)));
+ return (HostDataSourcePtr(new PgSqlHostDataSource(parameters, io_service)));
}
};
// Factory class method
static HostDataSourcePtr
- factory(const DatabaseConnection::ParameterMap& parameters) {
+ factory(const DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service) {
LOG_INFO(hosts_logger, DHCPSRV_CQL_HOST_DB)
.arg(DatabaseConnection::redactedAccessString(parameters));
- return (HostDataSourcePtr(new CqlHostDataSource(parameters)));
+ return (HostDataSourcePtr(new CqlHostDataSource(parameters, io_service)));
}
};
#ifndef HOST_DATA_SOURCE_FACTORY_H
#define HOST_DATA_SOURCE_FACTORY_H
+#include <asiolink/io_service.h>
#include <database/database_connection.h>
#include <dhcpsrv/base_host_data_source.h>
#include <exceptions/exceptions.h>
/// "keyword=value" pairs, separated by spaces. They are backend-
/// -end specific, although must include the "type" keyword which
/// gives the backend in use.
+ /// @param io_service The IOService object, used for all ASIO operations.
///
/// @throw isc::InvalidParameter dbaccess string does not contain the "type"
/// keyword.
/// @throw isc::dhcp::InvalidType The "type" keyword in dbaccess does not
/// identify a supported backend.
- static void add(HostDataSourceList& sources, const std::string& dbaccess);
+ static void add(HostDataSourceList& sources, const std::string& dbaccess,
+ const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr());
/// @brief Delete a host data source.
///
///
/// A factory takes a parameter map and returns a pointer to a host
/// data source. In case of failure it must throw and not return NULL.
- typedef std::function<HostDataSourcePtr (const db::DatabaseConnection::ParameterMap&)> Factory;
+ typedef std::function<HostDataSourcePtr (const db::DatabaseConnection::ParameterMap&,
+ const isc::asiolink::IOServicePtr&)> Factory;
/// @brief Register a host data source factory
///
}
void
-HostMgr::addBackend(const std::string& access) {
- HostDataSourceFactory::add(getHostMgrPtr()->alternate_sources_, access);
+HostMgr::addBackend(const std::string& access, const IOServicePtr& io_service) {
+ HostDataSourceFactory::add(getHostMgrPtr()->alternate_sources_, access, io_service);
}
bool
#ifndef HOST_MGR_H
#define HOST_MGR_H
+#include <asiolink/io_service.h>
#include <database/database_connection.h>
#include <dhcpsrv/base_host_data_source.h>
#include <dhcpsrv/cache_host_data_source.h>
///
/// @param access Host backend access parameters for the alternate
/// host backend. It holds "keyword=value" pairs, separated by spaces.
+ /// @param io_service The IOService object, used for all ASIO operations.
+ ///
/// The supported values are specific to the alternate backend in use.
/// However, the "type" parameter will be common and it will specify which
/// backend is to be used. Currently, no parameters are supported
/// and the parameter is ignored.
- static void addBackend(const std::string& access);
+ static void addBackend(const std::string& access,
+ const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr());
/// @brief Delete an alternate host backend (aka host data source).
///
}
void
-LeaseMgrFactory::create(const std::string& dbaccess) {
+LeaseMgrFactory::create(const std::string& dbaccess,
+ const isc::asiolink::IOServicePtr& io_service) {
const std::string type = "type";
// Parse the access string and create a redacted string for logging.
if (parameters[type] == string("mysql")) {
#ifdef HAVE_MYSQL
LOG_INFO(dhcpsrv_logger, DHCPSRV_MYSQL_DB).arg(redacted);
- getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters));
+ getLeaseMgrPtr().reset(new MySqlLeaseMgr(parameters, io_service));
return;
#else
LOG_ERROR(dhcpsrv_logger, DHCPSRV_UNKNOWN_DB).arg("mysql");
if (parameters[type] == string("postgresql")) {
#ifdef HAVE_PGSQL
LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_DB).arg(redacted);
- getLeaseMgrPtr().reset(new PgSqlLeaseMgr(parameters));
+ getLeaseMgrPtr().reset(new PgSqlLeaseMgr(parameters, io_service));
return;
#else
LOG_ERROR(dhcpsrv_logger, DHCPSRV_UNKNOWN_DB).arg("postgresql");
#ifndef LEASE_MGR_FACTORY_H
#define LEASE_MGR_FACTORY_H
+#include <asiolink/io_service.h>
#include <database/database_connection.h>
#include <dhcpsrv/lease_mgr.h>
#include <exceptions/exceptions.h>
/// "keyword=value" pairs, separated by spaces. They are backend-
/// -end specific, although must include the "type" keyword which
/// gives the backend in use.
+ /// @param io_service The IOService object, used for all ASIO operations.
///
/// @throw isc::InvalidParameter dbaccess string does not contain the "type"
/// keyword.
/// @throw isc::dhcp::InvalidType The "type" keyword in dbaccess does not
/// identify a supported backend.
- static void create(const std::string& dbaccess);
+ static void create(const std::string& dbaccess,
+ const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr());
/// @brief Destroy lease manager
///
/// is encapsulated in this method to avoid a "static initialization
/// fiasco" if defined in an external static variable.
static boost::scoped_ptr<LeaseMgr>& getLeaseMgrPtr();
-
};
-}; // end of isc::dhcp namespace
-}; // end of isc namespace
+} // end of isc::dhcp namespace
+} // end of isc namespace
#endif // LEASE_MGR_FACTORY_H
///
/// This constructor opens database connection and initializes prepared
/// statements used in the queries.
- MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters);
+ MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service);
/// @brief Destructor.
~MySqlHostDataSourceImpl();
// If running in single-threaded mode, there's nothing to do here.
}
-MySqlHostDataSourceImpl::MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
+MySqlHostDataSourceImpl::MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service)
: parameters_(parameters), ip_reservations_unique_(true) {
// Validate the schema version first.
}
}
-MySqlHostDataSource::MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
- : impl_(new MySqlHostDataSourceImpl(parameters)) {
+MySqlHostDataSource::MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service)
+ : impl_(new MySqlHostDataSourceImpl(parameters, io_service)) {
}
MySqlHostDataSource::~MySqlHostDataSource() {
/// schema version is invalid.
/// @throw isc::db::DbOperationError An operation on the open database has
/// failed.
- MySqlHostDataSource(const db::DatabaseConnection::ParameterMap& parameters);
+ MySqlHostDataSource(const db::DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service);
/// @brief Virtual destructor.
///
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
+#include <dhcpsrv/cfg_db_access.h>
+#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/mysql_lease_mgr.h>
+#include <dhcpsrv/timer_mgr.h>
#include <mysql/mysql_connection.h>
#include <util/multi_threading_mgr.h>
// MySqlLeaseContext Constructor
-MySqlLeaseContext::MySqlLeaseContext(const DatabaseConnection::ParameterMap& parameters)
- : conn_(parameters) {
+MySqlLeaseContext::MySqlLeaseContext(const MySqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service,
+ DbCallback callback)
+ : conn_(parameters, io_service, callback) {
}
// MySqlLeaseContextAlloc Constructor and Destructor
// MySqlLeaseMgr Constructor and Destructor
-MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
- : parameters_(parameters) {
+MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters,
+ const IOServicePtr& io_service)
+ : parameters_(parameters), io_service_(io_service) {
// Validate schema version first.
std::pair<uint32_t, uint32_t> code_version(MYSQL_SCHEMA_VERSION_MAJOR,
// Create an initial context.
pool_.reset(new MySqlLeaseContextPool());
pool_->pool_.push_back(createContext());
+
+ auto db_reconnect_ctl = pool_->pool_[0]->conn_.reconnectCtl();
+
+ std::string manager = "MySqlLeaseMgr[";
+ manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+ std::string timer_name = manager + "]DbReconnectTimer";
+
+ TimerMgr::instance()->registerTimer(timer_name,
+ std::bind(&MySqlLeaseMgr::dbReconnect, db_reconnect_ctl),
+ db_reconnect_ctl->retryInterval(),
+ asiolink::IntervalTimer::ONE_SHOT);
}
MySqlLeaseMgr::~MySqlLeaseMgr() {
+ std::string manager = "MySqlLeaseMgr[";
+ manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+ std::string timer_name = manager + "]DbReconnectTimer";
+
+ TimerMgr::instance()->unregisterTimer(timer_name);
+}
+
+bool
+MySqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
+ MultiThreadingCriticalSection cs;
+
+ DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl);
+
+ bool reopened = false;
+
+ // At least one connection was lost.
+ try {
+ CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
+ LeaseMgrFactory::destroy();
+ LeaseMgrFactory::create(cfg_db->getLeaseDbAccessString()/*, io_service_ */);
+
+ reopened = true;
+ } catch (const std::exception& ex) {
+ //LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_DB_RECONNECT_ATTEMPT_FAILED)
+ // .arg(ex.what());
+ }
+
+ if (reopened) {
+ // Cancel the timer.
+ const std::string& timer_name = db_reconnect_ctl->timerName();
+ TimerMgr::instance()->cancel(timer_name);
+
+ DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl);
+ } else {
+ if (!db_reconnect_ctl->checkRetries()) {
+ // We're out of retries, log it and initiate shutdown.
+ //LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_DB_RECONNECT_RETRIES_EXHAUSTED)
+ // .arg(db_reconnect_ctl->maxRetries());
+
+ DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl);
+
+ return (false);
+ }
+
+ //LOG_INFO(dhcpsrv_logger, DHCPSRV_MYSQL_DB_RECONNECT_ATTEMPT_SCHEDULE)
+ // .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
+ // .arg(db_reconnect_ctl->maxRetries())
+ // .arg(db_reconnect_ctl->retryInterval());
+
+ TimerMgr::instance()->setup(db_reconnect_ctl->timerName());
+ }
+
+ return (true);
}
// Create context.
MySqlLeaseContextPtr
MySqlLeaseMgr::createContext() const {
- MySqlLeaseContextPtr ctx(new MySqlLeaseContext(parameters_));
+ MySqlLeaseContextPtr ctx(new MySqlLeaseContext(parameters_, io_service_,
+ &MySqlLeaseMgr::dbReconnect));
// Open the database.
ctx->conn_.openDatabase();
ctx->exchange4_.reset(new MySqlLease4Exchange());
ctx->exchange6_.reset(new MySqlLease6Exchange());
+ std::string manager = "MySqlLeaseMgr[";
+ manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+ std::string timer_name = manager + "]DbReconnectTimer";
+
+ ctx->conn_.makeReconnectCtl(timer_name);
+
return (ctx);
}
#ifndef MYSQL_LEASE_MGR_H
#define MYSQL_LEASE_MGR_H
+#include <asiolink/io_service.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/dhcpsrv_exceptions.h>
#include <dhcpsrv/lease_mgr.h>
/// @brief Constructor
///
/// @param parameters See MySqlLeaseMgr constructor.
- MySqlLeaseContext(const db::DatabaseConnection::ParameterMap& parameters);
+ /// @param io_service The IOService object, used for all ASIO operations.
+ MySqlLeaseContext(const db::DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service,
+ db::DbCallback callback);
/// The exchange objects are used for transfer of data to/from the database.
/// They are pointed-to objects as the contents may change in "const" calls,
///
/// @param parameters A data structure relating keywords and values
/// concerned with the database.
+ /// @param io_service The IOService object, used for all ASIO operations.
///
/// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
/// @throw isc::db::DbOpenError Error opening the database or the schema
/// version is incorrect.
/// @throw isc::db::DbOperationError An operation on the open database has
/// failed.
- MySqlLeaseMgr(const db::DatabaseConnection::ParameterMap& parameters);
+ MySqlLeaseMgr(const db::DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr());
/// @brief Destructor (closes database)
virtual ~MySqlLeaseMgr();
/// failed.
MySqlLeaseContextPtr createContext() const;
+ /// @brief Attempts to reconnect the server to the lease DB backend manager.
+ ///
+ /// This is a self-rescheduling function that attempts to reconnect to the
+ /// server's lease DB backends after connectivity to one or more have been
+ /// lost. Upon entry it will attempt to reconnect via
+ /// @ref LeaseMgrFactory::create.
+ /// If this is successful, DHCP servicing is re-enabled and server returns
+ /// to normal operation.
+ ///
+ /// If reconnection fails and the maximum number of retries has not been
+ /// exhausted, it will schedule a call to itself to occur at the
+ /// configured retry interval. DHCP service remains disabled.
+ ///
+ /// If the maximum number of retries has been exhausted an error is logged
+ /// and the server shuts down.
+ ///
+ /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
+ /// configured reconnect parameters.
+ /// @return true if connection has been recovered, false otherwise.
+ static bool dbReconnect(db::ReconnectCtlPtr db_reconnect_ctl);
+
/// @brief Local version of getDBVersion() class method
static std::string getDBVersion();
/// @brief The pool of contexts
MySqlLeaseContextPoolPtr pool_;
+
+ /// @brief IOService object, used for all ASIO operations.
+ isc::asiolink::IOServicePtr io_service_;
};
} // namespace dhcp
///
/// This constructor opens database connection and initializes prepared
/// statements used in the queries.
- PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters);
+ PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service);
/// @brief Destructor.
~PgSqlHostDataSourceImpl();
// If running in single-threaded mode, there's nothing to do here.
}
-PgSqlHostDataSourceImpl::PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
+PgSqlHostDataSourceImpl::PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service)
: parameters_(parameters), ip_reservations_unique_(true) {
// Validate the schema version first.
/*********** PgSqlHostDataSource *********************/
-PgSqlHostDataSource::PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters)
- : impl_(new PgSqlHostDataSourceImpl(parameters)) {
+PgSqlHostDataSource::PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service)
+ : impl_(new PgSqlHostDataSourceImpl(parameters, io_service)) {
}
PgSqlHostDataSource::~PgSqlHostDataSource() {
/// @throw isc::db::DbOpenError Error opening the database
/// @throw isc::db::DbOperationError An operation on the open database has
/// failed.
- PgSqlHostDataSource(const db::DatabaseConnection::ParameterMap& parameters);
+ PgSqlHostDataSource(const db::DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service);
/// @brief Virtual destructor.
/// Frees database resources and closes the database connection through
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
+#include <dhcpsrv/cfg_db_access.h>
+#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/dhcpsrv_log.h>
#include <dhcpsrv/dhcpsrv_exceptions.h>
+#include <dhcpsrv/lease_mgr_factory.h>
#include <dhcpsrv/pgsql_lease_mgr.h>
+#include <dhcpsrv/timer_mgr.h>
#include <util/multi_threading_mgr.h>
#include <boost/make_shared.hpp>
// PgSqlLeaseContext Constructor
-PgSqlLeaseContext::PgSqlLeaseContext(const DatabaseConnection::ParameterMap& parameters)
- : conn_(parameters) {
+PgSqlLeaseContext::PgSqlLeaseContext(const PgSqlConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service,
+ DbCallback callback)
+ : conn_(parameters, io_service, callback) {
}
// PgSqlLeaseContextAlloc Constructor and Destructor
// PgSqlLeaseMgr Constructor and Destructor
-PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
- : parameters_(parameters) {
+PgSqlLeaseMgr::PgSqlLeaseMgr(const PgSqlConnection::ParameterMap& parameters,
+ const IOServicePtr& io_service)
+ : parameters_(parameters), io_service_(io_service) {
// Validate schema version first.
std::pair<uint32_t, uint32_t> code_version(PG_SCHEMA_VERSION_MAJOR,
// Create an initial context.
pool_.reset(new PgSqlLeaseContextPool());
pool_->pool_.push_back(createContext());
+
+ auto db_reconnect_ctl = pool_->pool_[0]->conn_.reconnectCtl();
+
+ std::string manager = "PgSqlLeaseMgr[";
+ manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+ std::string timer_name = manager + "]DbReconnectTimer";
+
+ TimerMgr::instance()->registerTimer(timer_name,
+ std::bind(&PgSqlLeaseMgr::dbReconnect, db_reconnect_ctl),
+ db_reconnect_ctl->retryInterval(),
+ asiolink::IntervalTimer::ONE_SHOT);
}
PgSqlLeaseMgr::~PgSqlLeaseMgr() {
+ std::string manager = "PgSqlLeaseMgr[";
+ manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+ std::string timer_name = manager + "]DbReconnectTimer";
+
+ TimerMgr::instance()->unregisterTimer(timer_name);
+}
+
+bool
+PgSqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) {
+ MultiThreadingCriticalSection cs;
+
+ DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl);
+
+ bool reopened = false;
+
+ // At least one connection was lost.
+ try {
+ CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess();
+ LeaseMgrFactory::destroy();
+ LeaseMgrFactory::create(cfg_db->getLeaseDbAccessString()/*, io_service_ */);
+
+ reopened = true;
+ } catch (const std::exception& ex) {
+ //LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_DB_RECONNECT_ATTEMPT_FAILED)
+ // .arg(ex.what());
+ }
+
+ if (reopened) {
+ // Cancel the timer.
+ const std::string& timer_name = db_reconnect_ctl->timerName();
+ TimerMgr::instance()->cancel(timer_name);
+
+ DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl);
+ } else {
+ if (!db_reconnect_ctl->checkRetries()) {
+ // We're out of retries, log it and initiate shutdown.
+ //LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_DB_RECONNECT_RETRIES_EXHAUSTED)
+ // .arg(db_reconnect_ctl->maxRetries());
+
+ DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl);
+
+ return (false);
+ }
+
+ //LOG_INFO(dhcpsrv_logger, DHCPSRV_PGSQL_DB_RECONNECT_ATTEMPT_SCHEDULE)
+ // .arg(db_reconnect_ctl->maxRetries() - db_reconnect_ctl->retriesLeft() + 1)
+ // .arg(db_reconnect_ctl->maxRetries())
+ // .arg(db_reconnect_ctl->retryInterval());
+
+ TimerMgr::instance()->setup(db_reconnect_ctl->timerName());
+ }
+
+ return (true);
}
// Create context.
PgSqlLeaseContextPtr
PgSqlLeaseMgr::createContext() const {
- PgSqlLeaseContextPtr ctx(new PgSqlLeaseContext(parameters_));
+ PgSqlLeaseContextPtr ctx(new PgSqlLeaseContext(parameters_, io_service_,
+ &PgSqlLeaseMgr::dbReconnect));
// Open the database.
ctx->conn_.openDatabase();
ctx->exchange4_.reset(new PgSqlLease4Exchange());
ctx->exchange6_.reset(new PgSqlLease6Exchange());
+ std::string manager = "PgSqlLeaseMgr[";
+ manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+ std::string timer_name = manager + "]DbReconnectTimer";
+
+ ctx->conn_.makeReconnectCtl(timer_name);
+
return (ctx);
}
#ifndef PGSQL_LEASE_MGR_H
#define PGSQL_LEASE_MGR_H
+#include <asiolink/io_service.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/dhcpsrv_exceptions.h>
#include <dhcpsrv/lease_mgr.h>
/// @brief Constructor
///
/// @param parameters See PgSqlLeaseMgr constructor.
- PgSqlLeaseContext(const db::DatabaseConnection::ParameterMap& parameters);
+ /// @param io_service The IOService object, used for all ASIO operations.
+ PgSqlLeaseContext(const db::DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service,
+ db::DbCallback callback);
/// The exchange objects are used for transfer of data to/from the database.
/// They are pointed-to objects as the contents may change in "const" calls,
///
/// @param parameters A data structure relating keywords and values
/// concerned with the database.
+ /// @param io_service The IOService object, used for all ASIO operations.
///
/// @throw isc::dhcp::NoDatabaseName Mandatory database name not given
/// @throw isc::db::DbOpenError Error opening the database or the schema
/// version is incorrect.
/// @throw isc::db::DbOperationError An operation on the open database has
/// failed.
- PgSqlLeaseMgr(const db::DatabaseConnection::ParameterMap& parameters);
+ PgSqlLeaseMgr(const db::DatabaseConnection::ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr());
/// @brief Destructor (closes database)
virtual ~PgSqlLeaseMgr();
/// failed.
PgSqlLeaseContextPtr createContext() const;
+ /// @brief Attempts to reconnect the server to the lease DB backend manager.
+ ///
+ /// This is a self-rescheduling function that attempts to reconnect to the
+ /// server's lease DB backends after connectivity to one or more have been
+ /// lost. Upon entry it will attempt to reconnect via
+ /// @ref LeaseMgrFactory::create.
+ /// If this is successful, DHCP servicing is re-enabled and server returns
+ /// to normal operation.
+ ///
+ /// If reconnection fails and the maximum number of retries has not been
+ /// exhausted, it will schedule a call to itself to occur at the
+ /// configured retry interval. DHCP service remains disabled.
+ ///
+ /// If the maximum number of retries has been exhausted an error is logged
+ /// and the server shuts down.
+ ///
+ /// @param db_reconnect_ctl pointer to the ReconnectCtl containing the
+ /// configured reconnect parameters.
+ /// @return true if connection has been recovered, false otherwise.
+ static bool dbReconnect(db::ReconnectCtlPtr db_reconnect_ctl);
+
/// @brief Local version of getDBVersion() class method
static std::string getDBVersion();
/// @brief The pool of contexts
PgSqlLeaseContextPoolPtr pool_;
+
+ /// @brief IOService object, used for all ASIO operations.
+ isc::asiolink::IOServicePtr io_service_;
};
} // namespace dhcp
// Create host data source which accepts setting non-unique IP
// reservations.
MemHostDataSourcePtr hds(new MemHostDataSource());
- auto testFactory = [hds](const DatabaseConnection::ParameterMap&) {
+ auto testFactory = [hds](const DatabaseConnection::ParameterMap&,
+ const IOServicePtr&) {
return (hds);
- };
+ };
HostDataSourceFactory::registerFactory("test", testFactory);
HostMgr::addBackend("type=test");
// Create host data source which does not accept setting IP reservations
// non-unique setting.
NonUniqueHostDataSourcePtr hds(new NonUniqueHostDataSource());
- auto testFactory = [hds](const DatabaseConnection::ParameterMap&) {
+ auto testFactory = [hds](const DatabaseConnection::ParameterMap&,
+ const IOServicePtr&) {
return (hds);
- };
+ };
HostDataSourceFactory::registerFactory("test", testFactory);
HostMgr::addBackend("type=test");
// Create host data source which accepts setting non-unique IP
// reservations.
MemHostDataSourcePtr hds(new MemHostDataSource());
- auto testFactory = [hds](const DatabaseConnection::ParameterMap&) {
+ auto testFactory = [hds](const DatabaseConnection::ParameterMap&,
+ const IOServicePtr&) {
return (hds);
- };
+ };
HostDataSourceFactory::registerFactory("test", testFactory);
HostMgr::addBackend("type=test");
// Create host data source which does not accept setting IP reservations
// non-unique setting.
NonUniqueHostDataSourcePtr hds(new NonUniqueHostDataSource());
- auto testFactory = [hds](const DatabaseConnection::ParameterMap&) {
+ auto testFactory = [hds](const DatabaseConnection::ParameterMap&,
+ const IOServicePtr&) {
return (hds);
- };
+ };
HostDataSourceFactory::registerFactory("test", testFactory);
HostMgr::addBackend("type=test");
void
LeaseMgrDbLostCallbackTest::testNoCallbackOnOpenFailure() {
- DatabaseConnection::db_lost_callback =
+ DatabaseConnection::db_lost_callback_ =
std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
callback_called_ = false;
void
LeaseMgrDbLostCallbackTest::testDbLostCallback() {
// Set the connectivity lost callback.
- DatabaseConnection::db_lost_callback =
+ DatabaseConnection::db_lost_callback_ =
std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
// Find the most recently opened socket. Our SQL client's socket should
class LeaseMgrDbLostCallbackTest : public ::testing::Test {
public:
LeaseMgrDbLostCallbackTest() {
- db::DatabaseConnection::db_lost_callback = 0;
+ db::DatabaseConnection::db_lost_callback_ = 0;
}
virtual ~LeaseMgrDbLostCallbackTest() {
- db::DatabaseConnection::db_lost_callback = 0;
+ db::DatabaseConnection::db_lost_callback_ = 0;
}
/// @brief Prepares the class for a test.
// Host cache.
hcptr_.reset(new TestHostCache());
- auto cacheFactory = [this](const DatabaseConnection::ParameterMap&) {
+ auto cacheFactory = [this](const DatabaseConnection::ParameterMap&,
+ const isc::asiolink::IOServicePtr&) {
return (hcptr_);
};
HostDataSourceFactory::registerFactory("cache", cacheFactory);
// Host data source.
memptr_.reset(new TestHostDataSource());
- auto testFactory = [this](const DatabaseConnection::ParameterMap&) {
+ auto testFactory = [this](const DatabaseConnection::ParameterMap&,
+ const isc::asiolink::IOServicePtr&) {
return (memptr_);
};
HostDataSourceFactory::registerFactory("test", testFactory);
// No cache.
ncptr_.reset(new TestNoCache());
- auto nocacheFactory = [this](const DatabaseConnection::ParameterMap&) {
+ auto nocacheFactory = [this](const DatabaseConnection::ParameterMap&,
+ const isc::asiolink::IOServicePtr&) {
return (ncptr_);
};
HostDataSourceFactory::registerFactory("nocache", nocacheFactory);
// One backend.
obptr_.reset(new TestOneBackend());
- auto oneFactory = [this](const DatabaseConnection::ParameterMap&) {
+ auto oneFactory = [this](const DatabaseConnection::ParameterMap&,
+ const isc::asiolink::IOServicePtr&) {
return (obptr_);
};
HostDataSourceFactory::registerFactory("one", oneFactory);
#include <config.h>
+#include <asiolink/io_service.h>
#include <dhcpsrv/host_data_source_factory.h>
#include <dhcpsrv/testutils/memory_host_data_source.h>
#include <exceptions/exceptions.h>
using namespace std;
using namespace isc;
+using namespace isc::asiolink;
using namespace isc::db;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
// @brief Factory of mem1
HostDataSourcePtr
-mem1Factory(const DatabaseConnection::ParameterMap&) {
+mem1Factory(const DatabaseConnection::ParameterMap&, const IOServicePtr&) {
return (HostDataSourcePtr(new Mem1HostDataSource()));
}
// @brief Factory of mem2
HostDataSourcePtr
-mem2Factory(const DatabaseConnection::ParameterMap&) {
+mem2Factory(const DatabaseConnection::ParameterMap&, const IOServicePtr&) {
return (HostDataSourcePtr(new Mem2HostDataSource()));
}
}
// @brief Factory function returning 0
-HostDataSourcePtr factory0(const DatabaseConnection::ParameterMap&) {
+HostDataSourcePtr
+factory0(const DatabaseConnection::ParameterMap&, const IOServicePtr&) {
return (HostDataSourcePtr());
}
/// appropriate schema and create a basic host manager to
/// wipe out any prior instance
virtual void SetUp() {
- DatabaseConnection::db_lost_callback = 0;
+ DatabaseConnection::db_lost_callback_ = 0;
// Ensure we have the proper schema with no transient data.
createSchema();
// Wipe out any pre-existing mgr
/// Invoked by gtest upon test exit, we destroy the schema
/// we created.
virtual void TearDown() {
- DatabaseConnection::db_lost_callback = 0;
+ DatabaseConnection::db_lost_callback_ = 0;
// If data wipe enabled, delete transient data otherwise destroy the schema
destroySchema();
}
HostMgr::create();
// Set the connectivity lost callback.
- DatabaseConnection::db_lost_callback =
+ DatabaseConnection::db_lost_callback_ =
std::bind(&HostMgrDbLostCallbackTest::db_lost_callback, this, ph::_1);
// Find the most recently opened socket. Our SQL client's socket should
#include <config.h>
+#include <asiolink/io_service.h>
#include <dhcpsrv/testutils/memory_host_data_source.h>
+using namespace isc::asiolink;
using namespace isc::db;
using namespace std;
}
HostDataSourcePtr
-memFactory(const DatabaseConnection::ParameterMap& /*parameters*/) {
+memFactory(const DatabaseConnection::ParameterMap& /*parameters*/,
+ const IOServicePtr& /*io_service*/) {
return (HostDataSourcePtr(new MemHostDataSource()));
}
/// @param parameters
/// @return A pointer to a base host data source instance.
HostDataSourcePtr
-memFactory(const db::DatabaseConnection::ParameterMap& /*parameters*/);
+memFactory(const db::DatabaseConnection::ParameterMap& /*parameters*/,
+ const isc::asiolink::IOServicePtr& /*io_service*/);
} // namespace isc::dhcp::test
} // namespace isc::dhcp
#ifndef MYSQL_CONNECTION_H
#define MYSQL_CONNECTION_H
+#include <asiolink/io_service.h>
#include <database/database_connection.h>
#include <database/db_exceptions.h>
#include <database/db_log.h>
/// @brief Constructor
///
/// Initialize MySqlConnection object with parameters needed for connection.
- MySqlConnection(const ParameterMap& parameters)
- : DatabaseConnection(parameters) {
+ MySqlConnection(const ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr(),
+ DbCallback callback = DbCallback())
+ : DatabaseConnection(parameters), io_service_(io_service),
+ callback_(callback) {
}
/// @brief Destructor
/// @tparam StatementIndex Type of the statement index enum.
///
/// @param index Index of the query to be executed.
- /// @param in_bindings Input bindings holding values to substitue placeholders
+ /// @param in_bindings Input bindings holding values to substitute placeholders
/// in the query.
///
/// @return Number of affected rows.
///
/// If the error is recoverable, the function will throw a DbOperationError.
/// If the error is deemed unrecoverable, such as a loss of connectivity
- /// with the server, the function will call invokeDbLostCallback(). If the
- /// invocation returns false then either there is no callback registered
- /// or the callback has elected not to attempt to reconnect, and a
- /// DbUnrecoverableError is thrown.
+ /// with the server, the function will call startRecoverDbConnection() which
+ /// will start the connection recovery.
///
/// If the invocation returns true, this indicates the calling layer will
/// attempt recovery, and the function throws a DbOperationError to allow
.arg(mysql_error(mysql_))
.arg(mysql_errno(mysql_));
- // Mark the connection as no longer usuable.
+ // Mark this connection as no longer usable.
markUnusable();
- // If there's no lost db callback or it returns false,
- // then we're not attempting to recover so we're done.
- if (!invokeDbLostCallback()) {
- isc_throw(db::DbUnrecoverableError,
- "database connectivity cannot be recovered");
- }
+ // Start the connection recovery.
+ startRecoverDbConnection();
// We still need to throw so caller can error out of the current
// processing.
isc_throw(db::DbConnectionUnusable,
- "fatal database errror or connectivity lost");
+ "fatal database error or connectivity lost");
default:
// Connection is ok, so it must be an SQL error
isc_throw(db::DbOperationError, what << " for <"
}
}
+ /// @brief The recover connection
+ ///
+ /// This function starts the recover process of the connection.
+ ///
+ /// @note The recover function must be run on the IO Service thread.
+ void startRecoverDbConnection() {
+ if (callback_) {
+ io_service_->post(std::bind(callback_, reconnectCtl()));
+ }
+ }
+
/// @brief Prepared statements
///
/// This field is public, because it is used heavily from MySqlConnection
/// This field is public, because it is used heavily from MySqlConnection
/// and will be from MySqlHostDataSource.
MySqlHolder mysql_;
+
+ /// @brief IOService object, used for all ASIO operations.
+ isc::asiolink::IOServicePtr io_service_;
+
+ /// @brief The callback used to recover the connection.
+ DbCallback callback_;
};
-}; // end of isc::db namespace
-}; // end of isc namespace
+} // end of isc::db namespace
+} // end of isc namespace
#endif // MYSQL_CONNECTION_H
// Deallocate the prepared queries.
if (PQstatus(conn_) == CONNECTION_OK) {
PgSqlResult r(PQexec(conn_, "DEALLOCATE all"));
- if(PQresultStatus(r) != PGRES_COMMAND_OK) {
+ if (PQresultStatus(r) != PGRES_COMMAND_OK) {
// Highly unlikely but we'll log it and go on.
DB_LOG_ERROR(PGSQL_DEALLOC_ERROR)
.arg(PQerrorMessage(conn_));
// Prepare all statements queries with all known fields datatype
PgSqlResult r(PQprepare(conn_, statement.name, statement.text,
statement.nbparams, statement.types));
- if(PQresultStatus(r) != PGRES_COMMAND_OK) {
+ if (PQresultStatus(r) != PGRES_COMMAND_OK) {
isc_throw(DbOperationError, "unable to prepare PostgreSQL statement: "
<< statement.text << ", reason: " << PQerrorMessage(conn_));
}
// misleadingly returned as fatal. However, a loss of connectivity
// can lead to a NULL sqlstate with a status of PGRES_FATAL_ERROR.
const char* sqlstate = PQresultErrorField(r, PG_DIAG_SQLSTATE);
- if ((sqlstate == NULL) ||
+ if ((sqlstate == NULL) ||
((memcmp(sqlstate, "08", 2) == 0) || // Connection Exception
(memcmp(sqlstate, "53", 2) == 0) || // Insufficient resources
(memcmp(sqlstate, "54", 2) == 0) || // Program Limit exceeded
// Mark this connection as no longer usable.
markUnusable();
- // If there's no lost db callback or it returns false,
- // then we're not attempting to recover so we're done.
- if (!invokeDbLostCallback()) {
- isc_throw(db::DbUnrecoverableError,
- "database connectivity cannot be recovered");
- }
+ // Start the connection recovery.
+ startRecoverDbConnection();
// We still need to throw so caller can error out of the current
// processing.
// Apparently it wasn't fatal, so we throw with a helpful message.
const char* error_message = PQerrorMessage(conn_);
isc_throw(DbOperationError, "Statement exec failed:" << " for: "
- << statement.name << ", status: " << s
- << "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
- << "], reason: " << error_message);
+ << statement.name << ", status: " << s
+ << "sqlstate:[ " << (sqlstate ? sqlstate : "<null>")
+ << " ], reason: " << error_message);
}
}
}
}
-}; // end of isc::db namespace
-}; // end of isc namespace
+} // end of isc::db namespace
+} // end of isc namespace
#ifndef PGSQL_CONNECTION_H
#define PGSQL_CONNECTION_H
+#include <asiolink/io_service.h>
#include <database/database_connection.h>
#include <libpq-fe.h>
/// @brief Constructor
///
/// Initialize PgSqlConnection object with parameters needed for connection.
- PgSqlConnection(const ParameterMap& parameters)
- : DatabaseConnection(parameters) {
+ PgSqlConnection(const ParameterMap& parameters,
+ const isc::asiolink::IOServicePtr& io_service = isc::asiolink::IOServicePtr(),
+ DbCallback callback = DbCallback())
+ : DatabaseConnection(parameters), io_service_(io_service),
+ callback_(callback) {
}
/// @brief Destructor
///
/// If the error is recoverable, the function will throw a DbOperationError.
/// If the error is deemed unrecoverable, such as a loss of connectivity
- /// with the server, the function will call invokeDbLostCallback(). If the
- /// invocation returns false then either there is no callback registered
- /// or the callback has elected not to attempt to reconnect, and a
- /// DbUnrecoverableError is thrown.
+ /// with the server, the function will call startRecoverDbConnection() which
+ /// will start the connection recovery.
///
/// If the invocation returns true, this indicates the calling layer will
/// attempt recovery, and the function throws a DbOperationError to allow
void checkStatementError(const PgSqlResult& r,
PgSqlTaggedStatement& statement);
+ /// @brief The recover connection
+ ///
+ /// This function starts the recover process of the connection.
+ ///
+ /// @note The recover function must be run on the IO Service thread.
+ void startRecoverDbConnection() {
+ if (callback_) {
+ io_service_->post(std::bind(callback_, reconnectCtl()));
+ }
+ }
+
/// @brief PgSql connection handle
///
/// This field is public, because it is used heavily from PgSqlLeaseMgr
return (conn_);
}
+ /// @brief IOService object, used for all ASIO operations.
+ isc::asiolink::IOServicePtr io_service_;
+
+ /// @brief The callback used to recover the connection.
+ DbCallback callback_;
};
-}; // end of isc::db namespace
-}; // end of isc namespace
+} // end of isc::db namespace
+} // end of isc namespace
#endif // PGSQL_CONNECTION_H