If there is no password to the account, set the password to the empty string
"". (This is also the default.)</para>
- </para>
+ <para>The multiple storage extension uses a similar syntax: a configuration
+ is placed into a "hosts-databases" list instead of into a "hosts-database"
+ entry as in:
+ <screen>
+ "Dhcp4": { "hosts-databases": [ { <userinput>"type": "mysql"</userinput>, ... }, ... ], ... }
+ </screen>
++ </para>
+
+ <para>For additional Cassandra specific parameters, see <xref
+ linkend="cassandra-database-configuration4"/>.</para>
</section>
If there is no password to the account, set the password to the empty string
"". (This is also the default.)</para>
-
+ <para>The multiple storage extension uses a similar syntax: a configuration
+ is placed into a "hosts-databases" list instead of into a "hosts-database"
+ entry as in:
+ <screen>
+ "Dhcp6": { "hosts-databases": [ { <userinput>"type": "mysql"</userinput>, ... }, ... ], ... }
+ </screen>
+ </para>
+
+ <para>
+ For additional Cassandra specific parameters, see <xref
+ linkend="cassandra-database-configuration4" />.</para>
++
</section>
<section xml:id="read-only-database-configuration6">
///
/// @return Const @c Host object using a specified IPv6 address/prefix.
virtual ConstHostPtr
-- get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const = 0;
++ get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const =
++ 0;
+
+ /// @brief Adds a new host to the collection.
+ ///
+ /// The implementations of this method should guard against duplicate
+ /// reservations for the same host, where possible. For example, when the
+ /// reservation for the same HW address and subnet id is added twice, the
+ /// implementation should throw an exception. Note, that usually it is
+ /// impossible to guard against adding duplicated host, where one instance
+ /// is identified by HW address, another one by DUID.
+ ///
+ /// @param host Pointer to the new @c Host object being added.
+ virtual void add(const HostPtr& host) = 0;
+
+ /// @brief Attempts to delete a host by (subnet-id, address)
+ ///
+ /// This method supports both v4 and v6.
+ ///
+ /// @param subnet_id subnet identifier.
+ /// @param addr specified address.
+ /// @return true if deletion was successful, false if the host was not there.
+ /// @throw various exceptions in case of errors
+ virtual bool del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) = 0;
+
+ /// @brief Attempts to delete a host by (subnet-id4, identifier, identifier-type)
+ ///
+ /// This method supports v4 hosts only.
+ ///
+ /// @param subnet_id IPv4 Subnet identifier.
+ /// @param identifier_type Identifier type.
+ /// @param identifier_begin Pointer to a beginning of a buffer containing
+ /// an identifier.
+ /// @param identifier_len Identifier length.
+ /// @return true if deletion was successful, false if the host was not there.
+ /// @throw various exceptions in case of errors
+ virtual bool del4(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin, const size_t identifier_len) = 0;
+
+ /// @brief Attempts to delete a host by (subnet-id6, identifier, identifier-type)
+ ///
+ /// This method supports v6 hosts only.
+ ///
+ /// @param subnet_id IPv6 Subnet identifier.
+ /// @param identifier_type Identifier type.
+ /// @param identifier_begin Pointer to a beginning of a buffer containing
+ /// an identifier.
+ /// @param identifier_len Identifier length.
+ /// @return true if deletion was successful, false if the host was not there.
+ /// @throw various exceptions in case of errors
+ virtual bool del6(const SubnetID& subnet_id,
+ const Host::IdentifierType& identifier_type,
+ const uint8_t* identifier_begin, const size_t identifier_len) = 0;
/// @brief Return backend type
///
#define CFG_DBACCESS_H
#include <cc/cfg_to_element.h>
+#include <dhcpsrv/database_connection.h>
+
#include <boost/shared_ptr.hpp>
#include <string>
+ #include <list>
namespace isc {
namespace dhcp {
#endif
#ifdef HAVE_CQL
- if (db_type == "cql") {
+ struct CqlHostDataSourceInit {
+ // Constructor registers
+ CqlHostDataSourceInit() {
+ HostDataSourceFactory::registerFactory("cql", factory);
+ }
+
+ // Destructor deregisters
+ ~CqlHostDataSourceInit() {
+ HostDataSourceFactory::deregisterFactory("cql");
+ }
+
+ // Factory class method
+ static HostDataSourcePtr
+ factory(const DatabaseConnection::ParameterMap& parameters) {
LOG_INFO(dhcpsrv_logger, DHCPSRV_CQL_HOST_DB)
.arg(DatabaseConnection::redactedAccessString(parameters));
- getHostDataSourcePtr().reset(new CqlHostDataSource(parameters));
- return;
+ return (HostDataSourcePtr(new CqlHostDataSource(parameters)));
}
- #endif
+ };
- // Get here on no match.
- isc_throw(InvalidType, "Hosts database access parameter 'type': " <<
- db_type << " is invalid");
- }
+ // Database backend will be registered at object initialization
+ CqlHostDataSourceInit cql_init_;
+ #endif
- void
- HostDataSourceFactory::destroy() {
- // Destroy current host data source instance. This is a no-op if no host
- // data source is available.
- if (getHostDataSourcePtr()) {
- LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, HOSTS_CFG_CLOSE_HOST_DATA_SOURCE)
- .arg(getHostDataSourcePtr()->getType());
- }
- getHostDataSourcePtr().reset();
- }
+ } // end of anonymous namespace
+
- } // namespace dhcp
- } // namespace isc
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/base_host_data_source.h>
+ #include <dhcpsrv/cache_host_data_source.h>
+#include <dhcpsrv/database_connection.h>
#include <dhcpsrv/host.h>
#include <dhcpsrv/subnet_id.h>
#include <boost/noncopyable.hpp>
libdhcpsrv_unittests_SOURCES += logging_unittest.cc
libdhcpsrv_unittests_SOURCES += logging_info_unittest.cc
libdhcpsrv_unittests_SOURCES += generic_lease_mgr_unittest.cc generic_lease_mgr_unittest.h
- libdhcpsrv_unittests_SOURCES += generic_host_data_source_unittest.cc generic_host_data_source_unittest.h
libdhcpsrv_unittests_SOURCES += memfile_lease_mgr_unittest.cc
libdhcpsrv_unittests_SOURCES += dhcp_parsers_unittest.cc
+libdhcpsrv_unittests_SOURCES += ncr_generator_unittest.cc
if HAVE_MYSQL
libdhcpsrv_unittests_SOURCES += mysql_lease_mgr_unittest.cc
libdhcpsrv_unittests_SOURCES += mysql_host_data_source_unittest.cc
#include <config.h>
- #include <asiolink/io_address.h>
- #include <dhcpsrv/tests/test_utils.h>
+#include <exceptions/exceptions.h>
+ #include <asiolink/io_address.h>
-#include <dhcpsrv/cql_connection.h>
-#include <dhcpsrv/cql_host_data_source.h>
-#include <dhcpsrv/cql_lease_mgr.h>
#include <dhcpsrv/host.h>
-#include <dhcpsrv/testutils/generic_host_data_source_unittest.h>
-#include <dhcpsrv/tests/test_utils.h>
+ #include <dhcpsrv/host_mgr.h>
+ #include <dhcpsrv/host_data_source_factory.h>
- #include <dhcpsrv/cql_exchange.h>
- #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
+#include <dhcpsrv/cql_connection.h>
++#include <dhcpsrv/cql_lease_mgr.h>
+#include <dhcpsrv/cql_host_data_source.h>
#include <dhcpsrv/testutils/cql_schema.h>
++#include <dhcpsrv/testutils/generic_host_data_source_unittest.h>
#include <dhcpsrv/testutils/host_data_source_utils.h>
- #include <dhcpsrv/host_data_source_factory.h>
-#include <exceptions/exceptions.h>
++#include <dhcpsrv/tests/test_utils.h>
#include <gtest/gtest.h>
// Connect to the database
try {
- HostDataSourceFactory::create(validCqlConnectionString());
+ HostMgr::create();
+ HostMgr::addBackend(validCqlConnectionString());
} catch (...) {
- std::cerr << "*** ERROR: unable to open database. The test"
- "*** environment is broken and must be fixed before"
- "*** the CQL tests will run correctly."
- "*** The reason for the problem is described in the"
- "*** accompanying exception output.";
+ std::cerr << "*** ERROR: unable to open database. The test\n"
+ "*** environment is broken and must be fixed before\n"
+ "*** the CQL tests will run correctly.\n"
+ "*** The reason for the problem is described in the\n"
+ "*** accompanying exception output.\n";
throw;
}
/// Closes the database and re-open it. Anything committed should be
/// visible.
///
- /// Parameter is ignored for CQL backend as the v4 and v6 leases share
+ /// Parameter is ignored for CQL backend as the v4 and v6 hosts share
/// the same database.
void reopen(Universe) {
- HostDataSourceFactory::destroy();
- HostDataSourceFactory::create(validCqlConnectionString());
- hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
+ HostMgr::create();
+ HostMgr::addBackend(validCqlConnectionString());
+ hdsptr_ = HostMgr::instance().getHostDataSource();
}
+
+ /// @brief Returns number of IPv4 options currently stored in DB.
+ virtual int countDBOptions4() {
+ int result = 0;
+
+ const CqlHostDataSource* cql_host_mgr = dynamic_cast<const CqlHostDataSource*>(&(*hdsptr_));
+ ConstHostCollection all = cql_host_mgr->getAllHosts();
+
+ for (ConstHostCollection::const_iterator it = all.begin();
+ it != all.end(); ++it) {
+ ConstCfgOptionPtr cfg_option4 = (*it)->getCfgOption4();
+ std::list<std::string> option_spaces4 = cfg_option4->getOptionSpaceNames();
+ std::list<std::string> vendor_spaces4 = cfg_option4->getVendorIdsSpaceNames();
+ option_spaces4.insert(option_spaces4.end(), vendor_spaces4.begin(),
+ vendor_spaces4.end());
+ for (const std::string& space : option_spaces4) {
+ OptionContainerPtr options = cfg_option4->getAll(space);
+ result += options->size();
+ }
+ }
+
+ return (result);
+ }
+
+ /// @brief Returns number of IPv6 options currently stored in DB.
+ virtual int countDBOptions6() {
+ int result = 0;
+
+ const CqlHostDataSource* cql_host_mgr = dynamic_cast<const CqlHostDataSource*>(&(*hdsptr_));
+ ConstHostCollection all = cql_host_mgr->getAllHosts();
+
+ for (ConstHostCollection::const_iterator it = all.begin();
+ it != all.end(); ++it) {
+ ConstCfgOptionPtr cfg_option6 = (*it)->getCfgOption6();
+ std::list<std::string> option_spaces6 = cfg_option6->getOptionSpaceNames();
+ std::list<std::string> vendor_spaces6 = cfg_option6->getVendorIdsSpaceNames();
+ option_spaces6.insert(option_spaces6.end(), vendor_spaces6.begin(),
+ vendor_spaces6.end());
+ for (const std::string& space : option_spaces6) {
+ OptionContainerPtr options = cfg_option6->getAll(space);
+ result += options->size();
+ }
+ }
+
+ return (result);
+ }
+
+ /// @brief Returns number of IPv6 reservations currently stored in DB.
+ virtual int countDBReservations6() {
+ int result = 0;
+
+ const CqlHostDataSource* cql_host_mgr = dynamic_cast<const CqlHostDataSource*>(&(*hdsptr_));
+ ConstHostCollection all = cql_host_mgr->getAllHosts();
+
+ for (ConstHostCollection::const_iterator it = all.begin();
+ it != all.end(); ++it) {
+ IPv6ResrvRange reservations = (*it)->getIPv6Reservations();
+ result += std::distance(reservations.first, reservations.second);
+ }
+
+ return (result);
+ }
+
};
/// @brief Check that database can be opened
<< "*** before the CQL tests will run correctly.\n";
}
- // Check that lease manager open the database opens correctly with a longer
+ // Check that host manager open the database opens correctly with a longer
// timeout. If it fails, print the error message.
try {
- std::string connection_string = validCqlConnectionString() + std::string(" ") +
- std::string(VALID_TIMEOUT);
+ // CQL specifies the timeout values in ms, not seconds. Therefore
+ // we need to add extra 000 to the "connect-timeout=10" string.
+ string connection_string = validCqlConnectionString() + string(" ") +
+ string(VALID_TIMEOUT) + string("000");
- HostDataSourceFactory::create(connection_string);
- EXPECT_NO_THROW((void) HostDataSourceFactory::getHostDataSourcePtr());
- HostDataSourceFactory::destroy();
+ HostMgr::create();
+ EXPECT_NO_THROW(HostMgr::addBackend(connection_string));
+ HostMgr::delBackend("cql");
} catch (const isc::Exception& ex) {
FAIL() << "*** ERROR: unable to open database, reason:\n"
<< " " << ex.what() << "\n"
throw;
}
- hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
+ hdsptr_ = HostMgr::instance().getHostDataSource();
}
- /// @brief Destructor
- ///
- /// Rolls back all pending transactions. The deletion of myhdsptr_ will close
- /// the database. Then reopen it and delete everything created by the test.
- virtual ~MySqlHostDataSourceTest() {
+ /// @brief Destroys the HDS and the schema.
+ void destroyTest() {
try {
hdsptr_->rollback();
} catch (...) {
<< "*** before the MySQL tests will run correctly.\n";
}
- // Check that attempting to get an instance of the host data source when
+ // Check that attempting to get an instance of the host manager when
// none is set throws an exception.
- EXPECT_FALSE(HostDataSourceFactory::getHostDataSourcePtr());
+ EXPECT_FALSE(HostMgr::instance().getHostDataSource());
// Check that wrong specification of backend throws an exception.
- // (This is really a check on LeaseMgrFactory, but is convenient to
+ // (This is really a check on HostDataSourceFactory, but is convenient to
// perform here.)
- EXPECT_THROW(HostDataSourceFactory::create(connectionString(
+ EXPECT_THROW(HostMgr::addBackend(connectionString(
NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
InvalidParameter);
- EXPECT_THROW(HostDataSourceFactory::create(connectionString(
+ EXPECT_THROW(HostMgr::addBackend(connectionString(
INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
InvalidType);
throw;
}
- hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
+ hdsptr_ = HostMgr::instance().getHostDataSource();
}
- /// @brief Destructor
- ///
- /// Rolls back all pending transactions. The deletion of myhdsptr_ will
- /// close the database. Then reopen it and delete everything created by
- /// the test.
- virtual ~PgSqlHostDataSourceTest() {
+ /// @brief Destroys the HDS and the schema.
+ void destroyTest() {
try {
hdsptr_->rollback();
} catch (...) {
/// Closes the database and re-open it. Anything committed should be
/// visible.
///
- /// Parameter is ignored for PostgreSQL backend as the v4 and v6 leases
- /// share the same database.
+ /// Parameter is ignored for PostgreSQL backend as the v4 and v6 leases share
+ /// the same database.
void reopen(Universe) {
- HostDataSourceFactory::destroy();
- HostDataSourceFactory::create(validPgSQLConnectionString());
- hdsptr_ = HostDataSourceFactory::getHostDataSourcePtr();
+ HostMgr::create();
+ HostMgr::addBackend(validPgSQLConnectionString());
+ hdsptr_ = HostMgr::instance().getHostDataSource();
}
/// @brief returns number of rows in a table
<< "*** before the PostgreSQL tests will run correctly.\n";
}
- // Check that attempting to get an instance of the host data source when
+ // Check that attempting to get an instance of the host manager when
// none is set throws an exception.
- EXPECT_FALSE(HostDataSourceFactory::getHostDataSourcePtr());
+ EXPECT_FALSE(HostMgr::instance().getHostDataSource());
// Check that wrong specification of backend throws an exception.
- // (This is really a check on LeaseMgrFactory, but is convenient to
+ // (This is really a check on HostDataSourceFactory, but is convenient to
// perform here.)
- EXPECT_THROW(HostDataSourceFactory::create(connectionString(
+ EXPECT_THROW(HostMgr::addBackend(connectionString(
NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)),
InvalidParameter);
- EXPECT_THROW(HostDataSourceFactory::create(connectionString(
+ EXPECT_THROW(HostMgr::addBackend(connectionString(
INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)),
InvalidType);
#include <dhcp/option_string.h>
#include <dhcp/option_vendor.h>
#include <dhcpsrv/database_connection.h>
+ #include <dhcpsrv/db_exceptions.h>
+ #include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/host_data_source_factory.h>
- #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
- #include <dhcpsrv/tests/test_utils.h>
+ #include <dhcpsrv/testutils/generic_host_data_source_unittest.h>
-#include <dhcpsrv/testutils/schema.h>
+ #include <dhcpsrv/testutils/host_data_source_utils.h>
+#include <dhcpsrv/testutils/schema.h>
+ #include <util/buffer.h>
+
#include <boost/foreach.hpp>
- #include <dhcpsrv/testutils/host_data_source_utils.h>
#include <gtest/gtest.h>
- #include <util/buffer.h>
#include <chrono>
#include <cstring>