From: Francis Dupont Date: Thu, 30 May 2024 23:14:18 +0000 (+0200) Subject: [#3375] Made cfg_host MT safe X-Git-Tag: Kea-2.7.0~58 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40827e15c3b76adad3d8a1f3327c4951295455bf;p=thirdparty%2Fkea.git [#3375] Made cfg_host MT safe --- diff --git a/src/lib/dhcpsrv/cfg_hosts.cc b/src/lib/dhcpsrv/cfg_hosts.cc index 433aabe2e9..2888e947ea 100644 --- a/src/lib/dhcpsrv/cfg_hosts.cc +++ b/src/lib/dhcpsrv/cfg_hosts.cc @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -20,11 +21,15 @@ using namespace isc::asiolink; using namespace isc::data; +using namespace isc::util; using namespace std; namespace isc { namespace dhcp { +CfgHosts::CfgHosts() : mutex_(new mutex()) { +} + ConstHostCollection CfgHosts::getAll(const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, @@ -32,6 +37,7 @@ CfgHosts::getAll(const Host::IdentifierType& identifier_type, // Do not issue logging message here because it will be logged by // the getAllInternal method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal(identifier_type, identifier_begin, identifier_len, collection); return (collection); @@ -43,6 +49,7 @@ CfgHosts::getAll(const Host::IdentifierType& identifier_type, // Do not issue logging message here because it will be logged by // the getAllInternal method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal(identifier_type, identifier_begin, identifier_len, collection); return (collection); @@ -53,6 +60,7 @@ CfgHosts::getAll4(const SubnetID& subnet_id) const { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal4(subnet_id, collection); return (collection); } @@ -62,6 +70,7 @@ CfgHosts::getAll4(const SubnetID& subnet_id) { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal4(subnet_id, collection); return (collection); } @@ -71,6 +80,7 @@ CfgHosts::getAll6(const SubnetID& subnet_id) const { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal6(subnet_id, collection); return (collection); } @@ -80,6 +90,7 @@ CfgHosts::getAll6(const SubnetID& subnet_id) { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal6(subnet_id, collection); return (collection); } @@ -89,6 +100,7 @@ CfgHosts::getAllbyHostname(const std::string& hostname) const { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllbyHostnameInternal(hostname, collection); return (collection); } @@ -98,6 +110,7 @@ CfgHosts::getAllbyHostname(const std::string& hostname) { // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllbyHostnameInternal(hostname, collection); return (collection); } @@ -108,6 +121,7 @@ CfgHosts::getAllbyHostname4(const std::string& hostname, // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal4 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllbyHostnameInternal4(hostname, subnet_id, collection); return (collection); } @@ -118,6 +132,7 @@ CfgHosts::getAllbyHostname4(const std::string& hostname, // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal4 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllbyHostnameInternal4(hostname, subnet_id, collection); return (collection); } @@ -128,6 +143,7 @@ CfgHosts::getAllbyHostname6(const std::string& hostname, // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal6 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllbyHostnameInternal6(hostname, subnet_id, collection); return (collection); } @@ -138,6 +154,7 @@ CfgHosts::getAllbyHostname6(const std::string& hostname, // Do not issue logging message here because it will be logged by // the getAllbyHostnameInternal6 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllbyHostnameInternal6(hostname, subnet_id, collection); return (collection); } @@ -150,6 +167,7 @@ CfgHosts::getPage4(const SubnetID& subnet_id, // Do not issue logging message here because it will be logged by // the getPageInternal4 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal4(subnet_id, lower_host_id, page_size, @@ -165,6 +183,7 @@ CfgHosts::getPage4(const SubnetID& subnet_id, // Do not issue logging message here because it will be logged by // the getPageInternal4 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal4(subnet_id, lower_host_id, page_size, @@ -180,6 +199,7 @@ CfgHosts::getPage6(const SubnetID& subnet_id, // Do not issue logging message here because it will be logged by // the getPageInternal6 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal6(subnet_id, lower_host_id, page_size, @@ -195,6 +215,7 @@ CfgHosts::getPage6(const SubnetID& subnet_id, // Do not issue logging message here because it will be logged by // the getPageInternal6 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal6(subnet_id, lower_host_id, page_size, @@ -209,6 +230,7 @@ CfgHosts::getPage4(size_t& /*source_index*/, // Do not issue logging message here because it will be logged by // the getPageInternal method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal(lower_host_id, page_size, collection); @@ -222,6 +244,7 @@ CfgHosts::getPage4(size_t& /*source_index*/, // Do not issue logging message here because it will be logged by // the getPageInternal method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal(lower_host_id, page_size, collection); @@ -235,6 +258,7 @@ CfgHosts::getPage6(size_t& /*source_index*/, // Do not issue logging message here because it will be logged by // the getPageInternal method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal(lower_host_id, page_size, collection); @@ -248,6 +272,7 @@ CfgHosts::getPage6(size_t& /*source_index*/, // Do not issue logging message here because it will be logged by // the getPageInternal method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getPageInternal(lower_host_id, page_size, collection); @@ -259,6 +284,7 @@ CfgHosts::getAll4(const IOAddress& address) const { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal4(address, collection); return (collection); } @@ -268,6 +294,7 @@ CfgHosts::getAll4(const IOAddress& address) { // Do not issue logging message here because it will be logged by // the getAllInternal4 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal4(address, collection); return (collection); } @@ -277,6 +304,7 @@ CfgHosts::getAll6(const IOAddress& address) const { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. ConstHostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal6(address, collection); return (collection); } @@ -286,6 +314,7 @@ CfgHosts::getAll6(const IOAddress& address) { // Do not issue logging message here because it will be logged by // the getAllInternal6 method. HostCollection collection; + MultiThreadingLock lock(*mutex_); getAllInternal6(address, collection); return (collection); } @@ -632,6 +661,42 @@ CfgHosts::getAllInternal4(const IOAddress& address, Storage& storage) const { .arg(storage.size()); } +ConstHostCollection +CfgHosts::getAllInternal4(const SubnetID& subnet_id, + const IOAddress& address) const { + LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4) + .arg(subnet_id) + .arg(address.toText()); + + // Must not specify address other than IPv4. + if (!address.isV4()) { + isc_throw(BadHostAddress, "must specify an IPv4 address when searching" + " for a host, specified address was " << address); + } + // Search for the Host using the reserved IPv4 address as a key. + ConstHostCollection hosts; + const HostContainerIndex1& idx = hosts_.get<1>(); + HostContainerIndex1Range r = idx.equal_range(address); + // Append each Host object to the storage. + BOOST_FOREACH(auto const& host, r) { + if (host->getIPv4SubnetID() == subnet_id) { + LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, + HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST) + .arg(subnet_id) + .arg(address.toText()) + .arg(host->toText()); + hosts.push_back(host); + } + } + + LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, + HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT) + .arg(subnet_id) + .arg(address.toText()) + .arg(hosts.size()); + return (hosts); +} + template void CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const { @@ -665,6 +730,7 @@ CfgHosts::get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) const { + MultiThreadingLock lock(*mutex_); return (getHostInternal(subnet_id, false, identifier_type, identifier_begin, identifier_len)); } @@ -674,6 +740,7 @@ CfgHosts::get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) { + MultiThreadingLock lock(*mutex_); return (getHostInternal(subnet_id, false, identifier_type, identifier_begin, identifier_len)); } @@ -683,46 +750,29 @@ CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const { LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4) .arg(subnet_id).arg(address.toText()); - ConstHostCollection hosts = getAll4(address); - for (auto const& host : hosts) { - if (host->getIPv4SubnetID() == subnet_id) { - LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, - HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_HOST) - .arg(subnet_id) - .arg(address.toText()) - .arg(host->toText()); - return (host); - } + ConstHostCollection hosts = getAllInternal4(subnet_id, address); + if (hosts.empty()) { + LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, + HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_NULL) + .arg(subnet_id) + .arg(address.toText()); + return (ConstHostPtr()); + } else { + ConstHostPtr host = *hosts.begin(); + LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, + HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_HOST) + .arg(subnet_id) + .arg(address.toText()) + .arg(host->toText()); + return (host); } - - LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_NULL) - .arg(subnet_id).arg(address.toText()); - return (ConstHostPtr()); } ConstHostCollection CfgHosts::getAll4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { - LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4) - .arg(subnet_id).arg(address.toText()); - - ConstHostCollection hosts; - for (auto const& host : getAll4(address)) { - if (host->getIPv4SubnetID() == subnet_id) { - LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA, - HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST) - .arg(subnet_id) - .arg(address.toText()) - .arg(host->toText()); - hosts.push_back(host); - } - } - LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT) - .arg(subnet_id) - .arg(address.toText()) - .arg(hosts.size()); - - return (hosts); + MultiThreadingLock lock(*mutex_); + return (getAllInternal4(subnet_id, address)); } ConstHostPtr @@ -730,6 +780,7 @@ CfgHosts::get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) const { + MultiThreadingLock lock(*mutex_); return (getHostInternal(subnet_id, true, identifier_type, identifier_begin, identifier_len)); } @@ -739,17 +790,20 @@ CfgHosts::get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type, const uint8_t* identifier_begin, const size_t identifier_len) { + MultiThreadingLock lock(*mutex_); return (getHostInternal(subnet_id, true, identifier_type, identifier_begin, identifier_len)); } ConstHostPtr CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) const { + MultiThreadingLock lock(*mutex_); return (getHostInternal6(prefix, prefix_len)); } HostPtr CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) { + MultiThreadingLock lock(*mutex_); return (getHostInternal6(prefix, prefix_len)); } @@ -757,6 +811,7 @@ ConstHostPtr CfgHosts::get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { // Do not log here because getHostInternal6 logs. + MultiThreadingLock lock(*mutex_); return (getHostInternal6(subnet_id, address)); } @@ -764,6 +819,7 @@ HostPtr CfgHosts::get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) { // Do not log here because getHostInternal6 logs. + MultiThreadingLock lock(*mutex_); return (getHostInternal6(subnet_id, address)); } @@ -771,6 +827,7 @@ ConstHostCollection CfgHosts::getAll6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const { ConstHostCollection hosts; + MultiThreadingLock lock(*mutex_); getAllInternal6(subnet_id, address, hosts); return (hosts); } @@ -963,6 +1020,8 @@ CfgHosts::add(const HostPtr& host) { " 0 when adding new host reservation"); } + MultiThreadingLock lock(*mutex_); + add4(host); add6(host); @@ -977,16 +1036,16 @@ CfgHosts::add4(const HostPtr& host) { // Check for duplicates for the specified IPv4 subnet. if (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) { if (hwaddr && !hwaddr->hwaddr_.empty() && - get4(host->getIPv4SubnetID(), Host::IDENT_HWADDR, - &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) { + getHostInternal(host->getIPv4SubnetID(), false, Host::IDENT_HWADDR, + &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) { isc_throw(DuplicateHost, "failed to add new host using the HW" << " address '" << hwaddr->toText(false) << "' to the IPv4 subnet id '" << host->getIPv4SubnetID() << "' as this host has already been added"); } if (duid && !duid->getDuid().empty() && - get4(host->getIPv4SubnetID(), Host::IDENT_DUID, - &duid->getDuid()[0], duid->getDuid().size())) { + getHostInternal(host->getIPv4SubnetID(), false, Host::IDENT_DUID, + &duid->getDuid()[0], duid->getDuid().size())) { isc_throw(DuplicateHost, "failed to add new host using the " << "DUID '" << duid->toText() << "' to the IPv4 subnet id '" << host->getIPv4SubnetID() @@ -995,16 +1054,16 @@ CfgHosts::add4(const HostPtr& host) { // Check for duplicates for the specified IPv6 subnet. } else if (host->getIPv6SubnetID() != SUBNET_ID_UNUSED) { if (duid && !duid->getDuid().empty() && - get6(host->getIPv6SubnetID(), Host::IDENT_DUID, - &duid->getDuid()[0], duid->getDuid().size())) { + getHostInternal(host->getIPv6SubnetID(), true, Host::IDENT_DUID, + &duid->getDuid()[0], duid->getDuid().size())) { isc_throw(DuplicateHost, "failed to add new host using the " << "DUID '" << duid->toText() << "' to the IPv6 subnet id '" << host->getIPv6SubnetID() << "' as this host has already been added"); } if (hwaddr && !hwaddr->hwaddr_.empty() && - get6(host->getIPv6SubnetID(), Host::IDENT_HWADDR, - &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) { + getHostInternal(host->getIPv6SubnetID(), true, Host::IDENT_HWADDR, + &hwaddr->hwaddr_[0], hwaddr->hwaddr_.size())) { isc_throw(DuplicateHost, "failed to add new host using the HW" << " address '" << hwaddr->toText(false) << "' to the IPv6 subnet id '" << host->getIPv6SubnetID() @@ -1015,7 +1074,8 @@ CfgHosts::add4(const HostPtr& host) { // Check if the address is already reserved for the specified IPv4 subnet. if (ip_reservations_unique_ && !host->getIPv4Reservation().isV4Zero() && (host->getIPv4SubnetID() != SUBNET_ID_UNUSED) && - get4(host->getIPv4SubnetID(), host->getIPv4Reservation())) { + !getAllInternal4(host->getIPv4SubnetID(), + host->getIPv4Reservation()).empty()) { isc_throw(ReservedAddress, "failed to add new host using the HW" " address '" << (hwaddr ? hwaddr->toText(false) : "(null)") << " and DUID '" << (duid ? duid->toText() : "(null)") @@ -1027,8 +1087,8 @@ CfgHosts::add4(const HostPtr& host) { // Check if the (identifier type, identifier) tuple is already used. const std::vector& id = host->getIdentifier(); if ((host->getIPv4SubnetID() != SUBNET_ID_UNUSED) && !id.empty()) { - if (get4(host->getIPv4SubnetID(), host->getIdentifierType(), &id[0], - id.size())) { + if (getHostInternal(host->getIPv4SubnetID(), false, + host->getIdentifierType(), &id[0], id.size())) { isc_throw(DuplicateHost, "failed to add duplicate IPv4 host using identifier: " << Host::getIdentifierAsText(host->getIdentifierType(), &id[0], id.size())); @@ -1066,7 +1126,8 @@ CfgHosts::add6(const HostPtr& host) { if (ip_reservations_unique_) { // If there's an entry for this (subnet-id, address), reject it. - if (get6(host->getIPv6SubnetID(), it.second.getPrefix())) { + if (getHostInternal6 + (host->getIPv6SubnetID(), it.second.getPrefix())) { isc_throw(DuplicateHost, "failed to add address reservation for " << "host using the HW address '" << (hwaddr ? hwaddr->toText(false) : "(null)") @@ -1084,10 +1145,16 @@ bool CfgHosts::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) { size_t erased_hosts = 0; size_t erased_addresses = 0; + MultiThreadingLock lock(*mutex_); if (addr.isV4()) { HostContainerIndex4& idx = hosts_.get<4>(); + ConstHostCollection hosts; + getAllInternal4(addr, hosts); // Delete IPv4 reservation and host. - for (auto const& host : getAll4(subnet_id, addr)) { + for (auto const& host : hosts) { + if (host->getIPv4SubnetID() != subnet_id) { + continue; + } erased_hosts += idx.erase(host->getHostId()); } erased_addresses = erased_hosts; @@ -1116,6 +1183,7 @@ CfgHosts::del(const SubnetID& subnet_id, const asiolink::IOAddress& addr) { size_t CfgHosts::delAll4(const SubnetID& subnet_id) { HostContainerIndex2& idx = hosts_.get<2>(); + MultiThreadingLock lock(*mutex_); size_t erased = idx.erase(subnet_id); LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_DEL_ALL_SUBNET4) @@ -1131,6 +1199,7 @@ CfgHosts::del4(const SubnetID& subnet_id, const uint8_t* identifier_begin, const size_t identifier_len) { HostContainerIndex0& idx = hosts_.get<0>(); + MultiThreadingLock lock(*mutex_); auto const t = boost::make_tuple(std::vector(identifier_begin, identifier_begin + identifier_len), identifier_type); @@ -1159,6 +1228,7 @@ size_t CfgHosts::delAll6(const SubnetID& subnet_id) { // Delete IPv6 reservations. HostContainer6Index2& idx6 = hosts6_.get<2>(); + MultiThreadingLock lock(*mutex_); size_t erased_addresses = idx6.erase(subnet_id); // Delete hosts. @@ -1184,6 +1254,7 @@ CfgHosts::del6(const SubnetID& subnet_id, auto const t = boost::make_tuple(std::vector(identifier_begin, identifier_begin + identifier_len), identifier_type); + MultiThreadingLock lock(*mutex_); auto const& range = idx.equal_range(t); size_t erased_hosts = 0; size_t erased_reservations = 0; @@ -1213,6 +1284,7 @@ CfgHosts::del6(const SubnetID& subnet_id, bool CfgHosts::setIPReservationsUnique(const bool unique) { + MultiThreadingLock lock(*mutex_); ip_reservations_unique_ = unique; return (true); } @@ -1236,6 +1308,7 @@ CfgHosts::toElement4() const { CfgHostsList result; // Iterate using arbitrary the index 0 const HostContainerIndex0& idx = hosts_.get<0>(); + MultiThreadingLock lock(*mutex_); for (auto const& host : idx) { // Convert host to element representation @@ -1253,6 +1326,7 @@ CfgHosts::toElement6() const { CfgHostsList result; // Iterate using arbitrary the index 0 const HostContainerIndex0& idx = hosts_.get<0>(); + MultiThreadingLock lock(*mutex_); for (auto const& host : idx) { // Convert host to Element representation diff --git a/src/lib/dhcpsrv/cfg_hosts.h b/src/lib/dhcpsrv/cfg_hosts.h index fb6226a4e0..7b675a93c9 100644 --- a/src/lib/dhcpsrv/cfg_hosts.h +++ b/src/lib/dhcpsrv/cfg_hosts.h @@ -1,4 +1,4 @@ -// Copyright (C) 2014-2023 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2014-2024 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 @@ -14,7 +14,10 @@ #include #include #include +#include #include +#include +#include #include namespace isc { @@ -38,6 +41,9 @@ class CfgHosts : public BaseHostDataSource, public WritableHostDataSource, public isc::data::CfgToElement { public: + /// @brief Constructor. + CfgHosts(); + /// @brief Destructor. virtual ~CfgHosts() { } @@ -536,13 +542,13 @@ public: /// has already been added to the IPv4 or IPv6 subnet. virtual void add(const HostPtr& host); - /// @brief Attempts to delete a hosts by address. + /// @brief Attempts to delete hosts by address. /// /// This method supports both v4 and v6. - /// @todo: Not implemented. /// /// @param subnet_id subnet identifier. /// @param addr specified address. + /// @return true if deletion was successful, false otherwise. virtual bool del(const SubnetID& subnet_id, const asiolink::IOAddress& addr); /// @brief Attempts to delete all hosts for a given IPv4 subnet. @@ -555,7 +561,6 @@ public: /// @brief Attempts to delete a host by (subnet4-id, identifier, identifier-type) /// /// This method supports v4 only. - /// @todo: Not implemented. /// /// @param subnet_id IPv4 Subnet identifier. /// @param identifier_type Identifier type. @@ -577,7 +582,6 @@ public: /// @brief Attempts to delete a host by (subnet6-id, identifier, identifier-type) /// /// This method supports v6 only. - /// @todo: Not implemented. /// /// @param subnet_id IPv6 Subnet identifier. /// @param identifier_type Identifier type. @@ -635,6 +639,9 @@ public: private: + /// @note: all private/internal methods suppose the caller takes + /// the mutex at the exception of toElement[46]. + /// @brief Returns @c Host objects for the specific identifier and type. /// /// This private method is called by the @c CfgHosts::getAll @@ -812,6 +819,19 @@ private: void getAllInternal6(const asiolink::IOAddress& address, Storage& storage) const; + /// @brief Returns @c Host objects for the specified (Subnet-id,IPv6 address) tuple. + /// + /// This private method is called by the @c CfgHosts::getAll4 methods + /// to retrieve the @c Host for which the specified IPv4 address is + /// reserved and is in specified subnet-id. The retrieved objects are + /// appended to the returned container. + /// + /// @param subnet_id Subnet identifier. + /// @param address IPv4 address. + /// @return Collection of const @c Host objects. + ConstHostCollection + getAllInternal4(const SubnetID& subnet_id, + const asiolink::IOAddress& address) const; /// @brief Returns @c Host objects for the specified (Subnet-id,IPv6 address) tuple. /// @@ -924,6 +944,9 @@ private: /// may be non-unique. bool ip_reservations_unique_ = true; + /// @brief The mutex used to protect host containers. + const boost::scoped_ptr mutex_; + /// @brief Unparse a configuration object (DHCPv4 reservations) /// /// @return a pointer to unparsed configuration diff --git a/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc b/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc index 33133284f9..0811c01363 100644 --- a/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc +++ b/src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -21,8 +22,10 @@ #include using namespace isc; -using namespace isc::dhcp; using namespace isc::asiolink; +using namespace isc::dhcp; +using namespace isc::test; +using namespace isc::util; namespace { @@ -55,6 +58,40 @@ public: /// @param address Address to be increased. IOAddress increase(const IOAddress& address, const uint8_t num) const; + /// @brief test methods. + void testGetAllNonRepeatingHosts(); + void testGetAllRepeatingHosts(); + void testGetAll4BySubnet(); + void testGetAll6BySubnet(); + void testGetAll6ByAddress(); + void testGetPage4(); + void testGetPage6(); + void testGetPage4All(); + void testGetPage6All(); + void testGetAll4ByAddress(); + void testDeleteForIPv4(); + void testDeleteForIPv6(); + void testDel4(); + void testDel6(); + void testDeleteAll4(); + void testGet4(); + void testUnparsed4(); + void testGet6(); + void testDeleteAll6(); + void testUnparse6(); + void testGet6ByAddr(); + void testGet6MultipleAddrs(); + void testAdd4AlreadyReserved(); + void testAllow4AlreadyReserved(); + void testAdd6Invalid2Hosts(); + void testAllowAddress6AlreadyReserved(); + void testAllowPrefix6AlreadyReserved(); + void testDuplicatesSubnet4HWAddr(); + void testDuplicatesSubnet4DUID(); + void testDuplicatesSubnet6HWAddr(); + void testDuplicatesSubnet6DUID(); + void testUpdate(); + /// @brief Collection of HW address objects allocated for unit tests. std::vector hwaddrs_; /// @brief Collection of DUIDs allocated for unit tests. @@ -95,10 +132,12 @@ CfgHostsTest::CfgHostsTest() { IOAddress addrb(addrb_template + i); addressesb_.push_back(addrb); } + MultiThreadingMgr::instance().setMode(false); } CfgHostsTest::~CfgHostsTest() { CfgMgr::instance().setFamily(AF_INET); + MultiThreadingMgr::instance().setMode(false); } IOAddress @@ -113,7 +152,8 @@ CfgHostsTest::increase(const IOAddress& address, const uint8_t num) const { // This test checks that hosts with unique HW addresses and DUIDs can be // retrieved from the host configuration. -TEST_F(CfgHostsTest, getAllNonRepeatingHosts) { +void +CfgHostsTest::testGetAllNonRepeatingHosts() { CfgHosts cfg; // Add 25 hosts identified by HW address and 25 hosts identified by // DUID. They are added to different subnets. @@ -161,9 +201,19 @@ TEST_F(CfgHostsTest, getAllNonRepeatingHosts) { } } +TEST_F(CfgHostsTest, getAllNonRepeatingHosts) { + testGetAllNonRepeatingHosts(); +} + +TEST_F(CfgHostsTest, getAllNonRepeatingHostsMultiThreading) { + MultiThreadingTest mt(true); + testGetAllNonRepeatingHosts(); +} + // This test verifies that the host can be added to multiple subnets and // that the getAll message retrieves all instances of the host. -TEST_F(CfgHostsTest, getAllRepeatingHosts) { +void +CfgHostsTest::testGetAllRepeatingHosts() { CfgHosts cfg; // Add hosts. for (unsigned i = 0; i < 25; ++i) { @@ -219,9 +269,19 @@ TEST_F(CfgHostsTest, getAllRepeatingHosts) { } } +TEST_F(CfgHostsTest, getAllRepeatingHosts) { + testGetAllRepeatingHosts(); +} + +TEST_F(CfgHostsTest, getAllRepeatingHostsMultiThreading) { + MultiThreadingTest mt(true); + testGetAllRepeatingHosts(); +} + // This test checks that hosts in the same subnet can be retrieved from // the host configuration. -TEST_F(CfgHostsTest, getAll4BySubnet) { +void +CfgHostsTest::testGetAll4BySubnet() { CfgHosts cfg; // Add 25 hosts identified by HW address in the same subnet. for (unsigned i = 0; i < 25; ++i) { @@ -245,9 +305,19 @@ TEST_F(CfgHostsTest, getAll4BySubnet) { } } +TEST_F(CfgHostsTest, getAll4BySubnet) { + testGetAll4BySubnet(); +} + +TEST_F(CfgHostsTest, getAll4BySubnetMultiThreading) { + MultiThreadingTest mt(true); + testGetAll4BySubnet(); +} + // This test checks that hosts in the same subnet can be retrieved from // the host configuration. -TEST_F(CfgHostsTest, getAll6BySubnet) { +void +CfgHostsTest::testGetAll6BySubnet() { CfgHosts cfg; // Add 25 hosts identified by DUID in the same subnet. for (unsigned i = 0; i < 25; ++i) { @@ -277,9 +347,19 @@ TEST_F(CfgHostsTest, getAll6BySubnet) { } } +TEST_F(CfgHostsTest, getAll6BySubnet) { + testGetAll6BySubnet(); +} + +TEST_F(CfgHostsTest, getAll6BySubnetMultiThreading) { + MultiThreadingTest mt(true); + testGetAll6BySubnet(); +} + // This test checks that hosts with the same reserved address can be retrieved // from the host configuration. -TEST_F(CfgHostsTest, getAll6ByAddress) { +void +CfgHostsTest::testGetAll6ByAddress() { CfgHosts cfg; // Add 25 hosts identified by DUID in the same subnet. for (unsigned i = 0; i < 25; ++i) { @@ -305,9 +385,19 @@ TEST_F(CfgHostsTest, getAll6ByAddress) { } } +TEST_F(CfgHostsTest, getAll6ByAddress) { + testGetAll6ByAddress(); +} + +TEST_F(CfgHostsTest, getAll6ByAddressMultiThreading) { + MultiThreadingTest mt(true); + testGetAll6ByAddress(); +} + // This test checks that hosts in the same subnet can be retrieved from // the host configuration by pages. -TEST_F(CfgHostsTest, getPage4) { +void +CfgHostsTest::testGetPage4() { CfgHosts cfg; // Add 25 hosts identified by DUID in the same subnet. for (unsigned i = 0; i < 25; ++i) { @@ -342,9 +432,19 @@ TEST_F(CfgHostsTest, getPage4) { EXPECT_EQ(0, page.size()); } +TEST_F(CfgHostsTest, getPage4) { + testGetPage4(); +} + +TEST_F(CfgHostsTest, getPage4MultiThreading) { + MultiThreadingTest mt(true); + testGetPage4(); +} + // This test checks that hosts in the same subnet can be retrieved from // the host configuration by pages. -TEST_F(CfgHostsTest, getPage6) { +void +CfgHostsTest::testGetPage6() { CfgHosts cfg; // Add 25 hosts identified by HW address in the same subnet. for (unsigned i = 0; i < 25; ++i) { @@ -384,9 +484,19 @@ TEST_F(CfgHostsTest, getPage6) { EXPECT_EQ(0, page.size()); } +TEST_F(CfgHostsTest, getPage6) { + testGetPage6(); +} + +TEST_F(CfgHostsTest, getPage6MultiThreading) { + MultiThreadingTest mt(true); + testGetPage6(); +} + // This test checks that all hosts can be retrieved from the host // configuration by pages. -TEST_F(CfgHostsTest, getPage4All) { +void +CfgHostsTest::testGetPage4All() { CfgHosts cfg; // Add 25 hosts identified by DUID. for (unsigned i = 0; i < 25; ++i) { @@ -417,9 +527,19 @@ TEST_F(CfgHostsTest, getPage4All) { EXPECT_EQ(0, page.size()); } +TEST_F(CfgHostsTest, getPage4All) { + testGetPage4All(); +} + +TEST_F(CfgHostsTest, getPage4AllMultiThreading) { + MultiThreadingTest mt(true); + testGetPage4All(); +} + // This test checks that all hosts can be retrieved from the host // configuration by pages. -TEST_F(CfgHostsTest, getPage6All) { +void +CfgHostsTest::testGetPage6All() { CfgHosts cfg; // Add 25 hosts identified by HW address. for (unsigned i = 0; i < 25; ++i) { @@ -455,9 +575,19 @@ TEST_F(CfgHostsTest, getPage6All) { EXPECT_EQ(0, page.size()); } +TEST_F(CfgHostsTest, getPage6All) { + testGetPage6All(); +} + +TEST_F(CfgHostsTest, getPage6AllMultiThreading) { + MultiThreadingTest mt(true); + testGetPage6All(); +} + // This test checks that all reservations for the specified IPv4 address can // be retrieved. -TEST_F(CfgHostsTest, getAll4ByAddress) { +void +CfgHostsTest::testGetAll4ByAddress() { CfgHosts cfg; // Add hosts. for (unsigned i = 0; i < 25; ++i) { @@ -483,9 +613,19 @@ TEST_F(CfgHostsTest, getAll4ByAddress) { EXPECT_EQ(25, *subnet_ids.rbegin()); } +TEST_F(CfgHostsTest, getAll4ByAddress) { + testGetAll4ByAddress(); +} + +TEST_F(CfgHostsTest, getAll4ByAddressMultiThreading) { + MultiThreadingTest mt(true); + testGetAll4ByAddress(); +} + // This test checks that the IPv4 reservation for the specified IPv4 address can // be deleted. -TEST_F(CfgHostsTest, deleteForIPv4) { +void +CfgHostsTest::testDeleteForIPv4() { CfgHosts cfg; // Add hosts. IOAddress address("10.0.0.42"); @@ -517,9 +657,19 @@ TEST_F(CfgHostsTest, deleteForIPv4) { EXPECT_EQ(0, hosts_by_address.size()); } +TEST_F(CfgHostsTest, deleteForIPv4) { + testDeleteForIPv4(); +} + +TEST_F(CfgHostsTest, deleteForIPv4MultiThreading) { + MultiThreadingTest mt(true); + testDeleteForIPv4(); +} + // This test checks that the IPv6 reservation for the specified subnet ID and // IPv6 address can be deleted. -TEST_F(CfgHostsTest, deleteForIPv6) { +void +CfgHostsTest::testDeleteForIPv6() { CfgHosts cfg; // Add hosts. IOAddress address("2001:db8:1::1"); @@ -554,6 +704,15 @@ TEST_F(CfgHostsTest, deleteForIPv6) { EXPECT_EQ(host_count-1, hosts_by_subnet.size()); } +TEST_F(CfgHostsTest, deleteForIPv6) { + testDeleteForIPv6(); +} + +TEST_F(CfgHostsTest, deleteForIPv6MultiThreading) { + MultiThreadingTest mt(true); + testDeleteForIPv6(); +} + // This test checks that false is returned for deleting the IPv4 reservation // that doesn't exist. TEST_F(CfgHostsTest, deleteForMissingIPv4) { @@ -561,6 +720,9 @@ TEST_F(CfgHostsTest, deleteForMissingIPv4) { // Delete non-existent host. EXPECT_FALSE(cfg.del(SubnetID(42), IOAddress(("10.0.0.42")))); + + MultiThreadingTest mt(true); + EXPECT_FALSE(cfg.del(SubnetID(42), IOAddress(("10.0.0.42")))); } // This test checks that false is returned for deleting the IPv6 reservation @@ -570,11 +732,15 @@ TEST_F(CfgHostsTest, deleteForMissingIPv6) { // Delete non-existent host. EXPECT_FALSE(cfg.del(SubnetID(42), IOAddress(("2001:db8:1::1")))); + + MultiThreadingTest mt(true); + EXPECT_FALSE(cfg.del(SubnetID(42), IOAddress(("2001:db8:1::1")))); } // This test checks that the reservation for the specified IPv4 subnet and // identifier can be deleted. -TEST_F(CfgHostsTest, del4) { +void +CfgHostsTest::testDel4() { CfgHosts cfg; // Add hosts. @@ -627,9 +793,19 @@ TEST_F(CfgHostsTest, del4) { EXPECT_FALSE(host); } +TEST_F(CfgHostsTest, del4) { + testDel4(); +} + +TEST_F(CfgHostsTest, del4MultiThreading) { + MultiThreadingTest mt(true); + testDel4(); +} + // This test checks that the host and its reservations for the specified IPv6 // subnet and identifier can be deleted. -TEST_F(CfgHostsTest, del6) { +void +CfgHostsTest::testDel6() { CfgHosts cfg; // Add hosts. @@ -686,12 +862,24 @@ TEST_F(CfgHostsTest, del6) { EXPECT_FALSE(host); } +TEST_F(CfgHostsTest, del6) { + testDel6(); +} + +TEST_F(CfgHostsTest, del6MultiThreading) { + MultiThreadingTest mt(true); + testDel6(); +} + // This test checks that false is returned for deleting the IPv4 host that // doesn't exist. TEST_F(CfgHostsTest, del4MissingHost) { CfgHosts cfg; EXPECT_FALSE(cfg.del4(SubnetID(42), Host::IdentifierType::IDENT_DUID, &duids_[0]->getDuid()[0], duids_[0]->getDuid().size())); + MultiThreadingTest mt(true); + EXPECT_FALSE(cfg.del4(SubnetID(42), Host::IdentifierType::IDENT_DUID, + &duids_[0]->getDuid()[0], duids_[0]->getDuid().size())); } // This test checks that false is returned for deleting the IPv6 host that @@ -700,11 +888,15 @@ TEST_F(CfgHostsTest, del6MissingHost) { CfgHosts cfg; EXPECT_FALSE(cfg.del6(SubnetID(42), Host::IdentifierType::IDENT_DUID, &duids_[0]->getDuid()[0], duids_[0]->getDuid().size())); + MultiThreadingTest mt(true); + EXPECT_FALSE(cfg.del6(SubnetID(42), Host::IdentifierType::IDENT_DUID, + &duids_[0]->getDuid()[0], duids_[0]->getDuid().size())); } // This test checks that all reservations for the specified IPv4 subnet can // be deleted. -TEST_F(CfgHostsTest, deleteAll4) { +void +CfgHostsTest::testDeleteAll4() { CfgHosts cfg; // Add hosts. for (unsigned i = 0; i < 25; ++i) { @@ -748,9 +940,19 @@ TEST_F(CfgHostsTest, deleteAll4) { EXPECT_EQ(1, *subnet_ids.begin()); } +TEST_F(CfgHostsTest, deleteAll4) { + testDeleteAll4(); +} + +TEST_F(CfgHostsTest, deleteAll4MultiThreading) { + MultiThreadingTest mt(true); + testDeleteAll4(); +} + // This test checks that the reservations can be retrieved for the particular // host connected to the specific IPv4 subnet (by subnet id). -TEST_F(CfgHostsTest, get4) { +void +CfgHostsTest::testGet4() { CfgHosts cfg; // Add hosts. for (unsigned i = 0; i < 25; ++i) { @@ -787,8 +989,18 @@ TEST_F(CfgHostsTest, get4) { } } +TEST_F(CfgHostsTest, get4) { + testGet4(); +} + +TEST_F(CfgHostsTest, get4MultiThreading) { + MultiThreadingTest mt(true); + testGet4(); +} + // This test checks that the DHCPv4 reservations can be unparsed -TEST_F(CfgHostsTest, unparsed4) { +void +CfgHostsTest::testUnparsed4() { CfgMgr::instance().setFamily(AF_INET); CfgHosts cfg; CfgHostsList list; @@ -871,9 +1083,19 @@ TEST_F(CfgHostsTest, unparsed4) { } } +TEST_F(CfgHostsTest, unparsed4) { + testUnparsed4(); +} + +TEST_F(CfgHostsTest, unparsed4MultiThreading) { + MultiThreadingTest mt(true); + testUnparsed4(); +} + // This test checks that the reservations can be retrieved for the particular // host connected to the specific IPv6 subnet (by subnet id). -TEST_F(CfgHostsTest, get6) { +void +CfgHostsTest::testGet6() { CfgHosts cfg; // Add hosts. for (unsigned i = 0; i < 25; ++i) { @@ -922,9 +1144,19 @@ TEST_F(CfgHostsTest, get6) { } } +TEST_F(CfgHostsTest, get6) { + testGet6(); +} + +TEST_F(CfgHostsTest, get6MultiThreading) { + MultiThreadingTest mt(true); + testGet6(); +} + // This test checks that all reservations for the specified IPv6 subnet can // be deleted. -TEST_F(CfgHostsTest, deleteAll6) { +void +CfgHostsTest::testDeleteAll6() { CfgHosts cfg; // Add hosts. for (unsigned i = 0; i < 25; ++i) { @@ -969,8 +1201,18 @@ TEST_F(CfgHostsTest, deleteAll6) { } } +TEST_F(CfgHostsTest, deleteAll6) { + testDeleteAll6(); +} + +TEST_F(CfgHostsTest, deleteAll6MultiThreading) { + MultiThreadingTest mt(true); + testDeleteAll6(); +} + // This test checks that the DHCPv6 reservations can be unparsed -TEST_F(CfgHostsTest, unparse6) { +void +CfgHostsTest::testUnparse6() { CfgMgr::instance().setFamily(AF_INET6); CfgHosts cfg; CfgHostsList list; @@ -1071,9 +1313,19 @@ TEST_F(CfgHostsTest, unparse6) { } } +TEST_F(CfgHostsTest, unparse6) { + testUnparse6(); +} + +TEST_F(CfgHostsTest, unparse6MultiThreading) { + MultiThreadingTest mt(true); + testUnparse6(); +} + // This test checks that the IPv6 reservations can be retrieved for a particular // (subnet-id, address) tuple. -TEST_F(CfgHostsTest, get6ByAddr) { +void +CfgHostsTest::testGet6ByAddr() { CfgHosts cfg; // Add hosts. for (unsigned i = 0; i < 25; ++i) { @@ -1103,9 +1355,19 @@ TEST_F(CfgHostsTest, get6ByAddr) { } } +TEST_F(CfgHostsTest, get6ByAddr) { + testGet6ByAddr(); +} + +TEST_F(CfgHostsTest, get6ByAddrMultiThreading) { + MultiThreadingTest mt(true); + testGet6ByAddr(); +} + // This test checks that the IPv6 reservations can be retrieved for a particular // (subnet-id, address) tuple. -TEST_F(CfgHostsTest, get6MultipleAddrs) { +void +CfgHostsTest::testGet6MultipleAddrs() { CfgHosts cfg; // Add 25 hosts. Each host has reservations for 5 addresses. @@ -1154,10 +1416,19 @@ TEST_F(CfgHostsTest, get6MultipleAddrs) { } } +TEST_F(CfgHostsTest, get6MultipleAddrs) { + testGet6MultipleAddrs(); +} + +TEST_F(CfgHostsTest, get6MultipleAddrsMultiThreading) { + MultiThreadingTest mt(true); + testGet6MultipleAddrs(); +} // Checks that it's not possible for a second host to reserve an address // which is already reserved. -TEST_F(CfgHostsTest, add4AlreadyReserved) { +void +CfgHostsTest::testAdd4AlreadyReserved() { CfgHosts cfg; // First host has a reservation for address 192.0.2.1 @@ -1179,9 +1450,19 @@ TEST_F(CfgHostsTest, add4AlreadyReserved) { EXPECT_THROW(cfg.add(host2), isc::dhcp::ReservedAddress); } +TEST_F(CfgHostsTest, add4AlreadyReserved) { + testAdd4AlreadyReserved(); +} + +TEST_F(CfgHostsTest, add4AlreadyReservedMultiThreading) { + MultiThreadingTest mt(true); + testAdd4AlreadyReserved(); +} + // Test that it is possible to allow inserting multiple reservations for // the same IP address. -TEST_F(CfgHostsTest, allow4AlreadyReserved) { +void +CfgHostsTest::testAllow4AlreadyReserved() { CfgHosts cfg; // Allow creating multiple reservations for the same IP address. ASSERT_TRUE(cfg.setIPReservationsUnique(false)); @@ -1212,9 +1493,19 @@ TEST_F(CfgHostsTest, allow4AlreadyReserved) { returned[1]->getIPv4Reservation().toText()); } +TEST_F(CfgHostsTest, allow4AlreadyReserved) { + testAllow4AlreadyReserved(); +} + +TEST_F(CfgHostsTest, allow4AlreadyReservedMultiThreading) { + MultiThreadingTest mt(true); + testAllow4AlreadyReserved(); +} + // Checks that it's not possible for two hosts to have the same address // reserved at the same time. -TEST_F(CfgHostsTest, add6Invalid2Hosts) { +void +CfgHostsTest::testAdd6Invalid2Hosts() { CfgHosts cfg; // First host has a reservation for address 2001:db8::1 @@ -1238,9 +1529,19 @@ TEST_F(CfgHostsTest, add6Invalid2Hosts) { EXPECT_THROW(cfg.add(host2), isc::dhcp::DuplicateHost); } +TEST_F(CfgHostsTest, add6Invalid2Hosts) { + testAdd6Invalid2Hosts(); +} + +TEST_F(CfgHostsTest, add6Invalid2HostsMultiThreading) { + MultiThreadingTest mt(true); + testAdd6Invalid2Hosts(); +} + // Test that it is possible to allow inserting multiple reservations for // the same IPv6 address. -TEST_F(CfgHostsTest, allowAddress6AlreadyReserved) { +void +CfgHostsTest::testAllowAddress6AlreadyReserved() { CfgHosts cfg; // Allow creating multiple reservations for the same IP address. ASSERT_TRUE(cfg.setIPReservationsUnique(false)); @@ -1279,9 +1580,19 @@ TEST_F(CfgHostsTest, allowAddress6AlreadyReserved) { range1.first->second.getPrefix().toText()); } +TEST_F(CfgHostsTest, allowAddress6AlreadyReserved) { + testAllowAddress6AlreadyReserved(); +} + +TEST_F(CfgHostsTest, allowAddress6AlreadyReservedMultiThreading) { + MultiThreadingTest mt(true); + testAllowAddress6AlreadyReserved(); +} + // Test that it is possible to allow inserting multiple reservations for // the same IPv6 delegated prefix. -TEST_F(CfgHostsTest, allowPrefix6AlreadyReserved) { +void +CfgHostsTest::testAllowPrefix6AlreadyReserved() { CfgHosts cfg; // Allow creating multiple reservations for the same delegated prefix. ASSERT_TRUE(cfg.setIPReservationsUnique(false)); @@ -1320,6 +1631,15 @@ TEST_F(CfgHostsTest, allowPrefix6AlreadyReserved) { range1.first->second.getPrefix().toText()); } +TEST_F(CfgHostsTest, allowPrefix6AlreadyReserved) { + testAllowPrefix6AlreadyReserved(); +} + +TEST_F(CfgHostsTest, allowPrefix6AlreadyReservedMultiThreading) { + MultiThreadingTest mt(true); + testAllowPrefix6AlreadyReserved(); +} + // Check that no error is reported when adding a host with subnet // ids equal to global. TEST_F(CfgHostsTest, globalSubnetIDs) { @@ -1344,7 +1664,8 @@ TEST_F(CfgHostsTest, unusedSubnetIDs) { // This test verifies that it is not possible to add the same Host to the // same IPv4 subnet twice. -TEST_F(CfgHostsTest, duplicatesSubnet4HWAddr) { +void +CfgHostsTest::testDuplicatesSubnet4HWAddr() { CfgHosts cfg; // Add a host. ASSERT_NO_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false), @@ -1367,9 +1688,19 @@ TEST_F(CfgHostsTest, duplicatesSubnet4HWAddr) { IOAddress("10.0.0.10"))))); } +TEST_F(CfgHostsTest, duplicatesSubnet4HWAddr) { + testDuplicatesSubnet4HWAddr(); +} + +TEST_F(CfgHostsTest, duplicatesSubnet4HWAddrMultiThreading) { + MultiThreadingTest mt(true); + testDuplicatesSubnet4HWAddr(); +} + // This test verifies that it is not possible to add the same Host to the // same IPv4 subnet twice. -TEST_F(CfgHostsTest, duplicatesSubnet4DUID) { +void +CfgHostsTest::testDuplicatesSubnet4DUID() { CfgHosts cfg; // Add a host. ASSERT_NO_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(), @@ -1392,9 +1723,19 @@ TEST_F(CfgHostsTest, duplicatesSubnet4DUID) { IOAddress("10.0.0.10"))))); } +TEST_F(CfgHostsTest, duplicatesSubnet4DUID) { + testDuplicatesSubnet4DUID(); +} + +TEST_F(CfgHostsTest, duplicatesSubnet4DUIDMultiThreading) { + MultiThreadingTest mt(true); + testDuplicatesSubnet4DUID(); +} + // This test verifies that it is not possible to add the same Host to the // same IPv6 subnet twice. -TEST_F(CfgHostsTest, duplicatesSubnet6HWAddr) { +void +CfgHostsTest::testDuplicatesSubnet6HWAddr() { CfgHosts cfg; // Add a host. ASSERT_NO_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false), @@ -1420,9 +1761,19 @@ TEST_F(CfgHostsTest, duplicatesSubnet6HWAddr) { "foo.example.com")))); } +TEST_F(CfgHostsTest, duplicatesSubnet6HWAddr) { + testDuplicatesSubnet6HWAddr(); +} + +TEST_F(CfgHostsTest, duplicatesSubnet6HWAddrMultiThreading) { + MultiThreadingTest mt(true); + testDuplicatesSubnet6HWAddr(); +} + // This test verifies that it is not possible to add the same Host to the // same IPv6 subnet twice. -TEST_F(CfgHostsTest, duplicatesSubnet6DUID) { +void +CfgHostsTest::testDuplicatesSubnet6DUID() { CfgHosts cfg; // Add a host. ASSERT_NO_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(), @@ -1448,8 +1799,18 @@ TEST_F(CfgHostsTest, duplicatesSubnet6DUID) { "foo.example.com")))); } -// Checks that updates work correctly. -TEST_F(CfgHostsTest, update) { +TEST_F(CfgHostsTest, duplicatesSubnet6DUID) { + testDuplicatesSubnet6DUID(); +} + +TEST_F(CfgHostsTest, duplicatesSubnet6DUIDMultiThreading) { + MultiThreadingTest mt(true); + testDuplicatesSubnet6DUID(); +} + +// Checks that updates work correctly. Note it is not really MT safe. +void +CfgHostsTest::testUpdate() { CfgHosts cfg; HostPtr const host(boost::make_shared(duids_[0]->toText(), "duid", SUBNET_ID_UNUSED, @@ -1506,4 +1867,13 @@ TEST_F(CfgHostsTest, update) { "key=(empty) ipv6_reservations=(none)", hosts[0]->toText()); } +TEST_F(CfgHostsTest, update) { + testUpdate(); +} + +TEST_F(CfgHostsTest, updateMultiThreading) { + MultiThreadingTest mt(true); + testUpdate(); +} + } // namespace