diff options
-rw-r--r-- | src/bin/dhcp4/ctrl_dhcp4_srv.cc | 6 | ||||
-rw-r--r-- | src/bin/dhcp4/json_config_parser.cc | 12 | ||||
-rw-r--r-- | src/bin/dhcp6/ctrl_dhcp6_srv.cc | 6 | ||||
-rw-r--r-- | src/bin/dhcp6/json_config_parser.cc | 12 | ||||
-rw-r--r-- | src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc | 4 | ||||
-rw-r--r-- | src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc | 4 | ||||
-rw-r--r-- | src/lib/dhcpsrv/mysql_host_data_source.cc | 29 | ||||
-rw-r--r-- | src/lib/dhcpsrv/mysql_lease_mgr.cc | 29 | ||||
-rw-r--r-- | src/lib/dhcpsrv/pgsql_host_data_source.cc | 29 | ||||
-rw-r--r-- | src/lib/dhcpsrv/pgsql_lease_mgr.cc | 29 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc | 203 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h | 84 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/host_mgr_unittest.cc | 1 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc | 44 | ||||
-rw-r--r-- | src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc | 44 |
15 files changed, 423 insertions, 113 deletions
diff --git a/src/bin/dhcp4/ctrl_dhcp4_srv.cc b/src/bin/dhcp4/ctrl_dhcp4_srv.cc index 81f8bc3df4..324b6dea6e 100644 --- a/src/bin/dhcp4/ctrl_dhcp4_srv.cc +++ b/src/bin/dhcp4/ctrl_dhcp4_srv.cc @@ -1117,12 +1117,6 @@ ControlledDhcpv4Srv::~ControlledDhcpv4Srv() { CommandMgr::instance().deregisterCommand("status-get"); CommandMgr::instance().deregisterCommand("version-get"); - // TimerMgr uses IO service to run asynchronous timers. - TimerMgr::instance()->setIOService(IOServicePtr()); - - // CommandMgr uses IO service to run asynchronous socket operations. - CommandMgr::instance().setIOService(IOServicePtr()); - // LeaseMgr uses IO service to run asynchronous timers. LeaseMgr::setIOService(IOServicePtr()); diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 48cdcd85b0..6099e74426 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -339,18 +339,6 @@ configureDhcp4Server(Dhcpv4Srv& server, isc::data::ConstElementPtr config_set, // Close DHCP sockets and remove any existing timers. if (!check_only) { - // If mysql or postgresql lease and host managers were configured, they - // need to be destroy before calling unregisterTimers as they are - // responsible for unregistering own connection timers. A memfile lease - // manager and an empty host manager will be created instead. - auto running_cfg = CfgMgr::instance().getCurrentCfg(); - auto parameters = DatabaseConnection::parse(running_cfg->getCfgDbAccess()->getLeaseDbAccessString()); - if (parameters["type"] != "memfile") { - CfgDbAccess cfg_db; - cfg_db.setAppendedParameters("universe=4"); - LeaseMgrFactory::create(cfg_db.getLeaseDbAccessString()); - } - HostMgr::create(); IfaceMgr::instance().closeSockets(); TimerMgr::instance()->unregisterTimers(); server.discardPackets(); diff --git a/src/bin/dhcp6/ctrl_dhcp6_srv.cc b/src/bin/dhcp6/ctrl_dhcp6_srv.cc index eb8317051f..654347ed49 100644 --- a/src/bin/dhcp6/ctrl_dhcp6_srv.cc +++ b/src/bin/dhcp6/ctrl_dhcp6_srv.cc @@ -1136,12 +1136,6 @@ ControlledDhcpv6Srv::~ControlledDhcpv6Srv() { CommandMgr::instance().deregisterCommand("status-get"); CommandMgr::instance().deregisterCommand("version-get"); - // TimerMgr uses IO service to run asynchronous timers. - TimerMgr::instance()->setIOService(IOServicePtr()); - - // CommandMgr uses IO service to run asynchronous socket operations. - CommandMgr::instance().setIOService(IOServicePtr()); - // LeaseMgr uses IO service to run asynchronous timers. LeaseMgr::setIOService(IOServicePtr()); diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index 7022aeda86..1394f048ff 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -442,18 +442,6 @@ configureDhcp6Server(Dhcpv6Srv& server, isc::data::ConstElementPtr config_set, // Close DHCP sockets and remove any existing timers. if (!check_only) { - // If mysql or postgresql lease and host managers were configured, they - // need to be destroy before calling unregisterTimers as they are - // responsible for unregistering own connection timers. A memfile lease - // manager and an empty host manager will be created instead. - auto running_cfg = CfgMgr::instance().getCurrentCfg(); - auto parameters = DatabaseConnection::parse(running_cfg->getCfgDbAccess()->getLeaseDbAccessString()); - if (parameters["type"] != "memfile") { - CfgDbAccess cfg_db; - cfg_db.setAppendedParameters("universe=6"); - LeaseMgrFactory::create(cfg_db.getLeaseDbAccessString()); - } - HostMgr::create(); IfaceMgr::instance().closeSockets(); TimerMgr::instance()->unregisterTimers(); server.discardPackets(); diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index 97c1ffd1d3..df142266a8 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -2341,8 +2341,7 @@ public: if (reopened) { // Cancel the timer. - const std::string& timer_name = db_reconnect_ctl->timerName(); - TimerMgr::instance()->cancel(timer_name); + TimerMgr::instance()->cancel(db_reconnect_ctl->timerName()); DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl); } else { @@ -2361,6 +2360,7 @@ public: .arg(db_reconnect_ctl->maxRetries()) .arg(db_reconnect_ctl->retryInterval()); + // Start the timer. TimerMgr::instance()->setup(db_reconnect_ctl->timerName()); } diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc index 9c56999794..103da095c7 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc @@ -2784,8 +2784,7 @@ public: if (reopened) { // Cancel the timer. - const std::string& timer_name = db_reconnect_ctl->timerName(); - TimerMgr::instance()->cancel(timer_name); + TimerMgr::instance()->cancel(db_reconnect_ctl->timerName()); DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl); } else { @@ -2804,6 +2803,7 @@ public: .arg(db_reconnect_ctl->maxRetries()) .arg(db_reconnect_ctl->retryInterval()); + // Start the timer. TimerMgr::instance()->setup(db_reconnect_ctl->timerName()); } diff --git a/src/lib/dhcpsrv/mysql_host_data_source.cc b/src/lib/dhcpsrv/mysql_host_data_source.cc index 4393e0ee88..63c987239d 100644 --- a/src/lib/dhcpsrv/mysql_host_data_source.cc +++ b/src/lib/dhcpsrv/mysql_host_data_source.cc @@ -2794,13 +2794,6 @@ MySqlHostDataSourceImpl::MySqlHostDataSourceImpl(const DatabaseConnection::Param // Create an initial context. pool_.reset(new MySqlHostContextPool()); pool_->pool_.push_back(createContext()); - - auto db_reconnect_ctl = pool_->pool_[0]->conn_.reconnectCtl(); - - TimerMgr::instance()->registerTimer(timer_name_, - std::bind(&MySqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl), - db_reconnect_ctl->retryInterval(), - asiolink::IntervalTimer::ONE_SHOT); } // Create context. @@ -2848,7 +2841,6 @@ MySqlHostDataSourceImpl::createContext() const { } MySqlHostDataSourceImpl::~MySqlHostDataSourceImpl() { - TimerMgr::instance()->unregisterTimer(timer_name_); } bool @@ -2859,6 +2851,8 @@ MySqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { bool reopened = false; + const std::string timer_name = db_reconnect_ctl->timerName(); + // At least one connection was lost. try { CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess(); @@ -2878,8 +2872,9 @@ MySqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (reopened) { // Cancel the timer. - const std::string& timer_name = db_reconnect_ctl->timerName(); - TimerMgr::instance()->cancel(timer_name); + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl); } else { @@ -2888,6 +2883,11 @@ MySqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_HOST_DB_RECONNECT_FAILED) .arg(db_reconnect_ctl->maxRetries()); + // Cancel the timer. + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } + DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl); return (false); @@ -2898,7 +2898,14 @@ MySqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { .arg(db_reconnect_ctl->maxRetries()) .arg(db_reconnect_ctl->retryInterval()); - TimerMgr::instance()->setup(db_reconnect_ctl->timerName()); + // Start the timer. + if (!TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->registerTimer(timer_name, + std::bind(&MySqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl), + db_reconnect_ctl->retryInterval(), + asiolink::IntervalTimer::ONE_SHOT); + } + TimerMgr::instance()->setup(timer_name); } return (true); diff --git a/src/lib/dhcpsrv/mysql_lease_mgr.cc b/src/lib/dhcpsrv/mysql_lease_mgr.cc index 8cf06ad4f9..38f120a1c8 100644 --- a/src/lib/dhcpsrv/mysql_lease_mgr.cc +++ b/src/lib/dhcpsrv/mysql_lease_mgr.cc @@ -1802,17 +1802,9 @@ MySqlLeaseMgr::MySqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) // Create an initial context. pool_.reset(new MySqlLeaseContextPool()); pool_->pool_.push_back(createContext()); - - auto db_reconnect_ctl = pool_->pool_[0]->conn_.reconnectCtl(); - - TimerMgr::instance()->registerTimer(timer_name_, - std::bind(&MySqlLeaseMgr::dbReconnect, db_reconnect_ctl), - db_reconnect_ctl->retryInterval(), - asiolink::IntervalTimer::ONE_SHOT); } MySqlLeaseMgr::~MySqlLeaseMgr() { - TimerMgr::instance()->unregisterTimer(timer_name_); } bool @@ -1823,6 +1815,8 @@ MySqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { bool reopened = false; + const std::string timer_name = db_reconnect_ctl->timerName(); + // At least one connection was lost. try { CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess(); @@ -1837,8 +1831,9 @@ MySqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (reopened) { // Cancel the timer. - const std::string& timer_name = db_reconnect_ctl->timerName(); - TimerMgr::instance()->cancel(timer_name); + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl); } else { @@ -1847,6 +1842,11 @@ MySqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { LOG_ERROR(dhcpsrv_logger, DHCPSRV_MYSQL_LEASE_DB_RECONNECT_FAILED) .arg(db_reconnect_ctl->maxRetries()); + // Cancel the timer. + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } + DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl); return (false); @@ -1857,7 +1857,14 @@ MySqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { .arg(db_reconnect_ctl->maxRetries()) .arg(db_reconnect_ctl->retryInterval()); - TimerMgr::instance()->setup(db_reconnect_ctl->timerName()); + // Start the timer. + if (!TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->registerTimer(timer_name, + std::bind(&MySqlLeaseMgr::dbReconnect, db_reconnect_ctl), + db_reconnect_ctl->retryInterval(), + asiolink::IntervalTimer::ONE_SHOT); + } + TimerMgr::instance()->setup(timer_name); } return (true); diff --git a/src/lib/dhcpsrv/pgsql_host_data_source.cc b/src/lib/dhcpsrv/pgsql_host_data_source.cc index ea7ef6bf3e..7f0a30cf50 100644 --- a/src/lib/dhcpsrv/pgsql_host_data_source.cc +++ b/src/lib/dhcpsrv/pgsql_host_data_source.cc @@ -2235,13 +2235,6 @@ PgSqlHostDataSourceImpl::PgSqlHostDataSourceImpl(const DatabaseConnection::Param // Create an initial context. pool_.reset(new PgSqlHostContextPool()); pool_->pool_.push_back(createContext()); - - auto db_reconnect_ctl = pool_->pool_[0]->conn_.reconnectCtl(); - - TimerMgr::instance()->registerTimer(timer_name_, - std::bind(&PgSqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl), - db_reconnect_ctl->retryInterval(), - asiolink::IntervalTimer::ONE_SHOT); } // Create context. @@ -2284,7 +2277,6 @@ PgSqlHostDataSourceImpl::createContext() const { } PgSqlHostDataSourceImpl::~PgSqlHostDataSourceImpl() { - TimerMgr::instance()->unregisterTimer(timer_name_); } bool @@ -2295,6 +2287,8 @@ PgSqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { bool reopened = false; + const std::string timer_name = db_reconnect_ctl->timerName(); + // At least one connection was lost. try { CfgDbAccessPtr cfg_db = CfgMgr::instance().getCurrentCfg()->getCfgDbAccess(); @@ -2314,8 +2308,9 @@ PgSqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (reopened) { // Cancel the timer. - const std::string& timer_name = db_reconnect_ctl->timerName(); - TimerMgr::instance()->cancel(timer_name); + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl); } else { @@ -2324,6 +2319,11 @@ PgSqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_HOST_DB_RECONNECT_FAILED) .arg(db_reconnect_ctl->maxRetries()); + // Cancel the timer. + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } + DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl); return (false); @@ -2334,7 +2334,14 @@ PgSqlHostDataSourceImpl::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { .arg(db_reconnect_ctl->maxRetries()) .arg(db_reconnect_ctl->retryInterval()); - TimerMgr::instance()->setup(db_reconnect_ctl->timerName()); + // Start the timer. + if (!TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->registerTimer(timer_name, + std::bind(&PgSqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl), + db_reconnect_ctl->retryInterval(), + asiolink::IntervalTimer::ONE_SHOT); + } + TimerMgr::instance()->setup(timer_name); } return (true); diff --git a/src/lib/dhcpsrv/pgsql_lease_mgr.cc b/src/lib/dhcpsrv/pgsql_lease_mgr.cc index 54aa910a5c..4ed51ad5fe 100644 --- a/src/lib/dhcpsrv/pgsql_lease_mgr.cc +++ b/src/lib/dhcpsrv/pgsql_lease_mgr.cc @@ -1236,17 +1236,9 @@ PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters) // Create an initial context. pool_.reset(new PgSqlLeaseContextPool()); pool_->pool_.push_back(createContext()); - - auto db_reconnect_ctl = pool_->pool_[0]->conn_.reconnectCtl(); - - TimerMgr::instance()->registerTimer(timer_name_, - std::bind(&PgSqlLeaseMgr::dbReconnect, db_reconnect_ctl), - db_reconnect_ctl->retryInterval(), - asiolink::IntervalTimer::ONE_SHOT); } PgSqlLeaseMgr::~PgSqlLeaseMgr() { - TimerMgr::instance()->unregisterTimer(timer_name_); } bool @@ -1255,6 +1247,8 @@ PgSqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { DatabaseConnection::invokeDbLostCallback(db_reconnect_ctl); + const std::string timer_name = db_reconnect_ctl->timerName(); + bool reopened = false; // At least one connection was lost. @@ -1271,8 +1265,9 @@ PgSqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { if (reopened) { // Cancel the timer. - const std::string& timer_name = db_reconnect_ctl->timerName(); - TimerMgr::instance()->cancel(timer_name); + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } DatabaseConnection::invokeDbRecoveredCallback(db_reconnect_ctl); } else { @@ -1281,6 +1276,11 @@ PgSqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { LOG_ERROR(dhcpsrv_logger, DHCPSRV_PGSQL_LEASE_DB_RECONNECT_FAILED) .arg(db_reconnect_ctl->maxRetries()); + // Cancel the timer. + if (TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->unregisterTimer(timer_name); + } + DatabaseConnection::invokeDbFailedCallback(db_reconnect_ctl); return (false); @@ -1291,7 +1291,14 @@ PgSqlLeaseMgr::dbReconnect(ReconnectCtlPtr db_reconnect_ctl) { .arg(db_reconnect_ctl->maxRetries()) .arg(db_reconnect_ctl->retryInterval()); - TimerMgr::instance()->setup(db_reconnect_ctl->timerName()); + // Start the timer. + if (!TimerMgr::instance()->isTimerRegistered(timer_name)) { + TimerMgr::instance()->registerTimer(timer_name, + std::bind(&PgSqlLeaseMgr::dbReconnect, db_reconnect_ctl), + db_reconnect_ctl->retryInterval(), + asiolink::IntervalTimer::ONE_SHOT); + } + TimerMgr::instance()->setup(timer_name); } return (true); diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc index 38da5a309c..b52301e3e7 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc @@ -3274,37 +3274,85 @@ LeaseMgrDbLostCallbackTest::testNoCallbackOnOpenFailure() { DatabaseConnection::db_lost_callback_ = std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); - callback_called_ = false; ASSERT_THROW(LeaseMgrFactory::create(invalidConnectString()), DbOpenError); io_service_->poll(); - EXPECT_FALSE(callback_called_); + EXPECT_EQ(0, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); } void -LeaseMgrDbLostCallbackTest::testDbLostCallback() { +LeaseMgrDbLostCallbackTest::testDbLostAndRecoveredCallback() { // Set the connectivity 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 - // be the next one. - int last_open_socket = findLastSocketFd(); + // Set the connectivity recovered callback. + DatabaseConnection::db_recovered_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); - // Fill holes. - FillFdHoles holes(last_open_socket); + // Set the connectivity failed callback. + DatabaseConnection::db_failed_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); // Connect to the lease backend. - ASSERT_NO_THROW(LeaseMgrFactory::create(validConnectString())); + ASSERT_NO_THROW(LeaseMgrFactory::create(access)); // The most recently opened socket should be for our SQL client. int sql_socket = test::findLastSocketFd(); ASSERT_TRUE(sql_socket > -1); - // Clear the callback invocation marker. - callback_called_ = false; + // Verify we can execute a query. We do not care if + // we find a lease or not. + LeaseMgr& lm = LeaseMgrFactory::instance(); + + Lease4Ptr lease; + ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0"))); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and recovered connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(1, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +LeaseMgrDbLostCallbackTest::testDbLostAndFailedCallback() { + // Set the connectivity lost callback. + DatabaseConnection::db_lost_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + DatabaseConnection::db_recovered_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + DatabaseConnection::db_failed_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); + + // Connect to the lease backend. + ASSERT_NO_THROW(LeaseMgrFactory::create(access)); + + // The most recently opened socket should be for our SQL client. + int sql_socket = test::findLastSocketFd(); + ASSERT_TRUE(sql_socket > -1); // Verify we can execute a query. We do not care if // we find a lease or not. @@ -3313,6 +3361,126 @@ LeaseMgrDbLostCallbackTest::testDbLostCallback() { Lease4Ptr lease; ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0"))); + access = invalidConnectString(); + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost and failed connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(1, db_failed_callback_called_); +} + +void +LeaseMgrDbLostCallbackTest::testDbLostAndRecoveredAfterTimeoutCallback() { + // Set the connectivity lost callback. + DatabaseConnection::db_lost_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + DatabaseConnection::db_recovered_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + DatabaseConnection::db_failed_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + std::string extra = " max-reconnect-tries=2 reconnect-wait-time=1"; + access += extra; + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); + + // Connect to the lease backend. + ASSERT_NO_THROW(LeaseMgrFactory::create(access)); + + // The most recently opened socket should be for our SQL client. + int sql_socket = test::findLastSocketFd(); + ASSERT_TRUE(sql_socket > -1); + + // Verify we can execute a query. We do not care if + // we find a lease or not. + LeaseMgr& lm = LeaseMgrFactory::instance(); + + Lease4Ptr lease; + ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0"))); + + access = invalidConnectString(); + access += extra; + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); + + // Now close the sql socket out from under backend client + ASSERT_EQ(0, close(sql_socket)); + + // A query should fail with DbConnectionUnusable. + ASSERT_THROW(lease = lm.getLease4(IOAddress("192.0.1.0")), + DbConnectionUnusable); + + io_service_->poll(); + + // Our lost connectivity callback should have been invoked. + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + access = validConnectString(); + access += extra; + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); + + sleep(1); + + io_service_->poll(); + + // Our recovered connectivity callback should have been invoked. + EXPECT_EQ(2, db_lost_callback_called_); + EXPECT_EQ(1, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); +} + +void +LeaseMgrDbLostCallbackTest::testDbLostAndFailedAfterTimeoutCallback() { + // Set the connectivity lost callback. + DatabaseConnection::db_lost_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_lost_callback, this, ph::_1); + + // Set the connectivity recovered callback. + DatabaseConnection::db_recovered_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_recovered_callback, this, ph::_1); + + // Set the connectivity failed callback. + DatabaseConnection::db_failed_callback_ = + std::bind(&LeaseMgrDbLostCallbackTest::db_failed_callback, this, ph::_1); + + std::string access = validConnectString(); + std::string extra = " max-reconnect-tries=2 reconnect-wait-time=1"; + access += extra; + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); + + // Connect to the lease backend. + ASSERT_NO_THROW(LeaseMgrFactory::create(access)); + + // The most recently opened socket should be for our SQL client. + int sql_socket = test::findLastSocketFd(); + ASSERT_TRUE(sql_socket > -1); + + // Verify we can execute a query. We do not care if + // we find a lease or not. + LeaseMgr& lm = LeaseMgrFactory::instance(); + + Lease4Ptr lease; + ASSERT_NO_THROW(lease = lm.getLease4(IOAddress("192.0.1.0"))); + + access = invalidConnectString(); + access += extra; + CfgMgr::instance().getCurrentCfg()->getCfgDbAccess()->setLeaseDbAccessString(access); + // Now close the sql socket out from under backend client ASSERT_EQ(0, close(sql_socket)); @@ -3323,7 +3491,18 @@ LeaseMgrDbLostCallbackTest::testDbLostCallback() { io_service_->poll(); // Our lost connectivity callback should have been invoked. - EXPECT_TRUE(callback_called_); + EXPECT_EQ(1, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(0, db_failed_callback_called_); + + sleep(1); + + io_service_->poll(); + + // Our failed connectivity callback should have been invoked. + EXPECT_EQ(2, db_lost_callback_called_); + EXPECT_EQ(0, db_recovered_callback_called_); + EXPECT_EQ(1, db_failed_callback_called_); } void diff --git a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h index 5f29872788..292356dbe9 100644 --- a/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h +++ b/src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.h @@ -9,6 +9,7 @@ #include <asiolink/io_service.h> #include <dhcpsrv/lease_mgr.h> +#include <dhcpsrv/timer_mgr.h> #include <gtest/gtest.h> @@ -526,15 +527,22 @@ public: class LeaseMgrDbLostCallbackTest : public ::testing::Test { public: LeaseMgrDbLostCallbackTest() - : callback_called_(false), + : db_lost_callback_called_(0), db_recovered_callback_called_(0), + db_failed_callback_called_(0), io_service_(boost::make_shared<isc::asiolink::IOService>()) { db::DatabaseConnection::db_lost_callback_ = 0; + db::DatabaseConnection::db_recovered_callback_ = 0; + db::DatabaseConnection::db_failed_callback_ = 0; LeaseMgr::setIOService(io_service_); + TimerMgr::instance()->setIOService(io_service_); } virtual ~LeaseMgrDbLostCallbackTest() { db::DatabaseConnection::db_lost_callback_ = 0; + db::DatabaseConnection::db_recovered_callback_ = 0; + db::DatabaseConnection::db_failed_callback_ = 0; LeaseMgr::setIOService(isc::asiolink::IOServicePtr()); + TimerMgr::instance()->unregisterTimers(); } /// @brief Prepares the class for a test. @@ -549,10 +557,10 @@ public: /// we created and toss our lease manager. virtual void TearDown(); - /// @brief Abstract method for destroying the back end specific shcema + /// @brief Abstract method for destroying the back end specific schema virtual void destroySchema() = 0; - /// @brief Abstract method for creating the back end specific shcema + /// @brief Abstract method for creating the back end specific schema virtual void createSchema() = 0; /// @brief Abstract method which returns the back end specific connection @@ -570,25 +578,83 @@ public: /// open should be handled directly by the application layer. void testNoCallbackOnOpenFailure(); - /// @brief Verifies the host manager's behavior if DB connection is lost + /// @brief Verifies the lease manager's behavior if DB connection is lost /// /// This function creates a lease manager with an back end that /// supports connectivity lost callback (currently only MySQL and /// PostgreSQL currently). It verifies connectivity by issuing a known /// valid query. Next it simulates connectivity lost by identifying and - /// closing the socket connection to the host backend. It then reissues + /// closing the socket connection to the lease backend. It then reissues /// the query and verifies that: /// -# The Query throws DbOperationError (rather than exiting) /// -# The registered DbLostCallback was invoked - void testDbLostCallback(); + /// -# The registered DbRecoveredCallback was invoked + void testDbLostAndRecoveredCallback(); - /// @brief Callback function registered with the host manager + /// @brief Verifies the lease manager's behavior if DB connection is lost + /// + /// This function creates a lease manager with an back end that + /// supports connectivity lost callback (currently only MySQL and + /// PostgreSQL currently). It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifying and + /// closing the socket connection to the lease backend. It then reissues + /// the query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbFailedCallback was invoked + void testDbLostAndFailedCallback(); + + /// @brief Verifies the lease manager's behavior if DB connection is lost + /// + /// This function creates a lease manager with an back end that + /// supports connectivity lost callback (currently only MySQL and + /// PostgreSQL currently). It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifyingLost and + /// closing the socket connection to the lease backend. It then reissues + /// the query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbRecoveredCallback was invoked after two reconnect + /// attempts (once failing and second triggered by timer) + void testDbLostAndRecoveredAfterTimeoutCallback(); + + /// @brief Verifies the lease manager's behavior if DB connection is lost + /// + /// This function creates a lease manager with an back end that + /// supports connectivity lost callback (currently only MySQL and + /// PostgreSQL currently). It verifies connectivity by issuing a known + /// valid query. Next it simulates connectivity lost by identifyingLost and + /// closing the socket connection to the lease backend. It then reissues + /// the query and verifies that: + /// -# The Query throws DbOperationError (rather than exiting) + /// -# The registered DbLostCallback was invoked + /// -# The registered DbFailedCallback was invoked after two reconnect + /// attempts (once failing and second triggered by timer) + void testDbLostAndFailedAfterTimeoutCallback(); + + /// @brief Callback function registered with the lease manager bool db_lost_callback(db::ReconnectCtlPtr /* not_used */) { - return (callback_called_ = true); + return (++db_lost_callback_called_); } /// @brief Flag used to detect calls to db_lost_callback function - bool callback_called_; + uint32_t db_lost_callback_called_; + + /// @brief Callback function registered with the lease manager + bool db_recovered_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_recovered_callback_called_); + } + + /// @brief Flag used to detect calls to db_recovered_callback function + uint32_t db_recovered_callback_called_; + + /// @brief Callback function registered with the lease manager + bool db_failed_callback(db::ReconnectCtlPtr /* not_used */) { + return (++db_failed_callback_called_); + } + + /// @brief Flag used to detect calls to db_failed_callback function + uint32_t db_failed_callback_called_; /// The IOService object, used for all ASIO operations. isc::asiolink::IOServicePtr io_service_; diff --git a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc index 8e3c235e9c..b5522b2fd4 100644 --- a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc @@ -1837,6 +1837,7 @@ TEST_F(PostgreSQLHostMgrTest, setIPReservationUnique) { TEST_F(PostgreSQLHostMgrDbLostCallbackTest, testDbLostCallback) { testDbLostCallback(); } + #endif // The following tests require Cassandra enabled. diff --git a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc index 88aa540004..731257262d 100644 --- a/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_lease_mgr_unittest.cc @@ -981,15 +981,51 @@ TEST_F(MySQLLeaseMgrDbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreadin } /// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostCallback) { +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredCallback) { MultiThreadingTest mt(false); - testDbLostCallback(); + testDbLostAndRecoveredCallback(); } /// @brief Verifies that loss of connectivity to MySQL is handled correctly. -TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostCallbackMultiThreading) { +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) { MultiThreadingTest mt(true); - testDbLostCallback(); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndFailedCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to MySQL is handled correctly. +TEST_F(MySQLLeaseMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedAfterTimeoutCallback(); } /// @brief Tests v4 lease stats query variants. diff --git a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc index 988ea8895e..a28bf23ea8 100644 --- a/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_lease_mgr_unittest.cc @@ -937,15 +937,51 @@ TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testNoCallbackOnOpenFailureMultiThreadin } /// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostCallback) { +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredCallback) { MultiThreadingTest mt(false); - testDbLostCallback(); + testDbLostAndRecoveredCallback(); } /// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. -TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostCallbackMultiThreading) { +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredCallbackMultiThreading) { MultiThreadingTest mt(true); - testDbLostCallback(); + testDbLostAndRecoveredCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndFailedCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndFailedCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndRecoveredAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndRecoveredAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallback) { + MultiThreadingTest mt(false); + testDbLostAndFailedAfterTimeoutCallback(); +} + +/// @brief Verifies that loss of connectivity to PostgreSQL is handled correctly. +TEST_F(PgSqlLeaseMgrDbLostCallbackTest, testDbLostAndFailedAfterTimeoutCallbackMultiThreading) { + MultiThreadingTest mt(true); + testDbLostAndFailedAfterTimeoutCallback(); } /// @brief Tests v4 lease stats query variants. |