]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#1428] Calls to fetch many hosts by id/address
authorMarcin Siodelski <marcin@isc.org>
Wed, 30 Sep 2020 13:16:30 +0000 (15:16 +0200)
committerMarcin Siodelski <marcin@isc.org>
Mon, 5 Oct 2020 13:14:57 +0000 (13:14 +0000)
The new calls have been added to the host backends to retrieve multiple
hosts by subnet_id and address.

23 files changed:
src/lib/dhcpsrv/base_host_data_source.h
src/lib/dhcpsrv/cfg_hosts.cc
src/lib/dhcpsrv/cfg_hosts.h
src/lib/dhcpsrv/cql_host_data_source.cc
src/lib/dhcpsrv/cql_host_data_source.h
src/lib/dhcpsrv/host_container.h
src/lib/dhcpsrv/host_mgr.cc
src/lib/dhcpsrv/host_mgr.h
src/lib/dhcpsrv/hosts_messages.cc
src/lib/dhcpsrv/hosts_messages.h
src/lib/dhcpsrv/hosts_messages.mes
src/lib/dhcpsrv/mysql_host_data_source.cc
src/lib/dhcpsrv/mysql_host_data_source.h
src/lib/dhcpsrv/pgsql_host_data_source.cc
src/lib/dhcpsrv/pgsql_host_data_source.h
src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc
src/lib/dhcpsrv/tests/host_cache_unittest.cc
src/lib/dhcpsrv/tests/host_mgr_unittest.cc
src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc
src/lib/dhcpsrv/testutils/host_data_source_utils.cc
src/lib/dhcpsrv/testutils/host_data_source_utils.h
src/lib/dhcpsrv/testutils/memory_host_data_source.cc
src/lib/dhcpsrv/testutils/memory_host_data_source.h

index 576b89b02a9e107758c3917db431f034f2adfdfd..7b37fde2c7f930cc5ca3c9e0d3b38022d8bef631 100644 (file)
@@ -310,6 +310,33 @@ public:
     get4(const SubnetID& subnet_id,
          const asiolink::IOAddress& address) const = 0;
 
+    /// @brief Returns all hosts connected to the IPv4 subnet and having
+    /// a reservation for a specified address.
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv4 address within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv4 reservation. In that case, the number of hosts returned
+    /// by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv4 address in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv4 address is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv4 address is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const = 0;
+
     /// @brief Returns a host connected to the IPv6 subnet.
     ///
     /// @param subnet_id Subnet identifier.
@@ -346,6 +373,33 @@ public:
     get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const =
  0;
 
+    /// @brief Returns all hosts connected to the IPv6 subnet and having
+    /// a reservation for a specified address or delegated prefix (lease).
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv6 lease within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv6 address or prefix. In that case, the number of hosts
+    /// returned by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv6 lease in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv6 lease is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv6 lease is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll6(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const = 0;
+
     /// @brief Adds a new host to the collection.
     ///
     /// The implementations of this method should guard against duplicate
index fe25c071f85737df813f5ea39d58bccb4eb0d8f0..bba9b39362a9aa5338b995990c069a089e76e5d9 100644 (file)
@@ -701,6 +701,30 @@ CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const {
     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 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);
+}
 
 ConstHostPtr
 CfgHosts::get6(const SubnetID& subnet_id,
@@ -744,6 +768,14 @@ CfgHosts::get6(const SubnetID& subnet_id,
     return (getHostInternal6<HostPtr, HostCollection>(subnet_id, address));
 }
 
+ConstHostCollection
+CfgHosts::getAll6(const SubnetID& subnet_id,
+                  const asiolink::IOAddress& address) const {
+    ConstHostCollection hosts;
+    getAllInternal6(subnet_id, address, hosts);
+    return (hosts);
+}
+
 template<typename ReturnType, typename Storage>
 ReturnType
 CfgHosts::getHostInternal6(const SubnetID& subnet_id,
index 050df7c11f03567ce4f9cd13d5b905a09726fe45..d3f136d339e4236ecc0d94cde588cc5b3bfba22f 100644 (file)
@@ -407,6 +407,33 @@ public:
     virtual ConstHostPtr
     get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv4 subnet and having
+    /// a reservation for a specified address.
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv4 address within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv4 reservation. In that case, the number of hosts returned
+    /// by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv4 address in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv4 address is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv4 address is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Returns a host connected to the IPv6 subnet.
     ///
     /// @param subnet_id Subnet identifier.
@@ -474,6 +501,33 @@ public:
     virtual HostPtr
     get6(const SubnetID& subnet_id, const asiolink::IOAddress& address);
 
+    /// @brief Returns all hosts connected to the IPv6 subnet and having
+    /// a reservation for a specified address or delegated prefix (lease).
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv6 lease within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv6 address or prefix. In that case, the number of hosts
+    /// returned by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv6 lease in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv6 lease is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv6 lease is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll6(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Adds a new host to the collection.
     ///
     /// @param host Pointer to the new @c Host object being added.
index dc83a23bcc353b13fcd127756ded2e6c70f0f048..6b10e2fb2738a00e4b6195a89f6b54d51efeae24 100644 (file)
@@ -3597,6 +3597,17 @@ CqlHostDataSource::get4(const SubnetID& subnet_id,
     return (impl_->get4(subnet_id, address));
 }
 
+ConstHostCollection
+CqlHostDataSource::getAll4(const SubnetID& subnet_id,
+                           const asiolink::IOAddress& address) const {
+    ConstHostCollection hosts;
+    auto host = get4(subnet_id, address);
+    if (host) {
+        hosts.push_back(host);
+    }
+    return (hosts);
+}
+
 ConstHostPtr
 CqlHostDataSource::get6(const SubnetID& subnet_id,
                         const Host::IdentifierType& identifier_type,
@@ -3623,6 +3634,17 @@ CqlHostDataSource::get6(const SubnetID& subnet_id,
     return (impl_->get6(subnet_id, address));
 }
 
+ConstHostCollection
+CqlHostDataSource::getAll6(const SubnetID& subnet_id,
+                           const asiolink::IOAddress& address) const {
+    ConstHostCollection hosts;
+    auto host = get6(subnet_id, address);
+    if (host) {
+        hosts.push_back(host);
+    }
+    return (hosts);
+}
+
 ConstHostCollection
 CqlHostDataSource::getAllHosts() const {
     return (impl_->getAllHosts());
index 15fb02de3a880fc3ac9d3c22b0edcb65bdda5fcb..0b583008980644fb5e058a61fc63bec700f1ac85 100644 (file)
@@ -328,6 +328,21 @@ public:
     get4(const SubnetID& subnet_id,
          const asiolink::IOAddress& address) const override;
 
+    /// @brief Returns all hosts connected to the IPv4 subnet and having
+    /// a reservation for a specified address.
+    ///
+    /// This backend does not support a configuration in which multiple
+    /// reservations can be created for a single IPv4 address, so it
+    /// always returns 1 or 0 hosts.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const override;
+
     /// @brief Returns a @ref Host connected to an IPv6 subnet.
     ///
     /// @param subnet_id subnet identifier to filter by
@@ -366,6 +381,21 @@ public:
     get6(const SubnetID& subnet_id,
          const asiolink::IOAddress& address) const override;
 
+    /// @brief Returns all hosts connected to the IPv6 subnet and having
+    /// a reservation for a specified address or delegated prefix (lease).
+    ///
+    /// This backend does not support a configuration in which multiple
+    /// reservations can be created for a single IPv6 address, so it
+    /// always returns 1 or 0 hosts.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll6(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Returns a collection of all the hosts.
     ///
     /// This method may return multiple @ref Host objects. It is only used in
index 113f4b8b139fc760e62ad2cc891aaf3ffb709877..95c06ad134fbc50f6d67c42bc63a70a5e76fd448 100644 (file)
@@ -209,7 +209,7 @@ typedef boost::multi_index_container<
 
         // Second index is used to search by (subnet_id, address) pair.
         // This is
-        boost::multi_index::ordered_unique<
+        boost::multi_index::ordered_non_unique<
 
             /// This is a composite key. It uses two keys: subnet-id and
             /// IPv6 address reservation.
index ba043d0316a1b49b3a8355aca44b29627af6f8fa..a2e1d875c0e96f12c7493a551961dae74e9c44b3 100644 (file)
@@ -395,6 +395,40 @@ HostMgr::get4(const SubnetID& subnet_id,
     return (ConstHostPtr());
 }
 
+ConstHostCollection
+HostMgr::getAll4(const SubnetID& subnet_id,
+                 const asiolink::IOAddress& address) const {
+    auto hosts = getCfgHosts()->getAll4(subnet_id, address);
+
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+              HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS4)
+        .arg(subnet_id)
+        .arg(address.toText());
+
+    if (cache_ptr_) {
+        auto cached = cache_ptr_->getAll4(subnet_id, address);
+        if (!cached.empty()) {
+            for (auto host : cached) {
+                if (!host->getNegative()) {
+                    hosts.push_back(host);
+                }
+            }
+            return (hosts);
+        }
+    }
+
+    for (auto source : alternate_sources_) {
+        if (source == cache_ptr_) {
+            continue;
+        }
+        auto alternate_hosts = source->getAll4(subnet_id, address);
+        for (auto host : alternate_hosts) {
+            cache(host);
+            hosts.push_back(host);
+        }
+    }
+    return (hosts);
+}
 
 ConstHostPtr
 HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
@@ -509,6 +543,41 @@ HostMgr::get6(const SubnetID& subnet_id,
     return (ConstHostPtr());
 }
 
+ConstHostCollection
+HostMgr::getAll6(const SubnetID& subnet_id,
+                 const asiolink::IOAddress& address) const {
+    auto hosts = getCfgHosts()->getAll6(subnet_id, address);
+
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+              HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS6)
+        .arg(subnet_id)
+        .arg(address.toText());
+
+    if (cache_ptr_) {
+        auto cached = cache_ptr_->getAll6(subnet_id, address);
+        if (!cached.empty()) {
+            for (auto host : cached) {
+                if (!host->getNegative()) {
+                    hosts.push_back(host);
+                }
+            }
+            return (hosts);
+        }
+    }
+
+    for (auto source : alternate_sources_) {
+        if (source == cache_ptr_) {
+            continue;
+        }
+        auto alternate_hosts = source->getAll6(subnet_id, address);
+        for (auto host : alternate_hosts) {
+            cache(host);
+            hosts.push_back(host);
+        }
+    }
+    return (hosts);
+}
+
 void
 HostMgr::add(const HostPtr& host) {
     if (alternate_sources_.empty()) {
index 6db35817595e0ec04c5d3dc1b4234fc8c99eecf1..3c61cb228d4afebff856cc75891176e2a11a77d6 100644 (file)
@@ -377,6 +377,33 @@ public:
     virtual ConstHostPtr
     get4(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv4 subnet and having
+    /// a reservation for a specified address.
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv4 address within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv4 reservation. In that case, the number of hosts returned
+    /// by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv4 address in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv4 address is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv4 address is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Returns any host connected to the IPv6 subnet.
     ///
     /// This method returns a host connected to the IPv6 subnet as described
@@ -436,6 +463,33 @@ public:
     virtual ConstHostPtr
     get6(const SubnetID& subnet_id, const asiolink::IOAddress& addr) const;
 
+    /// @brief Returns all hosts connected to the IPv6 subnet and having
+    /// a reservation for a specified address or delegated prefix (lease).
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv6 lease within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv6 address or prefix. In that case, the number of hosts
+    /// returned by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv6 lease in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv6 lease is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv6 lease is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll6(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Adds a new host to the alternate data source.
     ///
     /// This method will throw an exception if no alternate data source is
index 9be05252399038205ddd87beb38a2c9ba125a3b4..b0a103ff236dc57e308af1962a4687c6fe5f409f 100644 (file)
@@ -42,6 +42,9 @@ extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID4_HOST = "HOSTS_CFG_
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID6 = "HOSTS_CFG_GET_ALL_SUBNET_ID6";
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID6_COUNT = "HOSTS_CFG_GET_ALL_SUBNET_ID6_COUNT";
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID6_HOST = "HOSTS_CFG_GET_ALL_SUBNET_ID6_HOST";
+extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4 = "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4";
+extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT = "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT";
+extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST = "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST";
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6 = "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6";
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT = "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT";
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST = "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST";
@@ -66,6 +69,8 @@ extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_ADDRESS6 = "
 extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER = "HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER";
 extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_HOST = "HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_HOST";
 extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_NULL = "HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_NULL";
+extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS4 = "HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS4";
+extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS6 = "HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS6";
 extern const isc::log::MessageID HOSTS_MGR_NON_UNIQUE_IP_UNSUPPORTED = "HOSTS_MGR_NON_UNIQUE_IP_UNSUPPORTED";
 
 } // namespace dhcp
@@ -109,6 +114,9 @@ const char* values[] = {
     "HOSTS_CFG_GET_ALL_SUBNET_ID6", "get all hosts with reservations for IPv6 subnet %1",
     "HOSTS_CFG_GET_ALL_SUBNET_ID6_COUNT", "using IPv6 subnet %1, found %2 host(s)",
     "HOSTS_CFG_GET_ALL_SUBNET_ID6_HOST", "using IPv6 subnet %1, found host: %2",
+    "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4", "get all hosts with reservations for subnet id %1 and IPv4 address %2",
+    "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT", "using IPv4 subnet %1 and IPv4 address %2, found %3 host(s)",
+    "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST", "using IPv4 subnet %1 and IPv4 address %2, found host: %3",
     "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6", "get all hosts with reservations for subnet id %1 and IPv6 address %2",
     "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT", "using subnet id %1 and address %2, found %3 host(s)",
     "HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST", "using subnet id %1 and address %2, found host: %3",
@@ -133,6 +141,8 @@ const char* values[] = {
     "HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER", "get one host with IPv6 reservation for subnet id %1, identified by %2",
     "HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_HOST", "using subnet id %1 and identifier %2, found in %3 host: %4",
     "HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_NULL", "host not found using subnet id %1 and identifier %2",
+    "HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS4", "trying alternate sources for hosts using subnet id %1 and address %2",
+    "HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS6", "trying alternate sources for hosts using subnet id %1 and address %2",
     "HOSTS_MGR_NON_UNIQUE_IP_UNSUPPORTED", "host data source %1 does not support the mode in which IP reservations are non-unique",
     NULL
 };
index a1d2b0712db145db5e9f3d5f525c0439adbe162b..f056857350f07c95261a005c2e4c27c42b550b1b 100644 (file)
@@ -43,6 +43,9 @@ extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID4_HOST;
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID6;
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID6_COUNT;
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID6_HOST;
+extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4;
+extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT;
+extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST;
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6;
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT;
 extern const isc::log::MessageID HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST;
@@ -67,6 +70,8 @@ extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_ADDRESS6;
 extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER;
 extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_HOST;
 extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_IDENTIFIER_NULL;
+extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS4;
+extern const isc::log::MessageID HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS6;
 extern const isc::log::MessageID HOSTS_MGR_NON_UNIQUE_IP_UNSUPPORTED;
 
 } // namespace dhcp
index 8c1e8c0f8ef4f6007cbc4639d956db54c610f459..06a7844edde6a6e832b474fb76d87b936ae213a5 100644 (file)
@@ -146,6 +146,25 @@ description of the host found.
 This debug message is issued when starting to retrieve all hosts connected to
 the specific DHCPv4 subnet. The argument specifies subnet id.
 
+% HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4 get all hosts with reservations for subnet id %1 and IPv4 address %2
+This debug message is issued when starting to retrieve all hosts including
+the reservation for the given IPv4 address within the given subnet. The
+first argument specifies subnet identifier. The second argument specifies
+the IPv4 address for which the reservation is to be returned.
+
+% HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_COUNT using IPv4 subnet %1 and IPv4 address %2, found %3 host(s)
+This debug message logs the number of hosts found having the reservation
+for the specified IPv4 address within the specified subnet. The first
+argument specifies the subnet identifier. The second argument specifies
+the reserved IPv4 address. The third argument specifies the number of
+hosts found.
+
+% HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS4_HOST using IPv4 subnet %1 and IPv4 address %2, found host: %3
+This debug message is issued when found host having the reservation for
+the specified IPv4 address in the specified subnet.  The first argument
+specifies the subnet identifier. The second argument specifies the reserved
+IPv4 address. The third argument specifies host details.
+
 % HOSTS_CFG_GET_ALL_SUBNET_ID4_COUNT using IPv4 subnet %1, found %2 host(s)
 This debug message include the details of the host found using the DHCPv4
 subnet id. The arguments specify subnet id and the number of hosts found
@@ -284,6 +303,14 @@ identifier.
 This debug message is issued when no host was found using the specified
 subnet id and host identifier.
 
+% HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS4 trying alternate sources for hosts using subnet id %1 and address %2
+This debug message is issued when the Host Manager is starting to search
+for hosts in alternate host data sources by subnet ID and IPv4 address.
+
+% HOSTS_MGR_ALTERNATE_GET_ALL_SUBNET_ID_ADDRESS6 trying alternate sources for hosts using subnet id %1 and address %2
+This debug message is issued when the Host Manager is starting to search
+for hosts in alternate host data sources by subnet ID and IPv6 address.
+
 % HOSTS_MGR_NON_UNIQUE_IP_UNSUPPORTED host data source %1 does not support the mode in which IP reservations are non-unique
 This warning message is issued when an administrator attempted to configure the
 server to allow multiple host reservations for the same IP address or prefix.
index e4c97998d70a0d1af134d7261bd66ca8ca910a81..0eb73c8ea426f8ca15ec783d45772072bf3f7057 100644 (file)
@@ -3580,6 +3580,37 @@ MySqlHostDataSource::get4(const SubnetID& subnet_id,
     return (result);
 }
 
+ConstHostCollection
+MySqlHostDataSource::getAll4(const SubnetID& subnet_id,
+                             const asiolink::IOAddress& address) const {
+    if (!address.isV4()) {
+        isc_throw(BadValue, "MySqlHostDataSource::getAll4(id, address): "
+                  "wrong address type, address supplied is an IPv6 address");
+    }
+
+    // Get a context
+    MySqlHostContextAlloc get_context(*impl_);
+    MySqlHostContextPtr ctx = get_context.ctx_;
+
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[2];
+    uint32_t subnet = subnet_id;
+    memset(inbind, 0, sizeof(inbind));
+    inbind[0].buffer_type = MYSQL_TYPE_LONG;
+    inbind[0].buffer = reinterpret_cast<char*>(&subnet);
+    inbind[0].is_unsigned = MLM_TRUE;
+
+    uint32_t addr4 = address.toUint32();
+    inbind[1].buffer_type = MYSQL_TYPE_LONG;
+    inbind[1].buffer = reinterpret_cast<char*>(&addr4);
+    inbind[1].is_unsigned = MLM_TRUE;
+
+    ConstHostCollection collection;
+    impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID_ADDR, inbind,
+                             ctx->host_ipv4_exchange_, collection, false);
+    return (collection);
+}
+
 ConstHostPtr
 MySqlHostDataSource::get6(const SubnetID& subnet_id,
                           const Host::IdentifierType& identifier_type,
@@ -3680,6 +3711,42 @@ MySqlHostDataSource::get6(const SubnetID& subnet_id,
     return (result);
 }
 
+ConstHostCollection
+MySqlHostDataSource::getAll6(const SubnetID& subnet_id,
+                             const asiolink::IOAddress& address) const {
+    if (!address.isV6()) {
+        isc_throw(BadValue, "MySqlHostDataSource::getAll6(id, address): "
+                  "wrong address type, address supplied is an IPv4 address");
+    }
+
+    // Get a context
+    MySqlHostContextAlloc get_context(*impl_);
+    MySqlHostContextPtr ctx = get_context.ctx_;
+
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[2];
+    memset(inbind, 0, sizeof(inbind));
+
+    uint32_t subnet_buffer = static_cast<uint32_t>(subnet_id);
+    inbind[0].buffer_type = MYSQL_TYPE_LONG;
+    inbind[0].buffer = reinterpret_cast<char*>(&subnet_buffer);
+    inbind[0].is_unsigned = MLM_TRUE;
+
+    std::string addr6 = address.toText();
+    unsigned long addr6_length = addr6.size();
+
+    inbind[1].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[1].buffer = reinterpret_cast<char*>
+                        (const_cast<char*>(addr6.c_str()));
+    inbind[1].length = &addr6_length;
+    inbind[1].buffer_length = addr6_length;
+
+    ConstHostCollection collection;
+    impl_->getHostCollection(ctx, MySqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR, inbind,
+                             ctx->host_ipv6_exchange_, collection, false);
+    return (collection);
+}
+
 // Miscellaneous database methods.
 
 std::string
index f6266d4c6757d1607d6abcbbd9136d2b65a01b5b..6823e5a939bc5d9015b8adddc15b4af666534a22 100644 (file)
@@ -307,6 +307,33 @@ public:
     virtual ConstHostPtr get4(const SubnetID& subnet_id,
                               const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv4 subnet and having
+    /// a reservation for a specified address.
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv4 address within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv4 reservation. In that case, the number of hosts returned
+    /// by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv4 address in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv4 address is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv4 address is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Returns a host connected to the IPv6 subnet.
     ///
     /// @param subnet_id Subnet identifier.
@@ -341,6 +368,33 @@ public:
     virtual ConstHostPtr get6(const SubnetID& subnet_id,
                               const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv6 subnet and having
+    /// a reservation for a specified address or delegated prefix (lease).
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv6 lease within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv6 address or prefix. In that case, the number of hosts
+    /// returned by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv6 lease in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv6 lease is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv6 lease is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll6(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Return backend type
     ///
     /// Returns the type of the backend (e.g. "mysql", "memfile" etc.)
index 84a859b169378452bd2f1597bb0d365b845f1581..135d882cfdfad4c91d58fbb253d2f94255b99240 100644 (file)
@@ -2867,6 +2867,33 @@ PgSqlHostDataSource::get4(const SubnetID& subnet_id,
     return (result);
 }
 
+ConstHostCollection
+PgSqlHostDataSource::getAll4(const SubnetID& subnet_id,
+                             const asiolink::IOAddress& address) const {
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
+    if (!address.isV4()) {
+        isc_throw(BadValue, "PgSqlHostDataSource::get4(id, address) - "
+                  " wrong address type, address supplied is an IPv6 address");
+    }
+
+    // Set up the WHERE clause value
+    PsqlBindArrayPtr bind_array(new PsqlBindArray());
+
+    // Add the subnet id
+    bind_array->add(subnet_id);
+
+    // Add the address
+    bind_array->add(address);
+
+    ConstHostCollection collection;
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID_ADDR,
+                             bind_array, ctx->host_ipv4_exchange_, collection, false);
+    return (collection);
+}
+
 ConstHostPtr
 PgSqlHostDataSource::get6(const SubnetID& subnet_id,
                           const Host::IdentifierType& identifier_type,
@@ -2949,6 +2976,34 @@ PgSqlHostDataSource::get6(const SubnetID& subnet_id,
     return (result);
 }
 
+ConstHostCollection
+PgSqlHostDataSource::getAll6(const SubnetID& subnet_id,
+                             const asiolink::IOAddress& address) const {
+    if (!address.isV6()) {
+        isc_throw(BadValue, "PgSqlHostDataSource::get6(id, address): "
+                  "wrong address type, address supplied is an IPv4 address");
+    }
+
+    // Get a context
+    PgSqlHostContextAlloc get_context(*impl_);
+    PgSqlHostContextPtr ctx = get_context.ctx_;
+
+    // Set up the WHERE clause value
+    PsqlBindArrayPtr bind_array(new PsqlBindArray());
+
+    // Add the subnet id
+    bind_array->add(subnet_id);
+
+    // Add the prefix
+    bind_array->add(address);
+
+    ConstHostCollection collection;
+    impl_->getHostCollection(ctx, PgSqlHostDataSourceImpl::GET_HOST_SUBID6_ADDR,
+                             bind_array, ctx->host_ipv6_exchange_, collection, false);
+    return (collection);
+}
+
+
 // Miscellaneous database methods.
 
 std::string
index 152d8b0f722a2304ee659ea87f5e266a1d31d32a..d9d8414c4f7227bf6a972b08a85223e38d779745 100644 (file)
@@ -354,6 +354,33 @@ public:
     virtual ConstHostPtr get4(const SubnetID& subnet_id,
                               const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv4 subnet and having
+    /// a reservation for a specified address.
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv4 address within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv4 reservation. In that case, the number of hosts returned
+    /// by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv4 address in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv4 address is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv4 address is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Returns a host connected to the IPv6 subnet.
     ///
     /// @param subnet_id Subnet identifier.
@@ -388,6 +415,33 @@ public:
     virtual ConstHostPtr get6(const SubnetID& subnet_id,
                               const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv6 subnet and having
+    /// a reservation for a specified address or delegated prefix (lease).
+    ///
+    /// In most cases it is desired that there is at most one reservation
+    /// for a given IPv6 lease within a subnet. In a default configuration,
+    /// the backend does not allow for inserting more than one host with
+    /// the same IPv6 address or prefix. In that case, the number of hosts
+    /// returned by this function is 0 or 1.
+    ///
+    /// If the backend is configured to allow multiple hosts with reservations
+    /// for the same IPv6 lease in the given subnet, this method can return
+    /// more than one host.
+    ///
+    /// The typical use case when a single IPv6 lease is reserved for multiple
+    /// hosts is when these hosts represent different interfaces of the same
+    /// machine and each interface comes with a different MAC address. In that
+    /// case, the same IPv6 lease is assigned regarless of which interface is
+    /// used by the DHCP client to communicate with the server.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll6(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Return backend type
     ///
     /// Returns the type of database as the string "postgresql".  This is
index 0432587c2197399e900e38d7a18e3efb35296cc3..5baf7dad2656b3f41e1641af4cf89bf40e3b6031 100644 (file)
@@ -951,7 +951,11 @@ TEST_F(CfgHostsTest, allow4AlreadyReserved) {
                                      SubnetID(1), SUBNET_ID_UNUSED,
                                      IOAddress("192.0.2.1")));
     // Adding this should work because the HW address is different.
-    EXPECT_NO_THROW(cfg.add(host2));
+    ASSERT_NO_THROW(cfg.add(host2));
+
+    ConstHostCollection returned;
+    ASSERT_NO_THROW(returned = cfg.getAll4(host1->getIPv4SubnetID(), IOAddress("192.0.2.1")));
+    EXPECT_EQ(2, returned.size());
 }
 
 // Checks that it's not possible for two hosts to have the same address
@@ -1004,7 +1008,11 @@ TEST_F(CfgHostsTest, allow6AlreadyReserved) {
                                     IOAddress("2001:db8::1")));
 
     // Adding this should work because the DUID is different.
-    EXPECT_NO_THROW(cfg.add(host2));
+    ASSERT_NO_THROW(cfg.add(host2));
+
+    ConstHostCollection returned;
+    ASSERT_NO_THROW(returned = cfg.getAll6(host1->getIPv6SubnetID(), IOAddress("2001:db8::1")));
+    EXPECT_EQ(2, returned.size());
 }
 
 // Check that no error is reported when adding a host with subnet
index 1a9e1e6cdf24ccf9286c32f65d720a798975f2e8..34d0554b1da094208e5aa3b66f27bd0672a3691e 100644 (file)
@@ -658,6 +658,11 @@ public:
         return (getOne());
     }
 
+    ConstHostCollection
+    getAll4(const SubnetID&, const asiolink::IOAddress&) const {
+        return (getCollection());
+    }
+
     ConstHostPtr get6(const SubnetID&, const Host::IdentifierType&,
                       const uint8_t*, const size_t) const {
         return (getOne());
@@ -671,6 +676,11 @@ public:
         return (getOne());
     }
 
+    ConstHostCollection
+    getAll6(const SubnetID&, const IOAddress&) const {
+        return (getCollection());
+    }
+
     void add(const HostPtr&) {
     }
 
index adbbd07ec1073f4efde33577b4a597332d79fe5f..5facee2ace1f6ba22294d35b35139ab17d34f4c3 100644 (file)
@@ -247,6 +247,33 @@ protected:
     void testGet6ByPrefix(BaseHostDataSource& data_source1,
                           BaseHostDataSource& data_source2);
 
+    /// @brief This test verifies that HostMgr returns all reservations for the
+    /// specified DHCPv4 subnet and IPv4 address.
+    ///
+    /// If reservations are added to different host data sources, it is expected
+    /// that the @c HostMgr will retrieve reservations from both of them.
+    ///
+    /// @param data_source1 Host data source to which first reservation is
+    /// inserted.
+    /// @param data_source2 Host data source to which second reservation is
+    /// inserted.
+    void testGetAll4BySubnetIP(BaseHostDataSource& data_source1,
+                               BaseHostDataSource& data_source2);
+
+    /// @brief This test verifies that HostMgr returns all reservations for the
+    /// specified DHCPv6 subnet and IPv6 address.
+    ///
+    /// If reservations are added to different host data sources, it is expected
+    /// that the @c HostMgr will retrieve reservations from both of them.
+    ///
+    /// @param data_source1 Host data source to which first reservation is
+    /// inserted.
+    /// @param data_source2 Host data source to which second reservation is
+    /// inserted.
+    void testGetAll6BySubnetIP(BaseHostDataSource& data_source1,
+                               BaseHostDataSource& data_source2);
+
+
     /// @brief HW addresses to be used by the tests.
     std::vector<HWAddrPtr> hwaddrs_;
     /// @brief DUIDs to be used by the tests.
@@ -1131,6 +1158,80 @@ HostMgrTest::testGet6ByPrefix(BaseHostDataSource& data_source1,
     EXPECT_FALSE(host);
 }
 
+void
+HostMgrTest::testGetAll4BySubnetIP(BaseHostDataSource& data_source1,
+                                   BaseHostDataSource& data_source2) {
+    // Set the mode of operation with multiple reservations for the same
+    // IP address.
+    ASSERT_TRUE(HostMgr::instance().setIPReservationUnique(false));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->setIPReservationUnique(false);
+
+    // Initially, no reservations should be present.
+    ConstHostCollection hosts = HostMgr::instance().getAll4(SubnetID(1),
+                                                            IOAddress("192.0.2.5"));
+    ASSERT_TRUE(hosts.empty());
+
+    // Add two reservations for the same subnet and IP address.
+    addHost4(data_source1, hwaddrs_[0], SubnetID(1), IOAddress("192.0.2.5"));
+    addHost4(data_source2, hwaddrs_[1], SubnetID(1), IOAddress("192.0.2.5"));
+
+    CfgMgr::instance().commit();
+
+    // If there non-matching subnet is specified, nothing should be returned.
+    hosts = HostMgr::instance().getAll4(SubnetID(100), IOAddress("192.0.2.5"));
+    ASSERT_TRUE(hosts.empty());
+
+    // For the correct subnet, there should be two reservations.
+    hosts = HostMgr::instance().getAll4(SubnetID(1), IOAddress("192.0.2.5"));
+    ASSERT_EQ(2, hosts.size());
+
+    // Make sure that subnet is correct.
+    EXPECT_EQ(1, hosts[0]->getIPv4SubnetID());
+    EXPECT_EQ(1, hosts[1]->getIPv4SubnetID());
+
+    // Make sure that two hosts were returned.
+    EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText());
+    EXPECT_EQ("192.0.2.5", hosts[1]->getIPv4Reservation().toText());
+}
+
+void
+HostMgrTest::testGetAll6BySubnetIP(BaseHostDataSource& data_source1,
+                                   BaseHostDataSource& data_source2) {
+    // Set the mode of operation with multiple reservations for the same
+    // IP address.
+    ASSERT_TRUE(HostMgr::instance().setIPReservationUnique(false));
+    CfgMgr::instance().getStagingCfg()->getCfgHosts()->setIPReservationUnique(false);
+
+    // Initially, no reservations should be present.
+    ConstHostCollection hosts = HostMgr::instance().getAll6(SubnetID(1),
+                                                            IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(hosts.empty());
+
+    // Add two reservations for the same subnet.
+    addHost6(data_source1, duids_[0], SubnetID(1), IOAddress("2001:db8:1::5"));
+    addHost6(data_source2, duids_[1], SubnetID(1), IOAddress("2001:db8:1::5"));
+
+    CfgMgr::instance().commit();
+
+    // If there non-matching subnet is specified, nothing should be returned.
+    hosts = HostMgr::instance().getAll6(SubnetID(100), IOAddress("2001:db8:1::5"));
+    ASSERT_TRUE(hosts.empty());
+
+    // For the correct subnet, there should be two reservations.
+    hosts = HostMgr::instance().getAll6(SubnetID(1), IOAddress("2001:db8:1::5"));
+    ASSERT_EQ(2, hosts.size());
+
+    // Make sure that subnet is correct.
+    EXPECT_EQ(1, hosts[0]->getIPv6SubnetID());
+    EXPECT_EQ(1, hosts[1]->getIPv6SubnetID());
+
+    // Make sure that two different hosts were returned.
+    EXPECT_TRUE(hosts[0]->hasReservation(
+                IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5"))));
+    EXPECT_TRUE(hosts[1]->hasReservation(
+                IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5"))));
+}
+
 // This test verifies that HostMgr returns all reservations for the
 // specified HW address. The reservations are defined in the server's
 // configuration.
@@ -1145,6 +1246,20 @@ TEST_F(HostMgrTest, getAll4BySubnet) {
     testGetAll4BySubnet(*getCfgHosts(), *getCfgHosts());
 }
 
+// This test verifies that HostMgr returns all reservations for the specified
+// IPv4 subnet and reserved address. The reservations are specified in the
+// server's configuration.
+TEST_F(HostMgrTest, getAll4BySubnetIP) {
+    testGetAll4BySubnetIP(*getCfgHosts(), *getCfgHosts());
+}
+
+// This test verifies that HostMgr returns all reservations for the specified
+// IPv6 subnet and reserved address. The reservations are specified in the
+// server's configuration.
+TEST_F(HostMgrTest, getAll6BySubnetIP) {
+    testGetAll6BySubnetIP(*getCfgHosts(), *getCfgHosts());
+}
+
 // This test verifies that HostMgr returns all reservations for the
 // specified DHCPv6 subnet. The reservations are defined in the server's
 // configuration.
@@ -1435,6 +1550,18 @@ TEST_F(MySQLHostMgrTest, getAll6BySubnet) {
     testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance());
 }
 
+// This test verifies that HostMgr returns all reservations for the specified
+// IPv4 subnet and reserved address.
+TEST_F(MySQLHostMgrTest, getAll4BySubnetIP) {
+    testGetAll4BySubnetIP(*getCfgHosts(), *getCfgHosts());
+}
+
+// This test verifies that HostMgr returns all reservations for the specified
+// IPv6 subnet and reserved address.
+TEST_F(MySQLHostMgrTest, getAll6BySubnetIP) {
+    testGetAll6BySubnetIP(*getCfgHosts(), *getCfgHosts());
+}
+
 // This test verifies that reservations for a particular hostname can be
 // retrieved from the configuration file and a database simultaneously.
 TEST_F(MySQLHostMgrTest, getAllbyHostname) {
@@ -1600,6 +1727,18 @@ TEST_F(PostgreSQLHostMgrTest, getAll6BySubnet) {
     testGetAll6BySubnet(*getCfgHosts(), HostMgr::instance());
 }
 
+// This test verifies that HostMgr returns all reservations for the specified
+// IPv4 subnet and reserved address.
+TEST_F(PostgreSQLHostMgrTest, getAll4BySubnetIP) {
+    testGetAll4BySubnetIP(*getCfgHosts(), *getCfgHosts());
+}
+
+// This test verifies that HostMgr returns all reservations for the specified
+// IPv6 subnet and reserved address.
+TEST_F(PostgreSQLHostMgrTest, getAll6BySubnetIP) {
+    testGetAll6BySubnetIP(*getCfgHosts(), *getCfgHosts());
+}
+
 // This test verifies that reservations for a particular hostname can be
 // retrieved from the configuration file and a database simultaneously.
 TEST_F(PostgreSQLHostMgrTest, getAllbyHostname) {
index 8e34bc15064b160eee057aead2c391f5ed1f0510..9df193692ae631f303254fdc83249dfd0accce98 100644 (file)
@@ -1832,21 +1832,33 @@ GenericHostDataSourceTest::testAllowDuplicateIPv6() {
     ASSERT_TRUE(hdsptr_->setIPReservationUnique(false));
 
     // Create a host reservations.
-    HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_HWADDR, true);
+    HostPtr host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_HWADDR, true, true);
+    auto host_id = host->getHostId();
+    auto subnet_id = host->getIPv6SubnetID();
 
     // Add this reservation once.
     ASSERT_NO_THROW(hdsptr_->add(host));
 
     // Then try to add it again, it should throw an exception because the
     // HWADDR is the same.
+    host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_HWADDR, true, false);
+    host->setHostId(++host_id);
+    host->setIPv6SubnetID(subnet_id);
     ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
 
     // This time use a different host identifier and try again.
     // This update should succeed because we permitted to create
     // multiple IP reservations for the same IP address but different
     // identifier.
-    ASSERT_NO_THROW(host->setIdentifier("01:02:03:04:05:06", "hw-address"));
+    host = HostDataSourceUtils::initializeHost6("2001:db8::1", Host::IDENT_HWADDR, true, true);
+    host->setHostId(++host_id);
+    host->setIPv6SubnetID(subnet_id);
     ASSERT_NO_THROW(hdsptr_->add(host));
+
+    ConstHostCollection returned;
+    ASSERT_NO_THROW(returned = hdsptr_->getAll6(host->getIPv6SubnetID(), IOAddress("2001:db8::1")));
+    EXPECT_EQ(2, returned.size());
+    EXPECT_NE(returned[0]->getIdentifierAsText(), returned[1]->getIdentifierAsText());
 }
 
 void
@@ -1894,21 +1906,33 @@ GenericHostDataSourceTest::testAllowDuplicateIPv4() {
     ASSERT_TRUE(hdsptr_->setIPReservationUnique(false));
 
     // Create a host reservations.
-    HostPtr host = HostDataSourceUtils::initializeHost4("192.0.2.1", Host::IDENT_DUID);
+    HostPtr host = HostDataSourceUtils::initializeHost4("192.0.2.1", Host::IDENT_DUID, true);
+    auto host_id = host->getHostId();
+    auto subnet_id = host->getIPv4SubnetID();
 
     // Add this reservation once.
     ASSERT_NO_THROW(hdsptr_->add(host));
 
     // Then try to add it again, it should throw an exception because the
     // DUID is the same.
+    host = HostDataSourceUtils::initializeHost4("192.0.2.1", Host::IDENT_DUID, false);
+    host->setHostId(++host_id);
+    host->setIPv4SubnetID(subnet_id);
     ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
 
     // This time use a different host identifier and try again.
     // This update should succeed because we permitted to create
     // multiple IP reservations for the same IP address but different
     // identifier.
-    ASSERT_NO_THROW(host->setIdentifier("01:02:03:04:05:06", "hw-address"));
+    host = HostDataSourceUtils::initializeHost4("192.0.2.1", Host::IDENT_DUID, true);
+    host->setHostId(++host_id);
+    host->setIPv4SubnetID(subnet_id);
     ASSERT_NO_THROW(hdsptr_->add(host));
+
+    ConstHostCollection returned;
+    ASSERT_NO_THROW(returned = hdsptr_->getAll4(host->getIPv4SubnetID(), IOAddress("192.0.2.1")));
+    EXPECT_EQ(2, returned.size());
+    EXPECT_NE(returned[0]->getIdentifierAsText(), returned[1]->getIdentifierAsText());
 }
 
 void
index 1c6447286190077703364dc2831fc06d67d327c1..00cd1df0bbfe1812ba46672a37b31871c9bc526f 100644 (file)
@@ -58,12 +58,13 @@ HostDataSourceUtils::generateIdentifier(const bool new_identifier) {
 
 HostPtr
 HostDataSourceUtils::initializeHost4(const std::string& address,
-                                     const Host::IdentifierType& id) {
+                                     const Host::IdentifierType& id,
+                                     const bool new_identifier) {
     std::vector<uint8_t> ident;
     if (id == Host::IDENT_HWADDR) {
-        ident = generateHWAddr();
+        ident = generateHWAddr(new_identifier);
     } else {
-        ident = generateIdentifier();
+        ident = generateIdentifier(new_identifier);
     }
 
     // Let's create ever increasing subnet-ids. Let's keep those different,
index c10f2de57ec000434f7297c07253e7f9cfe41a4c..ff3fabd39cf9d27772f2d26940a335bea94f83c8 100644 (file)
@@ -20,14 +20,17 @@ namespace test {
 /// Intended to be used in tests and benchmarks.
 class HostDataSourceUtils {
 public:
-        /// @brief Creates a host reservation for specified IPv4 address.
+    /// @brief Creates a host reservation for specified IPv4 address.
     ///
     /// @param address IPv4 address to be set
     /// @param id Identifier type.
+    /// @param new_identifier Boolean value indicating if new host
+    /// identifier should be generated or the same as previously.
     ///
     /// @return generated Host object
     static isc::dhcp::HostPtr initializeHost4(const std::string& address,
-                                       const Host::IdentifierType& id);
+                                              const Host::IdentifierType& id,
+                                              const bool new_identifier = true);
 
     /// @brief Creates a host reservation for specified IPv6 address.
     ///
index 736047a1183fcbc89263562a91ce602468483be9..fdfc6f73ffd21f75dfd895677aa9a4b905e935cf 100644 (file)
@@ -239,6 +239,17 @@ MemHostDataSource::get4(const SubnetID& subnet_id,
     return (ConstHostPtr());
 }
 
+ConstHostCollection
+MemHostDataSource::getAll4(const SubnetID& subnet_id,
+                           const asiolink::IOAddress& address) const {
+    ConstHostCollection hosts;
+    auto host = get4(subnet_id, address);
+    if (host) {
+        hosts.push_back(host);
+    }
+    return (hosts);
+}
+
 ConstHostPtr
 MemHostDataSource::get6(const asiolink::IOAddress& /*prefix*/,
                         const uint8_t /*prefix_len*/) const {
@@ -271,6 +282,17 @@ MemHostDataSource::get6(const SubnetID& subnet_id,
     return (ConstHostPtr());
 }
 
+ConstHostCollection
+MemHostDataSource::getAll6(const SubnetID& subnet_id,
+                           const asiolink::IOAddress& address) const {
+    ConstHostCollection hosts;
+    auto host = get6(subnet_id, address);
+    if (host) {
+        hosts.push_back(host);
+    }
+    return (hosts);
+}
+
 void
 MemHostDataSource::add(const HostPtr& host) {
     host->setHostId(++next_host_id_);
index 7ea08e0b08955e26e38238150e43a882cc4d814a..a97fd4faac12addbcc8d556849fe6748a6608347 100644 (file)
@@ -158,6 +158,17 @@ public:
     get4(const SubnetID& subnet_id,
          const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv4 subnet and having
+    /// a reservation for a specified address.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv4 address.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll4(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Returns a host connected to the IPv6 subnet.
     ///
     /// @param subnet_id Subnet identifier.
@@ -192,6 +203,17 @@ public:
     virtual ConstHostPtr
     get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) const;
 
+    /// @brief Returns all hosts connected to the IPv6 subnet and having
+    /// a reservation for a specified address or delegated prefix (lease).
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param address reserved IPv6 address/prefix.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll6(const SubnetID& subnet_id,
+            const asiolink::IOAddress& address) const;
+
     /// @brief Adds a new host to the collection.
     ///
     /// @param host Pointer to the new @c Host object being added.