]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1375] added dbReconnect to host manager
authorRazvan Becheriu <razvan@isc.org>
Fri, 6 Nov 2020 16:25:13 +0000 (18:25 +0200)
committerRazvan Becheriu <razvan@isc.org>
Wed, 9 Dec 2020 17:12:46 +0000 (19:12 +0200)
16 files changed:
src/lib/asiolink/io_service.h
src/lib/dhcpsrv/base_host_data_source.h
src/lib/dhcpsrv/cql_host_data_source.cc
src/lib/dhcpsrv/host_data_source_factory.cc
src/lib/dhcpsrv/host_data_source_factory.h
src/lib/dhcpsrv/host_mgr.cc
src/lib/dhcpsrv/host_mgr.h
src/lib/dhcpsrv/mysql_host_data_source.cc
src/lib/dhcpsrv/mysql_host_data_source.h
src/lib/dhcpsrv/mysql_lease_mgr.cc
src/lib/dhcpsrv/pgsql_host_data_source.cc
src/lib/dhcpsrv/pgsql_host_data_source.h
src/lib/dhcpsrv/pgsql_lease_mgr.cc
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc
src/lib/pgsql/tests/pgsql_exchange_unittest.cc

index acfb97361ceb9340c751210c8f171750f95524b8..f6f33c15e293d9b6c01bb1ebed435fb776f02386 100644 (file)
@@ -5,7 +5,7 @@
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #ifndef ASIOLINK_IO_SERVICE_H
-#define ASIOLINK_IO_SERVICE_H 1
+#define ASIOLINK_IO_SERVICE_H
 
 #include <boost/version.hpp>
 #include <boost/shared_ptr.hpp>
index 9050feb83ca5fb5cf814daf5572d3e876784685a..c83a0bea34d12f59debc61fb026ba4522fa03224 100644 (file)
@@ -8,6 +8,7 @@
 #define BASE_HOST_DATA_SOURCE_H
 
 #include <asiolink/io_address.h>
+#include <database/database_connection.h>
 #include <dhcpsrv/host.h>
 #include <exceptions/exceptions.h>
 #include <boost/shared_ptr.hpp>
@@ -458,6 +459,15 @@ public:
     /// @return Type of the backend.
     virtual std::string getType() const = 0;
 
+    /// @brief Return backend parameters
+    ///
+    /// Returns the backend parameters
+    ///
+    /// @return Parameters of the backend.
+    virtual isc::db::DatabaseConnection::ParameterMap getParameters() const {
+        return (isc::db::DatabaseConnection::ParameterMap());
+    };
+
     /// @brief Commit Transactions
     ///
     /// Commits all pending database operations.  On databases that don't
index 38cd0e2107d80be7463da64ba719d73d7ec245f1..ff4f76e738d190e1872ee60adee5f6587993983e 100644 (file)
@@ -2052,7 +2052,7 @@ public:
     /// This constructor opens database connection and initializes
     /// prepared statements used in the queries.
     /// @param parameters parameters passed to the CQL connection.
-    explicit CqlHostDataSourceImpl(const CqlConnection::ParameterMap& parameters);
+    explicit CqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters);
 
     /// @brief Destructor.
     virtual ~CqlHostDataSourceImpl();
@@ -2509,7 +2509,7 @@ operator==(const HostKey& key1, const HostKey& key2) {
             std::get<IPv4_RESERVATION>(key1) == std::get<IPv4_RESERVATION>(key2));
 }
 
-CqlHostDataSourceImpl::CqlHostDataSourceImpl(const CqlConnection::ParameterMap& parameters)
+CqlHostDataSourceImpl::CqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters)
     : parameters_(parameters), dbconn_(parameters) {
     // Validate the schema version first.
     std::pair<uint32_t, uint32_t> code_version(CQL_SCHEMA_VERSION_MAJOR,
@@ -3447,7 +3447,7 @@ CqlHostDataSourceImpl::mergeHosts(const ConstHostPtr& source_host,
     source_host->getCfgOption6()->mergeTo(*target_host->getCfgOption6());
 }
 
-CqlHostDataSource::CqlHostDataSource(const CqlConnection::ParameterMap& parameters)
+CqlHostDataSource::CqlHostDataSource(const DatabaseConnection::ParameterMap& parameters)
     : impl_(new CqlHostDataSourceImpl(parameters)) {
 }
 
index 6def277d55eea93bc1c254f636df1fa994dc8e08..45817b614eaac3d48de61798fc92487343390500 100644 (file)
@@ -87,6 +87,7 @@ HostDataSourceFactory::add(HostDataSourceList& sources,
 bool
 HostDataSourceFactory::del(HostDataSourceList& sources,
                            const string& db_type) {
+    bool result = false;
     for (auto it = sources.begin(); it != sources.end(); ++it) {
         if ((*it)->getType() != db_type) {
             continue;
@@ -94,9 +95,28 @@ HostDataSourceFactory::del(HostDataSourceList& sources,
         LOG_DEBUG(hosts_logger, DHCPSRV_DBG_TRACE, HOSTS_CFG_CLOSE_HOST_DATA_SOURCE)
             .arg(db_type);
         sources.erase(it);
-        return (true);
+        result = true;
+    }
+    return (result);
+}
+
+bool
+HostDataSourceFactory::del(HostDataSourceList& sources,
+                           const string& db_type,
+                           const string& dbaccess) {
+    DatabaseConnection::ParameterMap parameters =
+            DatabaseConnection::parse(dbaccess);
+    bool result = false;
+    for (auto it = sources.begin(); it != sources.end(); ++it) {
+        if ((*it)->getType() != db_type || (*it)->getParameters() != parameters) {
+            continue;
+        }
+        LOG_DEBUG(hosts_logger, DHCPSRV_DBG_TRACE, HOSTS_CFG_CLOSE_HOST_DATA_SOURCE)
+            .arg((*it)->getType());
+        sources.erase(it);
+        result = true;
     }
-    return (false);
+    return (result);
 }
 
 bool
index 8543ee7eebb0129a4d4e0354bfa5c7af0e870180..97b9d2a6749ff13ad76575d4e54f9f35b32b64a6 100644 (file)
@@ -67,7 +67,7 @@ public:
 
     /// @brief Delete a host data source.
     ///
-    /// Delete the first instance of a host data source of the given type.
+    /// Delete all instances of a host data source of the given type.
     /// This should have the effect of closing the database connection.
     ///
     /// @param sources host data source list.
@@ -75,6 +75,21 @@ public:
     /// @return true when found and removed, false when not found.
     static bool del(HostDataSourceList& sources, const std::string& db_type);
 
+    /// @brief Delete a host data source.
+    ///
+    /// Delete instance of a host data source which matches specific parameters.
+    /// This should have the effect of closing the database connection.
+    ///
+    /// @param sources host data source list.
+    /// @param db_type database backend type.
+    /// @param dbaccess Database access parameters.  These are in the form of
+    ///        "keyword=value" pairs, separated by spaces. They are backend-
+    ///        -end specific, although must include the "type" keyword which
+    ///        gives the backend in use.
+    /// @return true when found and removed, false when not found.
+    static bool del(HostDataSourceList& sources, const std::string& db_type,
+                    const std::string& dbaccess);
+
     /// @brief Type of host data source factory
     ///
     /// A factory takes a parameter map and returns a pointer to a host
index 205bbf429d33a832112489a1bd51cf84bc44a1e0..9a08018bf38b02b2396c013d53d95c3aae6aee1f 100644 (file)
@@ -29,6 +29,7 @@ namespace isc {
 namespace dhcp {
 
 using namespace isc::asiolink;
+using namespace isc::db;
 
 IOServicePtr HostMgr::io_service_ = IOServicePtr();
 
@@ -57,6 +58,11 @@ HostMgr::delBackend(const std::string& db_type) {
     return (HostDataSourceFactory::del(getHostMgrPtr()->alternate_sources_, db_type));
 }
 
+bool
+HostMgr::delBackend(const std::string& db_type, const std::string& access) {
+    return (HostDataSourceFactory::del(getHostMgrPtr()->alternate_sources_, db_type, access));
+}
+
 void
 HostMgr::delAllBackends() {
     getHostMgrPtr()->alternate_sources_.clear();
index 2c8df47ff88ec226d6c0537d2f542041d7cf8cd8..0860761d8e3ac773af676e6b97e8336bea81ba30 100644 (file)
@@ -80,6 +80,14 @@ public:
     /// @return true when found and removed, false when not found.
     static bool delBackend(const std::string& db_type);
 
+    /// @brief Delete an alternate host backend (aka host data source).
+    ///
+    /// @param db_type database backend type.
+    /// @param access Host backend access parameters for the alternate
+    /// host backend. It holds "keyword=value" pairs, separated by spaces.
+    /// @return true when found and removed, false when not found.
+    static bool delBackend(const std::string& db_type, const std::string& access);
+
     /// @brief Delete all alternate backends.
     static void delAllBackends();
 
index a1c712e78a0fce3ade5be7e983f1a6fa91685179..edb60eb8f9f6e118644bae6d97ffc7f4ec5565ed 100644 (file)
@@ -6,14 +6,19 @@
 
 #include <config.h>
 
+#include <asiolink/io_service.h>
 #include <database/db_exceptions.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_space.h>
+#include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/mysql_host_data_source.h>
+#include <dhcpsrv/timer_mgr.h>
 #include <util/buffer.h>
 #include <util/multi_threading_mgr.h>
 #include <util/optional.h>
@@ -1946,7 +1951,9 @@ public:
     /// @brief Constructor
     ///
     /// @param parameters See MySqlHostMgr constructor.
-    MySqlHostContext(const DatabaseConnection::ParameterMap& parameters);
+    MySqlHostContext(const 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,
@@ -2054,11 +2061,32 @@ public:
     ///
     /// This constructor opens database connection and initializes prepared
     /// statements used in the queries.
-    MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters);
+    MySqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters);
 
     /// @brief Destructor.
     ~MySqlHostDataSourceImpl();
 
+    /// @brief Attempts to reconnect the server to the host DB backend manager.
+    ///
+    /// This is a self-rescheduling function that attempts to reconnect to the
+    /// server's host DB backends after connectivity to one or more have been
+    /// lost. Upon entry it will attempt to reconnect via
+    /// @ref HostDataSourceFactory::add.
+    /// 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(ReconnectCtlPtr db_reconnect_ctl);
+
     /// @brief Create a new context.
     ///
     /// The database is opened with all the SQL commands pre-compiled.
@@ -2690,8 +2718,10 @@ TaggedStatementArray tagged_statements = { {
 
 // MySqlHostContext Constructor
 
-MySqlHostContext::MySqlHostContext(const DatabaseConnection::ParameterMap& parameters)
-    : conn_(parameters), is_readonly_(true) {
+MySqlHostContext::MySqlHostContext(const DatabaseConnection::ParameterMap& parameters,
+                                   const isc::asiolink::IOServicePtr& io_service,
+                                   db::DbCallback callback)
+    : conn_(parameters, io_service, callback), is_readonly_(true) {
 }
 
 // MySqlHostContextAlloc Constructor and Destructor
@@ -2730,7 +2760,7 @@ MySqlHostDataSource::MySqlHostContextAlloc::~MySqlHostContextAlloc() {
     // If running in single-threaded mode, there's nothing to do here.
 }
 
-MySqlHostDataSourceImpl::MySqlHostDataSourceImpl(const MySqlConnection::ParameterMap& parameters)
+MySqlHostDataSourceImpl::MySqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters)
     : parameters_(parameters), ip_reservations_unique_(true) {
 
     // Validate the schema version first.
@@ -2748,13 +2778,26 @@ MySqlHostDataSourceImpl::MySqlHostDataSourceImpl(const MySqlConnection::Paramete
     // Create an initial context.
     pool_.reset(new MySqlHostContextPool());
     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(&MySqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
+                  db_reconnect_ctl->retryInterval(),
+                  asiolink::IntervalTimer::ONE_SHOT);
 }
 
 // Create context.
 
 MySqlHostContextPtr
 MySqlHostDataSourceImpl::createContext() const {
-    MySqlHostContextPtr ctx(new MySqlHostContext(parameters_));
+    MySqlHostContextPtr ctx(new MySqlHostContext(parameters_,
+                            HostMgr::getIOService(),
+                            &MySqlHostDataSourceImpl::dbReconnect));
 
     // Open the database.
     ctx->conn_.openDatabase();
@@ -2787,10 +2830,74 @@ MySqlHostDataSourceImpl::createContext() const {
     ctx->host_ipv6_reservation_exchange_.reset(new MySqlIPv6ReservationExchange());
     ctx->host_option_exchange_.reset(new MySqlOptionExchange());
 
+    std::string manager = "MySqlHostMgr[";
+    manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+    std::string timer_name = manager + "]DbReconnectTimer";
+
+    ctx->conn_.makeReconnectCtl(timer_name);
+
     return (ctx);
 }
 
 MySqlHostDataSourceImpl::~MySqlHostDataSourceImpl() {
+    std::string manager = "MySqlHostMgr[";
+    manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+    std::string timer_name = manager + "]DbReconnectTimer";
+
+    TimerMgr::instance()->unregisterTimer(timer_name);
+}
+
+bool
+MySqlHostDataSourceImpl::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();
+        std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
+        for (std::string& hds : host_db_access_list) {
+            auto parameters = DatabaseConnection::parse(hds);
+            if (HostMgr::delBackend("mysql", hds)) {
+                HostMgr::addBackend(hds);
+            }
+        }
+
+        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);
 }
 
 std::pair<uint32_t, uint32_t>
@@ -3023,13 +3130,18 @@ MySqlHostDataSourceImpl::checkReadOnly(MySqlHostContextPtr& ctx) const {
     }
 }
 
-MySqlHostDataSource::MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
+MySqlHostDataSource::MySqlHostDataSource(const DatabaseConnection::ParameterMap& parameters)
     : impl_(new MySqlHostDataSourceImpl(parameters)) {
 }
 
 MySqlHostDataSource::~MySqlHostDataSource() {
 }
 
+DatabaseConnection::ParameterMap
+MySqlHostDataSource::getParameters() const {
+    return impl_->parameters_;
+}
+
 void
 MySqlHostDataSource::add(const HostPtr& host) {
     // Get a context
index 0a0495d3d0755a1789313fc163b92e0077c4c869..be705d2081481b5af45641cc9e687f675e936e8d 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef MYSQL_HOST_DATA_SOURCE_H
 #define MYSQL_HOST_DATA_SOURCE_H
 
+#include <database/database_connection.h>
 #include <database/db_exceptions.h>
 #include <dhcpsrv/base_host_data_source.h>
 #include <mysql/mysql_connection.h>
@@ -69,6 +70,13 @@ public:
     /// Releases prepared MySQL statements used by the backend.
     virtual ~MySqlHostDataSource();
 
+    /// @brief Return backend parameters
+    ///
+    /// Returns the backend parameters
+    ///
+    /// @return Parameters of the backend.
+    virtual isc::db::DatabaseConnection::ParameterMap getParameters() const;
+
     /// @brief Adds a new host to the collection.
     ///
     /// The implementations of this method should guard against duplicate
index b50be15d8acd15360df2bc6e3b42905f7008fead..b4bd0a315889973b21152c761ff254a241b97a0b 100644 (file)
@@ -1736,7 +1736,7 @@ bool MySqlLeaseStatsQuery::negative_count_ = false;
 
 // MySqlLeaseContext Constructor
 
-MySqlLeaseContext::MySqlLeaseContext(const MySqlConnection::ParameterMap& parameters,
+MySqlLeaseContext::MySqlLeaseContext(const DatabaseConnection::ParameterMap& parameters,
                                      const isc::asiolink::IOServicePtr& io_service,
                                      DbCallback callback)
     : conn_(parameters, io_service, callback) {
@@ -1780,7 +1780,7 @@ MySqlLeaseMgr::MySqlLeaseContextAlloc::~MySqlLeaseContextAlloc() {
 
 // MySqlLeaseMgr Constructor and Destructor
 
-MySqlLeaseMgr::MySqlLeaseMgr(const MySqlConnection::ParameterMap& parameters)
+MySqlLeaseMgr::MySqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
     : parameters_(parameters) {
 
     // Validate schema version first.
index d0f3c02c8e8d3d4ea68ea54889f6fd5364a42b2b..7033b2fb07e7cadd4952d27fb8ef23dc97413ba4 100644 (file)
@@ -6,14 +6,19 @@
 
 #include <config.h>
 
+#include <asiolink/io_service.h>
 #include <database/db_exceptions.h>
 #include <dhcp/libdhcp++.h>
 #include <dhcp/option.h>
 #include <dhcp/option_definition.h>
 #include <dhcp/option_space.h>
+#include <dhcpsrv/cfg_db_access.h>
 #include <dhcpsrv/cfg_option.h>
+#include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/dhcpsrv_log.h>
+#include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/pgsql_host_data_source.h>
+#include <dhcpsrv/timer_mgr.h>
 #include <util/buffer.h>
 #include <util/multi_threading_mgr.h>
 #include <util/optional.h>
@@ -1308,7 +1313,9 @@ public:
     /// @brief Constructor
     ///
     /// @param parameters See PgSqlHostMgr constructor.
-    PgSqlHostContext(const DatabaseConnection::ParameterMap& parameters);
+    PgSqlHostContext(const 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,
@@ -1416,11 +1423,32 @@ public:
     ///
     /// This constructor opens database connection and initializes prepared
     /// statements used in the queries.
-    PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters);
+    PgSqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters);
 
     /// @brief Destructor.
     ~PgSqlHostDataSourceImpl();
 
+    /// @brief Attempts to reconnect the server to the host DB backend manager.
+    ///
+    /// This is a self-rescheduling function that attempts to reconnect to the
+    /// server's host DB backends after connectivity to one or more have been
+    /// lost. Upon entry it will attempt to reconnect via
+    /// @ref HostDataSourceFactory::add.
+    /// 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(ReconnectCtlPtr db_reconnect_ctl);
+
     /// @brief Create a new context.
     ///
     /// The database is opened with all the SQL commands pre-compiled.
@@ -1574,7 +1602,7 @@ public:
     std::pair<uint32_t, uint32_t> getVersion() const;
 
     /// @brief The parameters
-    PgSqlConnection::ParameterMap parameters_;
+    DatabaseConnection::ParameterMap parameters_;
 
     /// @brief Holds the setting whether the IP reservations must be unique or
     /// may be non-unique.
@@ -2131,8 +2159,10 @@ TaggedStatementArray tagged_statements = { {
 
 // PgSqlHostContext Constructor
 
-PgSqlHostContext::PgSqlHostContext(const DatabaseConnection::ParameterMap& parameters)
-    : conn_(parameters), is_readonly_(true) {
+PgSqlHostContext::PgSqlHostContext(const DatabaseConnection::ParameterMap& parameters,
+                                   const isc::asiolink::IOServicePtr& io_service,
+                                   db::DbCallback callback)
+    : conn_(parameters, io_service, callback), is_readonly_(true) {
 }
 
 // PgSqlHostContextAlloc Constructor and Destructor
@@ -2171,7 +2201,7 @@ PgSqlHostDataSource::PgSqlHostContextAlloc::~PgSqlHostContextAlloc() {
     // If running in single-threaded mode, there's nothing to do here.
 }
 
-PgSqlHostDataSourceImpl::PgSqlHostDataSourceImpl(const PgSqlConnection::ParameterMap& parameters)
+PgSqlHostDataSourceImpl::PgSqlHostDataSourceImpl(const DatabaseConnection::ParameterMap& parameters)
     : parameters_(parameters), ip_reservations_unique_(true) {
 
     // Validate the schema version first.
@@ -2189,13 +2219,26 @@ PgSqlHostDataSourceImpl::PgSqlHostDataSourceImpl(const PgSqlConnection::Paramete
     // Create an initial context.
     pool_.reset(new PgSqlHostContextPool());
     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(&PgSqlHostDataSourceImpl::dbReconnect, db_reconnect_ctl),
+                  db_reconnect_ctl->retryInterval(),
+                  asiolink::IntervalTimer::ONE_SHOT);
 }
 
 // Create context.
 
 PgSqlHostContextPtr
 PgSqlHostDataSourceImpl::createContext() const {
-    PgSqlHostContextPtr ctx(new PgSqlHostContext(parameters_));
+    PgSqlHostContextPtr ctx(new PgSqlHostContext(parameters_,
+                            HostMgr::getIOService(),
+                            &PgSqlHostDataSourceImpl::dbReconnect));
 
     // Open the database.
     ctx->conn_.openDatabase();
@@ -2223,10 +2266,74 @@ PgSqlHostDataSourceImpl::createContext() const {
     ctx->host_ipv6_reservation_exchange_.reset(new PgSqlIPv6ReservationExchange());
     ctx->host_option_exchange_.reset(new PgSqlOptionExchange());
 
+    std::string manager = "PgSqlHostMgr[";
+    manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+    std::string timer_name = manager + "]DbReconnectTimer";
+
+    ctx->conn_.makeReconnectCtl(timer_name);
+
     return (ctx);
 }
 
 PgSqlHostDataSourceImpl::~PgSqlHostDataSourceImpl() {
+    std::string manager = "PgSqlHostMgr[";
+    manager += boost::lexical_cast<std::string>(reinterpret_cast<uint64_t>(this));
+    std::string timer_name = manager + "]DbReconnectTimer";
+
+    TimerMgr::instance()->unregisterTimer(timer_name);
+}
+
+bool
+PgSqlHostDataSourceImpl::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();
+        std::list<std::string> host_db_access_list = cfg_db->getHostDbAccessStringList();
+        for (std::string& hds : host_db_access_list) {
+            auto parameters = DatabaseConnection::parse(hds);
+            if (HostMgr::delBackend("postgresql", hds)) {
+                HostMgr::addBackend(hds);
+            }
+        }
+
+        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);
 }
 
 uint64_t
@@ -2432,13 +2539,18 @@ PgSqlHostDataSourceImpl::checkReadOnly(PgSqlHostContextPtr& ctx) const {
 
 /*********** PgSqlHostDataSource *********************/
 
-PgSqlHostDataSource::PgSqlHostDataSource(const PgSqlConnection::ParameterMap& parameters)
+PgSqlHostDataSource::PgSqlHostDataSource(const DatabaseConnection::ParameterMap& parameters)
     : impl_(new PgSqlHostDataSourceImpl(parameters)) {
 }
 
 PgSqlHostDataSource::~PgSqlHostDataSource() {
 }
 
+DatabaseConnection::ParameterMap
+PgSqlHostDataSource::getParameters() const {
+    return impl_->parameters_;
+}
+
 void
 PgSqlHostDataSource::add(const HostPtr& host) {
     // Get a context
index 55b76afd89926a5739e54a372b01d8101ac0ccfb..aec52305a35f28292288c87b1525947199992068 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef PGSQL_HOST_DATA_SOURCE_H
 #define PGSQL_HOST_DATA_SOURCE_H
 
+#include <database/database_connection.h>
 #include <dhcpsrv/base_host_data_source.h>
 #include <pgsql/pgsql_connection.h>
 #include <pgsql/pgsql_exchange.h>
@@ -73,6 +74,13 @@ public:
     /// the destruction of member impl_.
     virtual ~PgSqlHostDataSource();
 
+    /// @brief Return backend parameters
+    ///
+    /// Returns the backend parameters
+    ///
+    /// @return Parameters of the backend.
+    virtual isc::db::DatabaseConnection::ParameterMap getParameters() const;
+
     /// @brief Adds a new host to the collection.
     ///
     /// The method will insert the given host and all of its children (v4
index c0abb428606d1044f91fbec8a527dffc312d7f84..cae1f43faa57e8f2851a2fdcd9bc357b3c573521 100644 (file)
@@ -1170,7 +1170,7 @@ bool PgSqlLeaseStatsQuery::negative_count_ = false;
 
 // PgSqlLeaseContext Constructor
 
-PgSqlLeaseContext::PgSqlLeaseContext(const PgSqlConnection::ParameterMap& parameters,
+PgSqlLeaseContext::PgSqlLeaseContext(const DatabaseConnection::ParameterMap& parameters,
                                      const isc::asiolink::IOServicePtr& io_service,
                                      DbCallback callback)
     : conn_(parameters, io_service, callback) {
@@ -1214,7 +1214,7 @@ PgSqlLeaseMgr::PgSqlLeaseContextAlloc::~PgSqlLeaseContextAlloc() {
 
 // PgSqlLeaseMgr Constructor and Destructor
 
-PgSqlLeaseMgr::PgSqlLeaseMgr(const PgSqlConnection::ParameterMap& parameters)
+PgSqlLeaseMgr::PgSqlLeaseMgr(const DatabaseConnection::ParameterMap& parameters)
     : parameters_(parameters) {
 
     // Validate schema version first.
index a656bba51564f415321103ff50aca2a51034e475..a040b30533c4f8b01db26187aee692750ef33f9e 100644 (file)
@@ -118,7 +118,7 @@ public:
     int countRowsInTable(const std::string& table) {
         string query = "SELECT * FROM " + table;
 
-        MySqlConnection::ParameterMap params;
+        DatabaseConnection::ParameterMap params;
         params["name"] = "keatest";
         params["user"] = "keatest";
         params["password"] = "keatest";
@@ -1227,7 +1227,7 @@ TEST_F(MySqlHostDataSourceTest, testAddRollback) {
     // when inserting reservations or options. The simplest way to
     // achieve that is to simply drop one of the tables. To do so, we
     // connect to the database and issue a DROP query.
-    MySqlConnection::ParameterMap params;
+    DatabaseConnection::ParameterMap params;
     params["name"] = "keatest";
     params["user"] = "keatest";
     params["password"] = "keatest";
@@ -1277,7 +1277,7 @@ TEST_F(MySqlHostDataSourceTest, testAddRollbackMultiThreading) {
     // when inserting reservations or options. The simplest way to
     // achieve that is to simply drop one of the tables. To do so, we
     // connect to the database and issue a DROP query.
-    MySqlConnection::ParameterMap params;
+    DatabaseConnection::ParameterMap params;
     params["name"] = "keatest";
     params["user"] = "keatest";
     params["password"] = "keatest";
index 3ca49bc04c7cb2114509d3d4a6f3a6020e0c7df2..32a51f6e59d769b4ae4b4c1479cded44532e895c 100644 (file)
@@ -118,7 +118,7 @@ public:
     int countRowsInTable(const std::string& table) {
         string query = "SELECT * FROM " + table;
 
-        PgSqlConnection::ParameterMap params;
+        DatabaseConnection::ParameterMap params;
         params["name"] = "keatest";
         params["user"] = "keatest";
         params["password"] = "keatest";
@@ -342,7 +342,7 @@ TEST(PgSqlHostDataSource, NoCallbackOnOpenFail) {
     createPgSQLSchema();
 
     callback_called = false;
-    DatabaseConnection::db_lost_callback = db_lost_callback;
+    DatabaseConnection::db_lost_callback_ = db_lost_callback;
     HostMgr::create();
     EXPECT_THROW(HostMgr::addBackend(connectionString(
         PGSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
@@ -369,7 +369,7 @@ TEST(PgSqlHostDataSource, NoCallbackOnOpenFailMultiThreading) {
     createPgSQLSchema();
 
     callback_called = false;
-    DatabaseConnection::db_lost_callback = db_lost_callback;
+    DatabaseConnection::db_lost_callback_ = db_lost_callback;
     HostMgr::create();
     EXPECT_THROW(HostMgr::addBackend(connectionString(
         PGSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)),
@@ -1236,7 +1236,7 @@ TEST_F(PgSqlHostDataSourceTest, testAddRollback) {
     // when inserting reservations or options. The simplest way to
     // achieve that is to simply drop one of the tables. To do so, we
     // connect to the database and issue a DROP query.
-    PgSqlConnection::ParameterMap params;
+    DatabaseConnection::ParameterMap params;
     params["name"] = "keatest";
     params["user"] = "keatest";
     params["password"] = "keatest";
@@ -1286,7 +1286,7 @@ TEST_F(PgSqlHostDataSourceTest, testAddRollbackMultiThreading) {
     // when inserting reservations or options. The simplest way to
     // achieve that is to simply drop one of the tables. To do so, we
     // connect to the database and issue a DROP query.
-    PgSqlConnection::ParameterMap params;
+    DatabaseConnection::ParameterMap params;
     params["name"] = "keatest";
     params["user"] = "keatest";
     params["password"] = "keatest";
index 173665ac94c3e768c4914527b6cb811a8749bef2..afaf0955ff1e743ae277165b608c462305651503 100644 (file)
@@ -130,7 +130,7 @@ public:
     /// the table (if present) and then recreates it.
     PgSqlBasicsTest() : expectedColNames_(NUM_BASIC_COLS) {
         // Create database connection parameter list
-        PgSqlConnection::ParameterMap params;
+        DatabaseConnection::ParameterMap params;
         params["name"] = "keatest";
         params["user"] = "keatest";
         params["password"] = "keatest";