diff --git a/src/bin/dhcp4/json_config_parser.cc b/src/bin/dhcp4/json_config_parser.cc index 5d39d1cb2c..6e46f87b1f 100644 --- a/src/bin/dhcp4/json_config_parser.cc +++ b/src/bin/dhcp4/json_config_parser.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -412,14 +413,14 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set, // Please move at the end when migration will be finished. if (config_pair.first == "lease-database") { - DbAccessParser parser(CfgDbAccess::LEASE_DB); + DbAccessParser parser(DBType::LEASE_DB); CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; } if (config_pair.first == "hosts-database") { - DbAccessParser parser(CfgDbAccess::HOSTS_DB); + DbAccessParser parser(DBType::HOSTS_DB); CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; @@ -427,9 +428,10 @@ configureDhcp4Server(Dhcpv4Srv&, isc::data::ConstElementPtr config_set, if (config_pair.first == "hosts-databases") { CfgDbAccessPtr cfg_db_access = srv_cfg->getCfgDbAccess(); - for (size_t i = 0; i < config_pair.second->size(); ++i) { - DbAccessParser parser(CfgDbAccess::HOSTS_DB + i); - parser.parse(cfg_db_access, config_pair.second->get(i)); + DbAccessParser parser(DBType::HOSTS_DB); + auto list = config_pair.second->listValue(); + for (auto it : list) { + parser.parse(cfg_db_access, it); } continue; } diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index ae70f42cac..a527419f1a 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -5790,11 +5790,11 @@ TEST_F(Dhcp4ParserTest, hostsDatabases) { ConstCfgDbAccessPtr cfgdb = CfgMgr::instance().getStagingCfg()->getCfgDbAccess(); ASSERT_TRUE(cfgdb); - const std::vector& hal = cfgdb->getHostDbAccessStringList(); + const std::list& hal = cfgdb->getHostDbAccessStringList(); ASSERT_EQ(2, hal.size()); // Keywords are in alphabetical order - EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest", hal[0]); - EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest", hal[1]); + EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest", hal.front()); + EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest", hal.back()); } // This test checks comments. Please keep it last. diff --git a/src/bin/dhcp6/json_config_parser.cc b/src/bin/dhcp6/json_config_parser.cc index f7d31980c2..2bc5e49022 100644 --- a/src/bin/dhcp6/json_config_parser.cc +++ b/src/bin/dhcp6/json_config_parser.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -525,25 +526,25 @@ configureDhcp6Server(Dhcpv6Srv&, isc::data::ConstElementPtr config_set, // Please move at the end when migration will be finished. if (config_pair.first == "lease-database") { - DbAccessParser parser(CfgDbAccess::LEASE_DB); + DbAccessParser parser(DBType::LEASE_DB); CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; } if (config_pair.first == "hosts-database") { - DbAccessParser parser(CfgDbAccess::HOSTS_DB); + DbAccessParser parser(DBType::HOSTS_DB); CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); parser.parse(cfg_db_access, config_pair.second); continue; } - // For now only support empty or singleton, ignoring extra entries. if (config_pair.first == "hosts-databases") { CfgDbAccessPtr cfg_db_access = srv_config->getCfgDbAccess(); - for (size_t i = 0; i < config_pair.second->size(); ++i) { - DbAccessParser parser(CfgDbAccess::HOSTS_DB + i); - parser.parse(cfg_db_access, config_pair.second->get(i)); + DbAccessParser parser(DBType::HOSTS_DB); + auto list = config_pair.second->listValue(); + for (auto it : list) { + parser.parse(cfg_db_access, it); } continue; } diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index 8910f69a7b..d9b63a55e0 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -6348,11 +6348,11 @@ TEST_F(Dhcp6ParserTest, hostsDatabases) { ConstCfgDbAccessPtr cfgdb = CfgMgr::instance().getStagingCfg()->getCfgDbAccess(); ASSERT_TRUE(cfgdb); - const std::vector& hal = cfgdb->getHostDbAccessStringList(); + const std::list& hal = cfgdb->getHostDbAccessStringList(); ASSERT_EQ(2, hal.size()); // Keywords are in alphabetical order - EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest", hal[0]); - EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest", hal[1]); + EXPECT_EQ("name=keatest1 password=keatest type=mysql user=keatest", hal.front()); + EXPECT_EQ("name=keatest2 password=keatest type=mysql user=keatest", hal.back()); } // This test checks comments. Please keep it last. diff --git a/src/lib/dhcpsrv/Makefile.am b/src/lib/dhcpsrv/Makefile.am index 9de9106eee..444bebffc3 100644 --- a/src/lib/dhcpsrv/Makefile.am +++ b/src/lib/dhcpsrv/Makefile.am @@ -116,6 +116,7 @@ libkea_dhcpsrv_la_SOURCES += daemon.cc daemon.h libkea_dhcpsrv_la_SOURCES += database_connection.cc database_connection.h libkea_dhcpsrv_la_SOURCES += db_exceptions.h libkea_dhcpsrv_la_SOURCES += db_log.cc db_log.h +libkea_dhcpsrv_la_SOURCES += db_type.h libkea_dhcpsrv_la_SOURCES += dhcp4o6_ipc.cc dhcp4o6_ipc.h libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h libkea_dhcpsrv_la_SOURCES += dhcpsrv_db_log.cc dhcpsrv_db_log.h @@ -275,6 +276,7 @@ libkea_dhcpsrv_include_HEADERS = \ daemon.h \ database_connection.h \ db_exceptions.h \ + db_type.h \ dhcp4o6_ipc.h \ dhcpsrv_log.h \ host.h \ diff --git a/src/lib/dhcpsrv/cfg_db_access.cc b/src/lib/dhcpsrv/cfg_db_access.cc index ff31f16a2a..77bc826b12 100644 --- a/src/lib/dhcpsrv/cfg_db_access.cc +++ b/src/lib/dhcpsrv/cfg_db_access.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -21,27 +22,31 @@ namespace isc { namespace dhcp { CfgDbAccess::CfgDbAccess() - : appended_parameters_(), db_access_(2) { - db_access_[LEASE_DB] = "type=memfile"; + : appended_parameters_(), lease_db_access_("type=memfile"), + host_db_access_() { } std::string CfgDbAccess::getLeaseDbAccessString() const { - return (getAccessString(db_access_[LEASE_DB])); + return (getAccessString(lease_db_access_)); } std::string CfgDbAccess::getHostDbAccessString() const { - return (getAccessString(db_access_[HOSTS_DB])); + if (host_db_access_.empty()) { + return (""); + } else { + return (getAccessString(host_db_access_.front())); + } } -std::vector +std::list CfgDbAccess::getHostDbAccessStringList() const { - std::vector ret; - for (size_t idx = HOSTS_DB; idx < db_access_.size(); ++idx) { - if (!db_access_[idx].empty()) { - ret.push_back(getAccessString(db_access_[idx])); + std::list ret; + for (const std::string& dbaccess : host_db_access_) { + if (!dbaccess.empty()) { + ret.push_back(getAccessString(dbaccess)); } } return (ret); @@ -55,14 +60,13 @@ CfgDbAccess::createManagers() const { // Recreate host data source. HostMgr::create(); - auto host_db_access_list = getHostDbAccessStringList(); - for (auto it = host_db_access_list.begin(); - it != host_db_access_list.end(); ++it) { - HostMgr::addSource(*it); + std::list host_db_access_list = getHostDbAccessStringList(); + for (std::string& hds : host_db_access_list) { + HostMgr::addBackend(hds); } // Check for a host cache. - HostMgr::checkCacheSource(true); + HostMgr::checkCacheBackend(true); } std::string diff --git a/src/lib/dhcpsrv/cfg_db_access.h b/src/lib/dhcpsrv/cfg_db_access.h index 2f8f95ea46..376bb814a7 100644 --- a/src/lib/dhcpsrv/cfg_db_access.h +++ b/src/lib/dhcpsrv/cfg_db_access.h @@ -10,7 +10,7 @@ #include #include #include -#include +#include namespace isc { namespace dhcp { @@ -22,10 +22,6 @@ namespace dhcp { /// passed to the @ref isc::dhcp::LeaseMgrFactory::create function. class CfgDbAccess { public: - /// @brief Specifies the database types - static const size_t LEASE_DB = 0; - static const size_t HOSTS_DB = 1; - /// @brief Constructor. CfgDbAccess(); @@ -48,7 +44,7 @@ public: /// /// @param lease_db_access New lease database access string. void setLeaseDbAccessString(const std::string& lease_db_access) { - db_access_[LEASE_DB] = lease_db_access; + lease_db_access_ = lease_db_access; } /// @brief Retrieves host database access string. @@ -60,22 +56,21 @@ public: /// @brief Sets host database access string. /// /// @param host_db_access New host database access string. - void setHostDbAccessString(const std::string& host_db_access) { - db_access_[HOSTS_DB] = host_db_access; + /// @param front Add at front if true, at back if false (default). + void setHostDbAccessString(const std::string& host_db_access, + bool front = false) { + if (front) { + host_db_access_.push_front(host_db_access); + } else { + host_db_access_.push_back(host_db_access); + } } /// @brief Retrieves host database access string. /// /// @return Database access strings with additional parameters /// specified with @ref CfgDbAccess::setAppendedParameters - std::vector getHostDbAccessStringList() const; - - /// @brief Pushes host database access string. - /// - /// @param db_access New host database access string. - void pushHostDbAccessString(const std::string& db_access) { - db_access_.push_back(db_access); - } + std::list getHostDbAccessStringList() const; /// @brief Creates instance of lease manager and host data sources /// according to the configuration specified. @@ -99,8 +94,11 @@ protected: /// strings. std::string appended_parameters_; - /// @brief Holds database access strings. - std::vector db_access_; + /// @brief Holds lease database access string. + std::string lease_db_access_; + + /// @brief Holds host database access strings. + std::list host_db_access_; }; @@ -121,7 +119,7 @@ struct CfgLeaseDbAccess : public CfgDbAccess, public isc::data::CfgToElement { /// /// @result a pointer to a configuration virtual isc::data::ElementPtr toElement() const { - return (CfgDbAccess::toElementDbAccessString(db_access_[LEASE_DB])); + return (CfgDbAccess::toElementDbAccessString(lease_db_access_)); } }; @@ -136,9 +134,9 @@ struct CfgHostDbAccess : public CfgDbAccess, public isc::data::CfgToElement { /// @result a pointer to a configuration virtual isc::data::ElementPtr toElement() const { isc::data::ElementPtr result = isc::data::Element::createList(); - for (size_t idx = HOSTS_DB; idx < db_access_.size(); ++idx) { + for (const std::string& dbaccess : host_db_access_) { isc::data::ElementPtr entry = - CfgDbAccess::toElementDbAccessString(db_access_[idx]); + CfgDbAccess::toElementDbAccessString(dbaccess); if (entry->size() > 0) { result->add(entry); } diff --git a/src/lib/dhcpsrv/database_backends.dox b/src/lib/dhcpsrv/database_backends.dox index 4bfbc7f040..29dbddba5e 100644 --- a/src/lib/dhcpsrv/database_backends.dox +++ b/src/lib/dhcpsrv/database_backends.dox @@ -105,4 +105,25 @@ For details, see @ref isc::dhcp::CqlConnection::openDatabase(). + @section dhcpdb-host Host Backends. + + Host backends (known also as host data sources) are similar to lease + backends with a few differences: + + - host backends are optional (so it is allowed to have none) because + the first source of host reservations is the server configuration, + others are alternate backends. + + - there may be more than one host backend. In such a case for lookups + returning a collection all results are appended, for lookups returning + at most one entry the first found is returned. Add operation is submitted + to all alternate backends which can ignore it, add the entry or throw + if the new entry conflicts with an already existing one. Delete + operations are submitted in sequence to all alternate backends until + one finds the entry, deletes it and returns true. + + - the first alternate backend can be a cache (host cache hook library + is a premium feature) which avoids to lookup slow databases. + For subnet ID and identifier negative caching is optionally supported. + */ diff --git a/src/lib/dhcpsrv/db_type.h b/src/lib/dhcpsrv/db_type.h new file mode 100644 index 0000000000..dc22fcd158 --- /dev/null +++ b/src/lib/dhcpsrv/db_type.h @@ -0,0 +1,22 @@ +// Copyright (C) 2018 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DB_TYPE_H +#define DB_TYPE_H + +namespace isc { +namespace dhcp { + +/// @brief Specifies the database type. +enum class DBType { + LEASE_DB = 1, + HOSTS_DB = 2 +}; + +} // namespace isc +} // namespace dhcp + +#endif diff --git a/src/lib/dhcpsrv/host.h b/src/lib/dhcpsrv/host.h index a92cda079f..5441727006 100644 --- a/src/lib/dhcpsrv/host.h +++ b/src/lib/dhcpsrv/host.h @@ -548,12 +548,15 @@ public: /// @brief Sets the negative cached flag. /// - /// @param negative sets whether this is a negative cached host. + /// @param negative sets whether this is a negative cached host, + /// i.e. a fake host in the cache which indicates non-existence + /// and avoids to lookup in a slow backend. void setNegative(bool negative) { negative_ = negative; } /// @brief Return the negative cache flag value. + /// When true standard lookup methods return null host pointer instead. bool getNegative() const { return (negative_); } diff --git a/src/lib/dhcpsrv/host_data_source_factory.cc b/src/lib/dhcpsrv/host_data_source_factory.cc index d393af61b1..dbe24f468b 100644 --- a/src/lib/dhcpsrv/host_data_source_factory.cc +++ b/src/lib/dhcpsrv/host_data_source_factory.cc @@ -96,6 +96,8 @@ HostDataSourceFactory::registerFactory(const string& db_type, return (false); } map_.insert(pair(db_type, factory)); + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, HOSTS_BACKEND_REGISTER) + .arg(db_type); return (true); } @@ -104,6 +106,8 @@ HostDataSourceFactory::deregisterFactory(const string& db_type) { auto index = map_.find(db_type); if (index != map_.end()) { map_.erase(index); + LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE, HOSTS_BACKEND_DEREGISTER) + .arg(db_type); return (true); } else { return (false); diff --git a/src/lib/dhcpsrv/host_mgr.cc b/src/lib/dhcpsrv/host_mgr.cc index 794fa7dcc4..54d72c553c 100644 --- a/src/lib/dhcpsrv/host_mgr.cc +++ b/src/lib/dhcpsrv/host_mgr.cc @@ -42,17 +42,17 @@ HostMgr::create() { } void -HostMgr::addSource(const std::string& access) { +HostMgr::addBackend(const std::string& access) { HostDataSourceFactory::add(getHostMgrPtr()->alternate_sources_, access); } bool -HostMgr::delSource(const std::string& db_type) { +HostMgr::delBackend(const std::string& db_type) { return (HostDataSourceFactory::del(getHostMgrPtr()->alternate_sources_, db_type)); } void -HostMgr::delAllSources() { +HostMgr::delAllBackends() { getHostMgrPtr()->alternate_sources_.clear(); } @@ -65,7 +65,7 @@ HostMgr::getHostDataSource() const { } bool -HostMgr::checkCacheSource(bool logging) { +HostMgr::checkCacheBackend(bool logging) { if (getHostMgrPtr()->cache_ptr_) { return (true); } @@ -98,9 +98,8 @@ HostMgr::instance() { ConstHostCollection HostMgr::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const { ConstHostCollection hosts = getCfgHosts()->getAll(hwaddr, duid); - for (auto it = alternate_sources_.begin(); - it != alternate_sources_.end(); ++it) { - ConstHostCollection hosts_plus = (*it)->getAll(hwaddr, duid); + for (auto source : alternate_sources_) { + ConstHostCollection hosts_plus = source->getAll(hwaddr, duid); hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end()); } return (hosts); @@ -113,10 +112,9 @@ HostMgr::getAll(const Host::IdentifierType& identifier_type, ConstHostCollection hosts = getCfgHosts()->getAll(identifier_type, identifier_begin, identifier_len); - for (auto it = alternate_sources_.begin(); - it != alternate_sources_.end(); ++it) { + for (auto source : alternate_sources_) { ConstHostCollection hosts_plus = - (*it)->getAll(identifier_type, identifier_begin, identifier_len); + source->getAll(identifier_type, identifier_begin, identifier_len); hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end()); } return (hosts); @@ -126,9 +124,8 @@ HostMgr::getAll(const Host::IdentifierType& identifier_type, ConstHostCollection HostMgr::getAll4(const IOAddress& address) const { ConstHostCollection hosts = getCfgHosts()->getAll4(address); - for (auto it = alternate_sources_.begin(); - it != alternate_sources_.end(); ++it) { - ConstHostCollection hosts_plus = (*it)->getAll4(address); + for (auto source : alternate_sources_) { + ConstHostCollection hosts_plus = source->getAll4(address); hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end()); } return (hosts); @@ -147,7 +144,8 @@ HostMgr::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr, .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)") .arg(duid ? duid->toText() : "(duid)"); for (auto it = alternate_sources_.begin(); - !host && it != alternate_sources_.end(); ++it) { + !host && // stop at first found + it != alternate_sources_.end(); ++it) { if (hwaddr) { host = (*it)->get4(subnet_id, hwaddr, DuidPtr()); } @@ -212,12 +210,7 @@ HostMgr::get4Any(const SubnetID& subnet_id, .arg(subnet_id) .arg(Host::getIdentifierAsText(identifier_type, identifier_begin, identifier_len)); - // @todo: remove this - if (negative_caching_) { - cacheNegative(subnet_id, SubnetID(0), - identifier_type, identifier_begin, identifier_len); - } - return (host); + return (ConstHostPtr()); } ConstHostPtr @@ -229,6 +222,9 @@ HostMgr::get4(const SubnetID& subnet_id, identifier_begin, identifier_len); if (host && host->getNegative()) { return (ConstHostPtr()); + } else if (!host && negative_caching_) { + cacheNegative(subnet_id, SubnetID(0), + identifier_type, identifier_begin, identifier_len); } return (host); } @@ -245,7 +241,8 @@ HostMgr::get4(const SubnetID& subnet_id, .arg(subnet_id) .arg(address.toText()); for (auto it = alternate_sources_.begin(); - !host && it != alternate_sources_.end(); ++it) { + !host && // stop at first found + it != alternate_sources_.end(); ++it) { host = (*it)->get4(subnet_id, address); if (host && cache_ptr_ && (it != alternate_sources_.begin())) { cache(host); @@ -272,7 +269,8 @@ HostMgr::get6(const SubnetID& subnet_id, const DuidPtr& duid, .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)"); for (auto it = alternate_sources_.begin(); - !host && it != alternate_sources_.end(); ++it) { + !host && // stop at first found + it != alternate_sources_.end(); ++it) { if (duid) { host = (*it)->get6(subnet_id, duid, HWAddrPtr()); } @@ -300,7 +298,8 @@ HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const { .arg(prefix.toText()) .arg(static_cast(prefix_len)); for (auto it = alternate_sources_.begin(); - !host && it != alternate_sources_.end(); ++it) { + !host && // stop at first found + it != alternate_sources_.end(); ++it) { host = (*it)->get6(prefix, prefix_len); if (host && cache_ptr_ && (it != alternate_sources_.begin())) { cache(host); @@ -358,13 +357,7 @@ HostMgr::get6Any(const SubnetID& subnet_id, .arg(Host::getIdentifierAsText(identifier_type, identifier_begin, identifier_len)); - // @todo: remove this - if (negative_caching_) { - cacheNegative(SubnetID(0), subnet_id, - identifier_type, identifier_begin, identifier_len); - } - - return (host); + return (ConstHostPtr()); } ConstHostPtr @@ -376,6 +369,9 @@ HostMgr::get6(const SubnetID& subnet_id, identifier_begin, identifier_len); if (host && host->getNegative()) { return (ConstHostPtr()); + } else if (!host && negative_caching_) { + cacheNegative(SubnetID(0), subnet_id, + identifier_type, identifier_begin, identifier_len); } return (host); } @@ -392,7 +388,8 @@ HostMgr::get6(const SubnetID& subnet_id, .arg(subnet_id) .arg(addr.toText()); for (auto it = alternate_sources_.begin(); - !host && it != alternate_sources_.end(); ++it) { + !host && // stop at first found + it != alternate_sources_.end(); ++it) { host = (*it)->get6(subnet_id, addr); if (host && cache_ptr_ && (it != alternate_sources_.begin())) { cache(host); @@ -410,11 +407,10 @@ HostMgr::add(const HostPtr& host) { isc_throw(NoHostDataSourceManager, "Unable to add new host because there is " "no hosts-database configured."); } - for (auto it = alternate_sources_.begin(); - it != alternate_sources_.end(); ++it) { - (*it)->add(host); + for (auto source : alternate_sources_) { + source->add(host); } - // If no backend throws it should be cached. + // If no backend throws the host should be cached. if (cache_ptr_) { cache(host); } @@ -427,9 +423,8 @@ HostMgr::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) { "no hosts-database configured."); } - for (auto it = alternate_sources_.begin(); - it != alternate_sources_.end(); ++it) { - if ((*it)->del(subnet_id, addr)) { + for (auto source : alternate_sources_) { + if (source->del(subnet_id, addr)) { return (true); } } @@ -444,10 +439,9 @@ HostMgr::del4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_ "no hosts-database configured."); } - for (auto it = alternate_sources_.begin(); - it != alternate_sources_.end(); ++it) { - if ((*it)->del4(subnet_id, identifier_type, - identifier_begin, identifier_len)) { + for (auto source : alternate_sources_) { + if (source->del4(subnet_id, identifier_type, + identifier_begin, identifier_len)) { return (true); } } @@ -462,10 +456,9 @@ HostMgr::del6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_ "no alternate host data source present"); } - for (auto it = alternate_sources_.begin(); - it != alternate_sources_.end(); ++it) { - if ((*it)->del6(subnet_id, identifier_type, - identifier_begin, identifier_len)) { + for (auto source : alternate_sources_) { + if (source->del6(subnet_id, identifier_type, + identifier_begin, identifier_len)) { return (true); } } diff --git a/src/lib/dhcpsrv/host_mgr.h b/src/lib/dhcpsrv/host_mgr.h index ebad4855b1..c6caf99671 100644 --- a/src/lib/dhcpsrv/host_mgr.h +++ b/src/lib/dhcpsrv/host_mgr.h @@ -63,33 +63,33 @@ public: /// static void create(); - /// @brief Add an alternate host data source. + /// @brief Add an alternate host backend (aka host data source). /// - /// @param access Host data source access parameters for the alternate - /// host data source. It holds "keyword=value" pairs, separated by spaces. - /// The supported values are specific to the alternate data source in use. + /// @param access Host backend access parameters for the alternate + /// host backend. It holds "keyword=value" pairs, separated by spaces. + /// The supported values are specific to the alternate backend in use. /// However, the "type" parameter will be common and it will specify which - /// data source is to be used. Currently, no parameters are supported + /// backend is to be used. Currently, no parameters are supported /// and the parameter is ignored. - static void addSource(const std::string& access); + static void addBackend(const std::string& access); - /// @brief Delete an alternate host data source. + /// @brief Delete an alternate host backend (aka host data source). /// /// @param db_type_type database backend type. /// @return true when found and removed, false when not found. - static bool delSource(const std::string& db_type); + static bool delBackend(const std::string& db_type); - /// @brief Delete all alternate host data source. - static void delAllSources(); + /// @brief Delete all alternate backends. + static void delAllBackends(); - /// @brief Check for the cache host data source. + /// @brief Check for the cache host backend. /// - /// Checks if the first host data source implements + /// Checks if the first host backend implements /// the cache abstract class and sets cache_ptr_. /// /// @param logging When true (not the default) emit an informational log. - /// @return true if the first host data source is a cache. - static bool checkCacheSource(bool logging = false); + /// @return true if the first host backend is a cache. + static bool checkCacheBackend(bool logging = false); /// @brief Returns a sole instance of the @c HostMgr. /// @@ -185,8 +185,7 @@ public: /// This method returns a single reservation for a particular host as /// documented in the @c BaseHostDataSource::get4 even when the /// reservation is marked as from negative caching. This allows to - /// overload negative caching with extra information in the user - /// context. + /// monitor negative caching. /// /// @param subnet_id Subnet identifier. /// @param identifier_type Identifier type. @@ -254,8 +253,7 @@ public: /// This method returns a host connected to the IPv6 subnet as described /// in the @c BaseHostDataSource::get6 even when the /// reservation is marked as from negative caching. This allows to - /// overload negative caching with extra information in the user - /// context. + /// monitor negative caching. /// /// @param subnet_id Subnet identifier. /// @param identifier_type Identifier type. diff --git a/src/lib/dhcpsrv/hosts_messages.mes b/src/lib/dhcpsrv/hosts_messages.mes index f939c115d0..54975efee1 100644 --- a/src/lib/dhcpsrv/hosts_messages.mes +++ b/src/lib/dhcpsrv/hosts_messages.mes @@ -6,6 +6,14 @@ $NAMESPACE isc::dhcp +% HOSTS_BACKEND_DEREGISTER deregistered backend type: %1 +This debug message is issued when a backend factory was deregistered. +It is no longer possible to use host backend of this type. + +% HOSTS_BACKEND_REGISTER registered backend type: %1 +This debug message is issued when a backend factory was successfully +registered. It is now possible to use host backend of this type. + % HOSTS_CFG_ADD_HOST add the host for reservations: %1 This debug message is issued when new host (with reservations) is added to the server's configuration. The argument describes the host and its diff --git a/src/lib/dhcpsrv/libdhcpsrv.dox b/src/lib/dhcpsrv/libdhcpsrv.dox index 22bc8b8888..3ab8e354f9 100644 --- a/src/lib/dhcpsrv/libdhcpsrv.dox +++ b/src/lib/dhcpsrv/libdhcpsrv.dox @@ -107,8 +107,8 @@ connection to the MySQL database and issue specific queries. A factory method creating an instance of a base host data source object must be registered (at global object initialization for built-in backends, dynamically for backends loaded at run-time). See host_data_source_factory.cc -for example code that registers MySQL and PostgreSQL. Note, that this instance is -created as "alternate host data source" as opposed to the primary data +for example code that registers MySQL and PostgreSQL. Note, that this instance +is created as "alternate host data source" as opposed to the primary data source which returns host reservations specified in the configuration file. The primary data source is implemented internally in the \ref isc::dhcp::HostMgr and uses the configuration data structures held by diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc index cd7b4e4179..24c52b3c32 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.cc +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -30,7 +31,7 @@ namespace dhcp { // Factory function to build the parser -DbAccessParser::DbAccessParser(size_t db_type) +DbAccessParser::DbAccessParser(DBType db_type) : values_(), type_(db_type) { } @@ -111,7 +112,7 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, StringPairMap::const_iterator type_ptr = values_copy.find("type"); if (type_ptr == values_copy.end()) { isc_throw(DhcpConfigError, - (type_ == CfgDbAccess::LEASE_DB ? "lease" : "host") + (type_ == DBType::LEASE_DB ? "lease" : "host") << " database access parameters must " "include the keyword 'type' to determine type of database " "to be accessed (" << database_config->getPosition() << ")"); @@ -168,12 +169,10 @@ DbAccessParser::parse(CfgDbAccessPtr& cfg_db, values_.swap(values_copy); // 5. Save the database access string in the Configuration Manager. - if (type_ == CfgDbAccess::LEASE_DB) { + if (type_ == DBType::LEASE_DB) { cfg_db->setLeaseDbAccessString(getDbAccessString()); - } else if (type_ == CfgDbAccess::HOSTS_DB) { - cfg_db->setHostDbAccessString(getDbAccessString()); - } else if (type_ > CfgDbAccess::HOSTS_DB) { - cfg_db->pushHostDbAccessString(getDbAccessString()); + } else if (type_ == DBType::HOSTS_DB) { + cfg_db->setHostDbAccessString(getDbAccessString(), false); } } diff --git a/src/lib/dhcpsrv/parsers/dbaccess_parser.h b/src/lib/dhcpsrv/parsers/dbaccess_parser.h index 94943ce154..e2013cc17e 100644 --- a/src/lib/dhcpsrv/parsers/dbaccess_parser.h +++ b/src/lib/dhcpsrv/parsers/dbaccess_parser.h @@ -37,7 +37,7 @@ public: /// @brief Constructor /// /// @param db_type Specifies database type (lease or hosts) - explicit DbAccessParser(size_t db_type); + explicit DbAccessParser(DBType db_type); /// The destructor. virtual ~DbAccessParser() @@ -92,7 +92,7 @@ private: std::map values_; ///< Stored parameter values - size_t type_; ///< Database type (leases or hosts) + DBType type_; ///< Database type (leases or hosts) }; }; // namespace dhcp diff --git a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc index ed04ee4a7b..d78bbf0e68 100644 --- a/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_db_access_unittest.cc @@ -76,20 +76,24 @@ TEST(CfgDbAccessTest, setHostDbAccessString) { runToElementTest(expected, CfgHostDbAccess(cfg)); // If access string is empty, no parameters will be appended. - ASSERT_NO_THROW(cfg.setHostDbAccessString("")); - EXPECT_TRUE(cfg.getHostDbAccessString().empty()); + CfgDbAccess cfg1; + ASSERT_NO_THROW(cfg1.setHostDbAccessString("")); + EXPECT_TRUE(cfg1.getHostDbAccessString().empty()); } // This test verifies that it is possible to set multiple host // database string. TEST(CfgDbAccessTest, pushHostDbAccessString) { + // Push a string. CfgDbAccess cfg; - ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql")); + ASSERT_NO_THROW(cfg.setHostDbAccessString("type=foo")); + + // Push another in front. + ASSERT_NO_THROW(cfg.setHostDbAccessString("type=mysql", true)); EXPECT_EQ("type=mysql", cfg.getHostDbAccessString()); - // Push two other strings - ASSERT_NO_THROW(cfg.pushHostDbAccessString("type=foo")); - ASSERT_NO_THROW(cfg.pushHostDbAccessString("type=bar")); + // Push a third string. + ASSERT_NO_THROW(cfg.setHostDbAccessString("type=bar")); // Check unparse std::string expected = "[ { \"type\": \"mysql\" }, "; @@ -97,20 +101,27 @@ TEST(CfgDbAccessTest, pushHostDbAccessString) { runToElementTest(expected, CfgHostDbAccess(cfg)); // Check access strings - std::vector hal = cfg.getHostDbAccessStringList(); + std::list hal = cfg.getHostDbAccessStringList(); ASSERT_EQ(3, hal.size()); - EXPECT_EQ("type=mysql", hal[0]); - EXPECT_EQ("type=foo", hal[1]); - EXPECT_EQ("type=bar", hal[2]); + std::list::const_iterator it = hal.cbegin(); + ASSERT_NE(hal.cend(), it); + EXPECT_EQ("type=mysql", *it); + ASSERT_NE(hal.cend(), ++it); + EXPECT_EQ("type=foo", *it); + ASSERT_NE(hal.cend(), ++it); + EXPECT_EQ("type=bar", *it); - // Reset the first string so it will be ignored. - ASSERT_NO_THROW(cfg.setHostDbAccessString("")); + // Build a similar list with the first string empty so it will be ignored. + CfgDbAccess cfg1; + ASSERT_NO_THROW(cfg1.setHostDbAccessString("")); + ASSERT_NO_THROW(cfg1.setHostDbAccessString("type=foo")); + ASSERT_NO_THROW(cfg1.setHostDbAccessString("type=bar")); expected = "[ { \"type\": \"foo\" }, { \"type\": \"bar\" } ]"; - runToElementTest(expected, CfgHostDbAccess(cfg)); - hal = cfg.getHostDbAccessStringList(); + runToElementTest(expected, CfgHostDbAccess(cfg1)); + hal = cfg1.getHostDbAccessStringList(); ASSERT_EQ(2, hal.size()); - EXPECT_EQ("type=foo", hal[0]); - EXPECT_EQ("type=bar", hal[1]); + EXPECT_EQ("type=foo", hal.front()); + EXPECT_EQ("type=bar", hal.back()); } // Tests that lease manager can be created from a specified configuration. diff --git a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc index 0850bbd19a..00a4636027 100644 --- a/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/cql_host_data_source_unittest.cc @@ -56,7 +56,7 @@ public: // Connect to the database try { HostMgr::create(); - HostMgr::addSource(validCqlConnectionString()); + HostMgr::addBackend(validCqlConnectionString()); } catch (...) { std::cerr << "*** ERROR: unable to open database. The test" "*** environment is broken and must be fixed before" @@ -77,7 +77,7 @@ public: // Rollback should never fail, as Cassandra doesn't support transactions // (commit and rollback are both no-op). } - HostMgr::delAllSources(); + HostMgr::delAllBackends(); hdsptr_.reset(); destroyCqlSchema(false, true); } @@ -107,7 +107,7 @@ public: /// the same database. void reopen(Universe) { HostMgr::create(); - HostMgr::addSource(validCqlConnectionString()); + HostMgr::addBackend(validCqlConnectionString()); hdsptr_ = HostMgr::instance().getHostDataSource(); } }; @@ -128,8 +128,8 @@ TEST(CqlHostDataSource, OpenDatabase) { // If it fails, print the error message. try { HostMgr::create(); - EXPECT_NO_THROW(HostMgr::addSource(validCqlConnectionString())); - HostMgr::delSource("cql"); + EXPECT_NO_THROW(HostMgr::addBackend(validCqlConnectionString())); + HostMgr::delBackend("cql"); } catch (const isc::Exception& ex) { FAIL() << "*** ERROR: unable to open database, reason:\n" << " " << ex.what() << "\n" @@ -143,8 +143,8 @@ TEST(CqlHostDataSource, OpenDatabase) { std::string connection_string = validCqlConnectionString() + std::string(" ") + std::string(VALID_TIMEOUT); HostMgr::create(); - EXPECT_NO_THROW(HostMgr::addSource(connection_string)); - HostMgr::delSource("cql"); + 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" @@ -159,34 +159,34 @@ TEST(CqlHostDataSource, OpenDatabase) { // Check that wrong specification of backend throws an exception. // (This is really a check on HostDataSourceFactory, but is convenient to // perform here.) - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)), InvalidParameter); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), InvalidType); // Check that invalid login data does not cause an exception, CQL should use // default values. - EXPECT_NO_THROW(HostMgr::addSource(connectionString(CQL_VALID_TYPE, + EXPECT_NO_THROW(HostMgr::addBackend(connectionString(CQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD))); - EXPECT_NO_THROW(HostMgr::addSource(connectionString(CQL_VALID_TYPE, + EXPECT_NO_THROW(HostMgr::addBackend(connectionString(CQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD))); - EXPECT_NO_THROW(HostMgr::addSource(connectionString(CQL_VALID_TYPE, + EXPECT_NO_THROW(HostMgr::addBackend(connectionString(CQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD))); - EXPECT_NO_THROW(HostMgr::addSource(connectionString(CQL_VALID_TYPE, + EXPECT_NO_THROW(HostMgr::addBackend(connectionString(CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD))); // Check that invalid timeouts throw DbOperationError. - EXPECT_THROW(HostMgr::addSource(connectionString(CQL_VALID_TYPE, + EXPECT_THROW(HostMgr::addBackend(connectionString(CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_1)), DbOperationError); - EXPECT_THROW(HostMgr::addSource(connectionString(CQL_VALID_TYPE, + EXPECT_THROW(HostMgr::addBackend(connectionString(CQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)), DbOperationError); // Check that CQL allows the hostname to not be specified. - EXPECT_NO_THROW(HostMgr::addSource(connectionString(CQL_VALID_TYPE, + EXPECT_NO_THROW(HostMgr::addBackend(connectionString(CQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD))); // Tidy up after the test diff --git a/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc index 052b881101..b963306f9c 100644 --- a/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc +++ b/src/lib/dhcpsrv/tests/dbaccess_parser_unittest.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -195,7 +196,7 @@ public: /// @brief Constructor /// /// @brief Keyword/value collection of database access parameters - TestDbAccessParser(size_t type) + TestDbAccessParser(DBType type) : DbAccessParser(type) {} @@ -243,7 +244,7 @@ TEST_F(DbAccessParserTest, validTypeMemfile) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid memfile", parser.getDbAccessParameters(), config); } @@ -257,7 +258,7 @@ TEST_F(DbAccessParserTest, hosts) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::HOSTS_DB); + TestDbAccessParser parser(DBType::HOSTS_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid memfile", parser.getDbAccessParameters(), config); } @@ -273,7 +274,7 @@ TEST_F(DbAccessParserTest, emptyKeyword) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid memfile", parser.getDbAccessParameters(), config); } @@ -290,7 +291,7 @@ TEST_F(DbAccessParserTest, persistV4Memfile) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid memfile", parser.getDbAccessParameters(), @@ -309,7 +310,7 @@ TEST_F(DbAccessParserTest, persistV6Memfile) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid memfile", parser.getDbAccessParameters(), @@ -328,7 +329,7 @@ TEST_F(DbAccessParserTest, validLFCInterval) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid LFC Interval", parser.getDbAccessParameters(), config); @@ -346,7 +347,7 @@ TEST_F(DbAccessParserTest, negativeLFCInterval) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -362,7 +363,7 @@ TEST_F(DbAccessParserTest, largeLFCInterval) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -378,7 +379,7 @@ TEST_F(DbAccessParserTest, validTimeout) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid timeout", parser.getDbAccessParameters(), config); @@ -396,7 +397,7 @@ TEST_F(DbAccessParserTest, negativeTimeout) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -412,7 +413,7 @@ TEST_F(DbAccessParserTest, largeTimeout) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -428,7 +429,7 @@ TEST_F(DbAccessParserTest, validPort) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid port", parser.getDbAccessParameters(), config); @@ -446,7 +447,7 @@ TEST_F(DbAccessParserTest, negativePort) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -462,7 +463,7 @@ TEST_F(DbAccessParserTest, largePort) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -480,7 +481,7 @@ TEST_F(DbAccessParserTest, validTypeMysql) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid mysql", parser.getDbAccessParameters(), config); } @@ -498,7 +499,7 @@ TEST_F(DbAccessParserTest, missingTypeKeyword) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -549,7 +550,7 @@ TEST_F(DbAccessParserTest, incrementalChanges) { "name", "keatest", NULL}; - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); // First configuration string should cause a representation of that string // to be held. @@ -613,7 +614,7 @@ TEST_F(DbAccessParserTest, getDbAccessString) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); // Get the database access string @@ -639,7 +640,7 @@ TEST_F(DbAccessParserTest, validReadOnly) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_NO_THROW(parser.parse(json_elements)); checkAccessString("Valid readonly parameter", @@ -661,7 +662,7 @@ TEST_F(DbAccessParserTest, invalidReadOnly) { ConstElementPtr json_elements = Element::fromJSON(json_config); EXPECT_TRUE(json_elements); - TestDbAccessParser parser(CfgDbAccess::LEASE_DB); + TestDbAccessParser parser(DBType::LEASE_DB); EXPECT_THROW(parser.parse(json_elements), DhcpConfigError); } @@ -679,8 +680,8 @@ TEST_F(DbAccessParserTest, multipleHost) { ConstElementPtr json_elements1 = Element::fromJSON(json_config1); ConstElementPtr json_elements2 = Element::fromJSON(json_config2); - TestDbAccessParser parser1(2); - TestDbAccessParser parser2(3); + TestDbAccessParser parser1(DBType::HOSTS_DB); + TestDbAccessParser parser2(DBType::HOSTS_DB); EXPECT_NO_THROW(parser1.parse(json_elements1)); EXPECT_NO_THROW(parser2.parse(json_elements2)); diff --git a/src/lib/dhcpsrv/tests/host_cache_unittest.cc b/src/lib/dhcpsrv/tests/host_cache_unittest.cc index f5ee61b1be..25b584f311 100644 --- a/src/lib/dhcpsrv/tests/host_cache_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_cache_unittest.cc @@ -125,7 +125,7 @@ public: return (hcptr_); }; HostDataSourceFactory::registerFactory("cache", cacheFactory); - HostMgr::addSource("type=cache"); + HostMgr::addBackend("type=cache"); // Host data source. memptr_.reset(new TestHostDataSource()); @@ -133,7 +133,7 @@ public: return (memptr_); }; HostDataSourceFactory::registerFactory("test", testFactory); - HostMgr::addSource("type=test"); + HostMgr::addBackend("type=test"); } /// @brief Destructor. @@ -153,7 +153,7 @@ public: TEST_F(HostCacheTest, identifier4) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->inserts_); ASSERT_TRUE(memptr_); @@ -204,7 +204,7 @@ TEST_F(HostCacheTest, identifier4) { TEST_F(HostCacheTest, identifier6) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->inserts_); ASSERT_TRUE(memptr_); @@ -261,7 +261,7 @@ TEST_F(HostCacheTest, identifier6) { TEST_F(HostCacheTest, address4) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->inserts_); ASSERT_TRUE(memptr_); @@ -310,7 +310,7 @@ TEST_F(HostCacheTest, address4) { TEST_F(HostCacheTest, address6) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->inserts_); ASSERT_TRUE(memptr_); @@ -365,7 +365,7 @@ TEST_F(HostCacheTest, address6) { TEST_F(HostCacheTest, negativeIdentifier4) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->adds_); ASSERT_TRUE(memptr_); @@ -376,7 +376,6 @@ TEST_F(HostCacheTest, negativeIdentifier4) { HostPtr host = HostDataSourceUtils::initializeHost4("192.0.2.1", Host::IDENT_HWADDR); ASSERT_TRUE(host); - // Try to get it cached. ConstHostPtr got = HostMgr::instance().get4(host->getIPv4SubnetID(), host->getIdentifierType(), @@ -396,10 +395,10 @@ TEST_F(HostCacheTest, negativeIdentifier4) { // Try it again. There is no such host, but this time negative cache is enabled, // so this negative response will be added to the cache. - got = HostMgr::instance().get4Any(host->getIPv4SubnetID(), - host->getIdentifierType(), - &host->getIdentifier()[0], - host->getIdentifier().size()); + got = HostMgr::instance().get4(host->getIPv4SubnetID(), + host->getIdentifierType(), + &host->getIdentifier()[0], + host->getIdentifier().size()); ASSERT_FALSE(got); EXPECT_EQ(1, hcptr_->size()); EXPECT_EQ(1, hcptr_->adds_); @@ -430,13 +429,22 @@ TEST_F(HostCacheTest, negativeIdentifier4) { EXPECT_EQ(1, hcptr_->size()); EXPECT_EQ(1, hcptr_->adds_); EXPECT_EQ(0, hcptr_->inserts_); + + // We can verify other overloads of get4() but the hwaddr/duid is + // not implemented by the memory test backend and the negative cache + // entry has no IP reservation simply because it was not known when + // it is created. And there is no by address negative cache because + // it is not allowed to create a host object, even a fake one, without + // an identifier. Now for performance the by identifier negative cache + // is critical, for by address it is better to optimize the out-of- + // pools case... } // Check negative cache feature for IPv6. TEST_F(HostCacheTest, negativeIdentifier6) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->adds_); ASSERT_TRUE(memptr_); @@ -467,12 +475,22 @@ TEST_F(HostCacheTest, negativeIdentifier6) { HostMgr::instance().setNegativeCaching(true); ASSERT_TRUE(HostMgr::instance().getNegativeCaching()); - // Try it but it will be cached only the second time. + // Try it but it will be cached only when get6 (vs get6Any) is called. got = HostMgr::instance().get6Any(host->getIPv6SubnetID(), host->getIdentifierType(), &host->getIdentifier()[0], host->getIdentifier().size()); ASSERT_FALSE(got); + EXPECT_EQ(0, hcptr_->size()); + EXPECT_EQ(0, hcptr_->adds_); + + got = HostMgr::instance().get6(host->getIPv6SubnetID(), + host->getIdentifierType(), + &host->getIdentifier()[0], + host->getIdentifier().size()); + ASSERT_FALSE(got); + + // There is a negative cached value now. EXPECT_EQ(1, hcptr_->size()); EXPECT_EQ(1, hcptr_->adds_); got = HostMgr::instance().get6Any(host->getIPv6SubnetID(), @@ -502,13 +520,15 @@ TEST_F(HostCacheTest, negativeIdentifier6) { EXPECT_EQ(1, hcptr_->size()); EXPECT_EQ(1, hcptr_->adds_); EXPECT_EQ(0, hcptr_->inserts_); + + // No other tests, cf negativeIdentifier4 end comment. } // Check that negative caching by address is not done for IPv4. TEST_F(HostCacheTest, negativeAddress4) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->adds_); ASSERT_TRUE(memptr_); @@ -538,7 +558,7 @@ TEST_F(HostCacheTest, negativeAddress4) { TEST_F(HostCacheTest, negativeAddress6) { // Check we have what we need. ASSERT_TRUE(hcptr_); - EXPECT_TRUE(HostMgr::checkCacheSource()); + EXPECT_TRUE(HostMgr::checkCacheBackend()); EXPECT_EQ(0, hcptr_->size()); EXPECT_EQ(0, hcptr_->adds_); ASSERT_TRUE(memptr_); diff --git a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc index 3210c84ecd..2383b8a516 100644 --- a/src/lib/dhcpsrv/tests/host_mgr_unittest.cc +++ b/src/lib/dhcpsrv/tests/host_mgr_unittest.cc @@ -350,6 +350,13 @@ HostMgrTest::testGet4Any() { EXPECT_EQ(1, host->getIPv4SubnetID()); EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText()); EXPECT_TRUE(host->getNegative()); + + // To be sure. Note we use the CfgHosts source so only this + // get4 overload works. + host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_DUID, + &duids_[0]->getDuid()[0], + duids_[0]->getDuid().size()); + EXPECT_FALSE(host); } void @@ -424,6 +431,13 @@ HostMgrTest::testGet6Any() { EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::1")))); EXPECT_TRUE(host->getNegative()); + + // To be sure. Note we use the CfgHosts source so only this + // get6 overload works. + host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_HWADDR, + &hwaddrs_[0]->hwaddr_[0], + hwaddrs_[0]->hwaddr_.size()); + EXPECT_FALSE(host); } void @@ -544,7 +558,7 @@ MySQLHostMgrTest::SetUp() { // Connect to the database try { - HostMgr::addSource(test::validMySQLConnectionString()); + HostMgr::addBackend(test::validMySQLConnectionString()); } catch (...) { std::cerr << "*** ERROR: unable to open database. The test\n" "*** environment is broken and must be fixed before\n" @@ -558,7 +572,7 @@ MySQLHostMgrTest::SetUp() { void MySQLHostMgrTest::TearDown() { HostMgr::instance().getHostDataSource()->rollback(); - HostMgr::delSource("mysql"); + HostMgr::delBackend("mysql"); test::destroyMySQLSchema(); } @@ -621,7 +635,7 @@ PostgreSQLHostMgrTest::SetUp() { // Connect to the database try { - HostMgr::addSource(test::validPgSQLConnectionString()); + HostMgr::addBackend(test::validPgSQLConnectionString()); } catch (...) { std::cerr << "*** ERROR: unable to open database. The test\n" "*** environment is broken and must be fixed before\n" @@ -635,7 +649,7 @@ PostgreSQLHostMgrTest::SetUp() { void PostgreSQLHostMgrTest::TearDown() { HostMgr::instance().getHostDataSource()->rollback(); - HostMgr::delSource("postgresql"); + HostMgr::delBackend("postgresql"); test::destroyPgSQLSchema(); } diff --git a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc index 36f7fe8e5f..6e4937cb9f 100644 --- a/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc @@ -49,7 +49,7 @@ public: // Connect to the database try { HostMgr::create(); - HostMgr::addSource(validMySQLConnectionString()); + HostMgr::addBackend(validMySQLConnectionString()); } catch (...) { std::cerr << "*** ERROR: unable to open database. The test\n" "*** environment is broken and must be fixed before\n" @@ -72,7 +72,7 @@ public: } catch (...) { // Rollback may fail if backend is in read only mode. That's ok. } - HostMgr::delAllSources(); + HostMgr::delAllBackends(); hdsptr_.reset(); destroyMySQLSchema(); } @@ -86,7 +86,7 @@ public: /// the same database. void reopen(Universe) { HostMgr::create(); - HostMgr::addSource(validMySQLConnectionString()); + HostMgr::addBackend(validMySQLConnectionString()); hdsptr_ = HostMgr::instance().getHostDataSource(); } @@ -152,8 +152,8 @@ TEST(MySqlHostDataSource, OpenDatabase) { // If it fails, print the error message. try { HostMgr::create(); - EXPECT_NO_THROW(HostMgr::addSource(validMySQLConnectionString())); - HostMgr::delSource("mysql"); + EXPECT_NO_THROW(HostMgr::addBackend(validMySQLConnectionString())); + HostMgr::delBackend("mysql"); } catch (const isc::Exception& ex) { FAIL() << "*** ERROR: unable to open database, reason:\n" << " " << ex.what() << "\n" @@ -167,8 +167,8 @@ TEST(MySqlHostDataSource, OpenDatabase) { string connection_string = validMySQLConnectionString() + string(" ") + string(VALID_TIMEOUT); HostMgr::create(); - EXPECT_NO_THROW(HostMgr::addSource(connection_string)); - HostMgr::delSource("mysql"); + EXPECT_NO_THROW(HostMgr::addBackend(connection_string)); + HostMgr::delBackend("mysql"); } catch (const isc::Exception& ex) { FAIL() << "*** ERROR: unable to open database, reason:\n" << " " << ex.what() << "\n" @@ -183,38 +183,38 @@ TEST(MySqlHostDataSource, OpenDatabase) { // Check that wrong specification of backend throws an exception. // (This is really a check on LeaseMgrFactory, but is convenient to // perform here.) - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)), InvalidParameter); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), InvalidType); // Check that invalid login data causes an exception. - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_1)), DbInvalidTimeout); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)), DbInvalidTimeout); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, VALID_TIMEOUT, INVALID_READONLY_DB)), DbInvalidReadOnly); // Check for missing parameters - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( MYSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)), NoDatabaseName); diff --git a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc index 2824728702..33c11746a9 100644 --- a/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc @@ -49,7 +49,7 @@ public: // Connect to the database try { HostMgr::create(); - HostMgr::addSource(validPgSQLConnectionString()); + HostMgr::addBackend(validPgSQLConnectionString()); } catch (...) { std::cerr << "*** ERROR: unable to open database. The test\n" "*** environment is broken and must be fixed before\n" @@ -73,7 +73,7 @@ public: } catch (...) { // Rollback may fail if backend is in read only mode. That's ok. } - HostMgr::delAllSources(); + HostMgr::delAllBackends(); hdsptr_.reset(); destroyPgSQLSchema(); } @@ -87,7 +87,7 @@ public: /// share the same database. void reopen(Universe) { HostMgr::create(); - HostMgr::addSource(validPgSQLConnectionString()); + HostMgr::addBackend(validPgSQLConnectionString()); hdsptr_ = HostMgr::instance().getHostDataSource(); } @@ -152,8 +152,8 @@ TEST(PgSqlHostDataSource, OpenDatabase) { // If it fails, print the error message. try { HostMgr::create(); - EXPECT_NO_THROW(HostMgr::addSource(validPgSQLConnectionString())); - HostMgr::delSource("postgresql"); + EXPECT_NO_THROW(HostMgr::addBackend(validPgSQLConnectionString())); + HostMgr::delBackend("postgresql"); } catch (const isc::Exception& ex) { FAIL() << "*** ERROR: unable to open database, reason:\n" << " " << ex.what() << "\n" @@ -166,8 +166,8 @@ TEST(PgSqlHostDataSource, OpenDatabase) { try { string connection_string = validPgSQLConnectionString() + string(" ") + string(VALID_TIMEOUT); - EXPECT_NO_THROW(HostMgr::addSource(connection_string)); - HostMgr::delSource("postgresql"); + EXPECT_NO_THROW(HostMgr::addBackend(connection_string)); + HostMgr::delBackend("postgresql"); } catch (const isc::Exception& ex) { FAIL() << "*** ERROR: unable to open database, reason:\n" << " " << ex.what() << "\n" @@ -182,35 +182,35 @@ TEST(PgSqlHostDataSource, OpenDatabase) { // Check that wrong specification of backend throws an exception. // (This is really a check on LeaseMgrFactory, but is convenient to // perform here.) - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( NULL, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)), InvalidParameter); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( INVALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), InvalidType); // Check that invalid login data causes an exception. - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( PGSQL_VALID_TYPE, INVALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( PGSQL_VALID_TYPE, VALID_NAME, INVALID_HOST, VALID_USER, VALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, INVALID_USER, VALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, INVALID_PASSWORD)), DbOpenError); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_1)), DbInvalidTimeout); - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( PGSQL_VALID_TYPE, VALID_NAME, VALID_HOST, VALID_USER, VALID_PASSWORD, INVALID_TIMEOUT_2)), DbInvalidTimeout); // Check for missing parameters - EXPECT_THROW(HostMgr::addSource(connectionString( + EXPECT_THROW(HostMgr::addBackend(connectionString( PGSQL_VALID_TYPE, NULL, VALID_HOST, INVALID_USER, VALID_PASSWORD)), NoDatabaseName); diff --git a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc index 2cb1f461ec..0c2b3f3e95 100644 --- a/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc +++ b/src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc @@ -212,7 +212,7 @@ GenericHostDataSourceTest::testReadOnlyDatabase(const char* valid_db_type) { // Close the database connection and reopen in "read-only" mode as // specified by the "VALID_READONLY_DB" parameter. HostMgr::create(); - HostMgr::addSource(connectionString( + HostMgr::addBackend(connectionString( valid_db_type, VALID_NAME, VALID_HOST, VALID_READONLY_USER, VALID_PASSWORD, VALID_READONLY_DB)); diff --git a/src/lib/dhcpsrv/testutils/memory_host_data_source.h b/src/lib/dhcpsrv/testutils/memory_host_data_source.h index 3b125042ec..906a3630b2 100644 --- a/src/lib/dhcpsrv/testutils/memory_host_data_source.h +++ b/src/lib/dhcpsrv/testutils/memory_host_data_source.h @@ -137,6 +137,8 @@ public: /// @brief Returns a host using the specified IPv6 prefix. /// + /// Currently not implemented. + /// /// @param prefix IPv6 prefix for which the @c Host object is searched. /// @param prefix_len IPv6 prefix length. /// @return Const @c Host object using a specified HW address or DUID. @@ -220,7 +222,10 @@ public: return (std::make_pair(0,0)); } + /// Specific methods. + /// @brief Returns store size. + /// /// @return number of hosts in the store. virtual size_t size() const;