Parcourir la source

[master] Merge branch 'trac4302_rebase'

Marcin Siodelski il y a 9 ans
Parent
commit
3979656c91

+ 47 - 1
src/bin/admin/scripts/mysql/dhcpdb_create.mysql

@@ -1,4 +1,4 @@
-# Copyright (C) 2012-2015 Internet Systems Consortium.
+# Copyright (C) 2012-2016 Internet Systems Consortium.
 #
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # 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
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -395,6 +395,52 @@ SET version = '4', minor = '1';
 
 
 # This line concludes database upgrade to version 4.1.
 # This line concludes database upgrade to version 4.1.
 
 
+# Update index used for searching DHCPv4 reservations by identifier and subnet id.
+# This index is now unique (to prevent duplicates) and includes DHCPv4 subnet
+# identifier.
+DROP INDEX key_dhcp4_identifier_subnet_id ON hosts;
+CREATE UNIQUE INDEX key_dhcp4_identifier_subnet_id ON hosts (dhcp_identifier ASC , dhcp_identifier_type ASC , dhcp4_subnet_id ASC);
+
+# Update index used for searching DHCPv6 reservations by identifier and subnet id.
+# This index is now unique to prevent duplicates.
+DROP INDEX key_dhcp6_identifier_subnet_id ON hosts;
+CREATE UNIQUE INDEX key_dhcp6_identifier_subnet_id ON hosts (dhcp_identifier ASC , dhcp_identifier_type ASC , dhcp6_subnet_id ASC);
+
+# Create index to search for reservations using IP address and subnet id.
+# This unique index guarantees that there is only one occurence of the
+# particular IPv4 address for a given subnet.
+CREATE UNIQUE INDEX key_dhcp4_ipv4_address_subnet_id ON hosts (ipv4_address ASC , dhcp4_subnet_id ASC);
+
+# Create index to search for reservations using address/prefix and prefix
+# length.
+CREATE UNIQUE INDEX key_dhcp6_address_prefix_len ON ipv6_reservations (address ASC , prefix_len ASC);
+
+# Create a table mapping host identifiers to their names. Values in this
+# table are used as a foreign key in hosts table to guarantee that only
+# identifiers present in host_identifier_type table are used in hosts
+# table.
+CREATE TABLE IF NOT EXISTS host_identifier_type (
+    type TINYINT PRIMARY KEY NOT NULL,   # Lease type code.
+    name VARCHAR(32)                     # Name of the lease type
+) ENGINE = INNODB;
+
+START TRANSACTION;
+INSERT INTO host_identifier_type VALUES (0, "hw-address"); # Non-temporary v6 addresses
+INSERT INTO host_identifier_type VALUES (1, "duid");       # Temporary v6 addresses
+INSERT INTO host_identifier_type VALUES (2, "circuit-id"); # Prefix delegations
+COMMIT;
+
+# Add a constraint that any identifier type value added to the hosts
+# must map to a value in the host_identifier_type table.
+ALTER TABLE hosts
+    ADD CONSTRAINT fk_host_identifier_type FOREIGN KEY (dhcp_identifier_type)
+    REFERENCES host_identifier_type (type);
+
+# Update the schema version number
+UPDATE schema_version
+SET version = '4', minor = '2';
+# This line concludes database upgrade to version 4.2.
+
 # Notes:
 # Notes:
 #
 #
 # Indexes
 # Indexes

+ 8 - 1
src/bin/admin/tests/mysql_tests.sh.in

@@ -1,6 +1,6 @@
 #!/bin/sh
 #!/bin/sh
 
 
-# Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 #
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # 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
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -178,6 +178,13 @@ EOF
     ERRCODE=$?
     ERRCODE=$?
     assert_eq 0 $ERRCODE "dhcp6_options table is missing or broken. (returned status code %d, expected %d)"
     assert_eq 0 $ERRCODE "dhcp6_options table is missing or broken. (returned status code %d, expected %d)"
 
 
+    # Sixth table: host_identifier_type
+    mysql -u$db_user -p$db_password $db_name >/dev/null 2>&1 <<EOF
+    SELECT type, name FROM host_identifier_type;
+EOF
+    ERRCODE=$?
+    assert_eq 0 $ERRCODE "host_identifier_type table is missing or broken. (returned status code %d, expected %d)"
+
     # Let's wipe the whole database
     # Let's wipe the whole database
     mysql_wipe
     mysql_wipe
 
 

+ 50 - 1
src/lib/dhcpsrv/base_host_data_source.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -98,6 +98,23 @@ public:
     virtual ConstHostCollection
     virtual ConstHostCollection
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const = 0;
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const = 0;
 
 
+    /// @brief Return all hosts connected to any subnet for which reservations
+    /// have been made using a specified identifier.
+    ///
+    /// This method returns all @c Host objects which represent reservations
+    /// for a specified identifier. This method may return multiple hosts
+    /// because a particular client may have reservations in multiple subnets.
+    ///
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll(const Host::IdentifierType& identifier_type,
+           const uint8_t* identifier_begin,
+           const size_t identifier_len) const = 0;
+
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     ///
     ///
     /// This method may return multiple @c Host objects if they are connected
     /// This method may return multiple @c Host objects if they are connected
@@ -127,6 +144,23 @@ public:
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
          const DuidPtr& duid = DuidPtr()) const = 0;
          const DuidPtr& duid = DuidPtr()) const = 0;
 
 
+
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get4(const SubnetID& subnet_id,
+         const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin,
+         const size_t identifier_len) const = 0;
+
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// a reservation for a specified IPv4 address.
     /// a reservation for a specified IPv4 address.
     ///
     ///
@@ -165,6 +199,21 @@ public:
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
          const HWAddrPtr& hwaddr = HWAddrPtr()) const = 0;
          const HWAddrPtr& hwaddr = HWAddrPtr()) const = 0;
 
 
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id,
+         const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin,
+         const size_t identifier_len) const = 0;
 
 
     /// @brief Returns a host using the specified IPv6 prefix.
     /// @brief Returns a host using the specified IPv6 prefix.
     ///
     ///

+ 190 - 51
src/lib/dhcpsrv/cfg_hosts.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -34,6 +34,29 @@ CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) {
 }
 }
 
 
 ConstHostCollection
 ConstHostCollection
+CfgHosts::getAll(const Host::IdentifierType& identifier_type,
+                 const uint8_t* identifier_begin,
+                 const size_t identifier_len) const {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal method.
+    ConstHostCollection collection;
+    getAllInternal<ConstHostCollection>(identifier_type, identifier_begin,
+                                        identifier_len, collection);
+    return (collection);
+}
+
+HostCollection
+CfgHosts::getAll(const Host::IdentifierType& identifier_type,
+                 const uint8_t* identifier_begin, const size_t identifier_len) {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal method.
+    HostCollection collection;
+    getAllInternal<HostCollection>(identifier_type, identifier_begin,
+                                   identifier_len, collection);
+    return (collection);
+}
+
+ConstHostCollection
 CfgHosts::getAll4(const IOAddress& address) const {
 CfgHosts::getAll4(const IOAddress& address) const {
     // Do not issue logging message here because it will be logged by
     // Do not issue logging message here because it will be logged by
     // the getAllInternal4 method.
     // the getAllInternal4 method.
@@ -71,27 +94,26 @@ CfgHosts::getAll6(const IOAddress& address) {
 
 
 template<typename Storage>
 template<typename Storage>
 void
 void
-CfgHosts::getAllInternal(const std::vector<uint8_t>& identifier,
-                         const Host::IdentifierType& identifier_type,
+CfgHosts::getAllInternal(const Host::IdentifierType& identifier_type,
+                         const uint8_t* identifier,
+                         const size_t identifier_len,
                          Storage& storage) const {
                          Storage& storage) const {
     // We will need to transform the identifier into the textual format.
     // We will need to transform the identifier into the textual format.
     // Until we do it, we mark it as invalid.
     // Until we do it, we mark it as invalid.
     std::string identifier_text = "(invalid)";
     std::string identifier_text = "(invalid)";
-    if (!identifier.empty()) {
-        try {
-            // Use Host object to find the textual form of the identifier.
-            // This may throw exception if the identifier is invalid.
-            Host host(&identifier[0], identifier.size(), identifier_type,
-                      SubnetID(0), SubnetID(0), IOAddress::IPV4_ZERO_ADDRESS());
-            identifier_text = host.getIdentifierAsText();
-
-        } catch (...) {
-            // Suppress exception and keep using (invalid) as an
-            // identifier. We will log that the identifier is
-            // invalid and return.
-        }
-
+    try {
+        // Use Host object to find the textual form of the identifier.
+        // This may throw exception if the identifier is invalid.
+        Host host(identifier, identifier_len, identifier_type,
+                  SubnetID(0), SubnetID(0), IOAddress::IPV4_ZERO_ADDRESS());
+        identifier_text = host.getIdentifierAsText();
+
+    } catch (...) {
+        // Suppress exception and keep using (invalid) as an
+        // identifier. We will log that the identifier is
+        // invalid and return.
     }
     }
+
     // This will log that we're invoking this function with the specified
     // This will log that we're invoking this function with the specified
     // identifier. The identifier may also be marked as (invalid) if it
     // identifier. The identifier may also be marked as (invalid) if it
     // had 0 length or its type is unsupported.
     // had 0 length or its type is unsupported.
@@ -106,10 +128,13 @@ CfgHosts::getAllInternal(const std::vector<uint8_t>& identifier,
     // Use the identifier and identifier type as a composite key.
     // Use the identifier and identifier type as a composite key.
     const HostContainerIndex0& idx = hosts_.get<0>();
     const HostContainerIndex0& idx = hosts_.get<0>();
     boost::tuple<const std::vector<uint8_t>, const Host::IdentifierType> t =
     boost::tuple<const std::vector<uint8_t>, const Host::IdentifierType> t =
-        boost::make_tuple(identifier, identifier_type);
+        boost::make_tuple(std::vector<uint8_t>(identifier,
+                                               identifier + identifier_len),
+                                               identifier_type);
 
 
     // Append each Host object to the storage.
     // Append each Host object to the storage.
-    for (HostContainerIndex0::iterator host = idx.lower_bound(t); host != idx.upper_bound(t);
+    for (HostContainerIndex0::iterator host = idx.lower_bound(t);
+         host != idx.upper_bound(t);
          ++host) {
          ++host) {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
         LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
                   HOSTS_CFG_GET_ALL_IDENTIFIER_HOST)
                   HOSTS_CFG_GET_ALL_IDENTIFIER_HOST)
@@ -133,12 +158,14 @@ CfgHosts::getAllInternal(const HWAddrPtr& hwaddr, const DuidPtr& duid,
         .arg(duid ? duid->toText() : "(no-duid)");
         .arg(duid ? duid->toText() : "(no-duid)");
 
 
     // Get hosts using HW address.
     // Get hosts using HW address.
-    if (hwaddr) {
-        getAllInternal<Storage>(hwaddr->hwaddr_, Host::IDENT_HWADDR, storage);
+    if (hwaddr && !hwaddr->hwaddr_.empty()) {
+        getAllInternal<Storage>(Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
+                                hwaddr->hwaddr_.size(), storage);
     }
     }
     // Get hosts using DUID.
     // Get hosts using DUID.
-    if (duid) {
-        getAllInternal<Storage>(duid->getDuid(), Host::IDENT_DUID, storage);
+    if (duid && !duid->getDuid().empty()) {
+        getAllInternal<Storage>(Host::IDENT_DUID, &duid->getDuid()[0],
+                                duid->getDuid().size(), storage);
     }
     }
 }
 }
 
 
@@ -200,13 +227,23 @@ CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const {
         .arg(storage.size());
         .arg(storage.size());
 }
 }
 
 
-
 ConstHostPtr
 ConstHostPtr
 CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
 CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
                const DuidPtr& duid) const {
                const DuidPtr& duid) const {
     // Do not log here because getHostInternal logs.
     // Do not log here because getHostInternal logs.
     // The false value indicates that it is an IPv4 subnet.
     // The false value indicates that it is an IPv4 subnet.
-    return (getHostInternal(subnet_id, false, hwaddr, duid));
+    HostPtr host;
+    if (hwaddr && !hwaddr->hwaddr_.empty()) {
+        host = getHostInternal(subnet_id, false, Host::IDENT_HWADDR,
+                               &hwaddr->hwaddr_[0],
+                               hwaddr->hwaddr_.size());
+    }
+    if (!host && duid && !duid->getDuid().empty()) {
+        host = getHostInternal(subnet_id, false, Host::IDENT_DUID,
+                               &duid->getDuid()[0],
+                               duid->getDuid().size());
+    }
+    return (host);
 }
 }
 
 
 HostPtr
 HostPtr
@@ -214,7 +251,36 @@ CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
                const DuidPtr& duid) {
                const DuidPtr& duid) {
     // Do not log here because getHostInternal logs.
     // Do not log here because getHostInternal logs.
     // The false value indicates that it is an IPv4 subnet.
     // The false value indicates that it is an IPv4 subnet.
-    return (getHostInternal(subnet_id, false, hwaddr, duid));
+    HostPtr host;
+    if (hwaddr && !hwaddr->hwaddr_.empty()) {
+        host = getHostInternal(subnet_id, false, Host::IDENT_HWADDR,
+                               &hwaddr->hwaddr_[0],
+                               hwaddr->hwaddr_.size());
+    }
+    if (!host && duid && !duid->getDuid().empty()) {
+        host = getHostInternal(subnet_id, false, Host::IDENT_DUID,
+                               &duid->getDuid()[0],
+                               duid->getDuid().size());
+    }
+    return (host);
+}
+
+ConstHostPtr
+CfgHosts::get4(const SubnetID& subnet_id,
+               const Host::IdentifierType& identifier_type,
+               const uint8_t* identifier_begin,
+               const size_t identifier_len) const {
+    return (getHostInternal(subnet_id, false, identifier_type, identifier_begin,
+                            identifier_len));
+}
+
+HostPtr
+CfgHosts::get4(const SubnetID& subnet_id,
+               const Host::IdentifierType& identifier_type,
+               const uint8_t* identifier_begin,
+               const size_t identifier_len) {
+    return (getHostInternal(subnet_id, false, identifier_type, identifier_begin,
+                            identifier_len));
 }
 }
 
 
 ConstHostPtr
 ConstHostPtr
@@ -246,7 +312,19 @@ CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) const {
                const HWAddrPtr& hwaddr) const {
     // Do not log here because getHostInternal logs.
     // Do not log here because getHostInternal logs.
     // The true value indicates that it is an IPv6 subnet.
     // The true value indicates that it is an IPv6 subnet.
-    return (getHostInternal(subnet_id, true, hwaddr, duid));
+    HostPtr host;
+    if (duid && !duid->getDuid().empty()) {
+        host = getHostInternal(subnet_id, true, Host::IDENT_DUID,
+                               &duid->getDuid()[0],
+                               duid->getDuid().size());
+    }
+    if (!host && hwaddr && !hwaddr->hwaddr_.empty()) {
+        host = getHostInternal(subnet_id, true, Host::IDENT_HWADDR,
+                               &hwaddr->hwaddr_[0],
+                               hwaddr->hwaddr_.size());
+    }
+
+    return (host);
 }
 }
 
 
 HostPtr
 HostPtr
@@ -254,18 +332,47 @@ CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) {
                const HWAddrPtr& hwaddr) {
     // Do not log here because getHostInternal logs.
     // Do not log here because getHostInternal logs.
     // The true value indicates that it is an IPv6 subnet.
     // The true value indicates that it is an IPv6 subnet.
-    return (getHostInternal(subnet_id, true, hwaddr, duid));
+    HostPtr host;
+    if (duid && !duid->getDuid().empty()) {
+        host = getHostInternal(subnet_id, true, Host::IDENT_DUID,
+                               &duid->getDuid()[0],
+                               duid->getDuid().size());
+    }
+    if (!host && hwaddr && !hwaddr->hwaddr_.empty()) {
+        host = getHostInternal(subnet_id, true, Host::IDENT_HWADDR,
+                               &hwaddr->hwaddr_[0],
+                               hwaddr->hwaddr_.size());
+    }
+
+    return (host);
 }
 }
 
 
 ConstHostPtr
 ConstHostPtr
-CfgHosts::get6(const IOAddress&, const uint8_t) const {
-    isc_throw(isc::NotImplemented,
-              "get6(prefix, len) const is not implemented");
+CfgHosts::get6(const SubnetID& subnet_id,
+               const Host::IdentifierType& identifier_type,
+               const uint8_t* identifier_begin,
+               const size_t identifier_len) const {
+    return (getHostInternal(subnet_id, true, identifier_type, identifier_begin,
+                            identifier_len));
 }
 }
 
 
 HostPtr
 HostPtr
-CfgHosts::get6(const IOAddress&, const uint8_t) {
-    isc_throw(isc::NotImplemented, "get6(prefix, len) is not implemented");
+CfgHosts::get6(const SubnetID& subnet_id,
+               const Host::IdentifierType& identifier_type,
+               const uint8_t* identifier_begin,
+               const size_t identifier_len) {
+    return (getHostInternal(subnet_id, true, identifier_type, identifier_begin,
+                            identifier_len));
+}
+
+ConstHostPtr
+CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
+    return (getHostInternal6<ConstHostPtr>(prefix, prefix_len));
+}
+
+HostPtr
+CfgHosts::get6(const IOAddress& prefix, const uint8_t prefix_len) {
+    return (getHostInternal6<HostPtr>(prefix, prefix_len));
 }
 }
 
 
 ConstHostPtr
 ConstHostPtr
@@ -316,6 +423,36 @@ CfgHosts::getHostInternal6(const SubnetID& subnet_id,
 
 
 }
 }
 
 
+template<typename ReturnType>
+ReturnType
+CfgHosts::getHostInternal6(const asiolink::IOAddress& prefix,
+                           const uint8_t prefix_len) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_PREFIX)
+        .arg(prefix.toText()).arg(static_cast<int>(prefix_len));
+
+    // Let's get all reservations that match subnet_id, address.
+    const HostContainer6Index0& idx = hosts6_.get<0>();
+    HostContainer6Index0Range r = make_pair(idx.lower_bound(prefix),
+                                            idx.upper_bound(prefix));
+    for (HostContainer6Index0::iterator resrv = r.first; resrv != r.second;
+         ++resrv) {
+        if (resrv->resrv_.getPrefixLen() == prefix_len) {
+            LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
+                      HOSTS_CFG_GET_ONE_PREFIX_HOST)
+                .arg(prefix.toText())
+                .arg(static_cast<int>(prefix_len))
+                .arg(resrv->host_->toText());
+            return (resrv->host_);
+        }
+    }
+
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
+              HOSTS_CFG_GET_ONE_PREFIX_NULL)
+        .arg(prefix.toText())
+        .arg(static_cast<int>(prefix_len));
+    return (ReturnType());
+}
+
 template<typename Storage>
 template<typename Storage>
 void
 void
 CfgHosts::getAllInternal6(const SubnetID& subnet_id,
 CfgHosts::getAllInternal6(const SubnetID& subnet_id,
@@ -344,7 +481,7 @@ CfgHosts::getAllInternal6(const SubnetID& subnet_id,
                   HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST)
                   HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST)
             .arg(subnet_id)
             .arg(subnet_id)
             .arg(address.toText())
             .arg(address.toText())
-            .arg(resrv->host_);
+            .arg(resrv->host_->toText());
         storage.push_back(resrv->host_);
         storage.push_back(resrv->host_);
     }
     }
 
 
@@ -357,18 +494,21 @@ CfgHosts::getAllInternal6(const SubnetID& subnet_id,
 
 
 HostPtr
 HostPtr
 CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
 CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
-                          const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
-    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID)
+                          const Host::IdentifierType& identifier_type,
+                          const uint8_t* identifier,
+                          const size_t identifier_len) const {
+
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER)
         .arg(subnet6 ? "IPv6" : "IPv4")
         .arg(subnet6 ? "IPv6" : "IPv4")
         .arg(subnet_id)
         .arg(subnet_id)
-        .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
-        .arg(duid ? duid->toText() : "(no-duid)");
+        .arg(Host::getIdentifierAsText(identifier_type, identifier, identifier_len));
 
 
-    // Get all hosts for the HW address and DUID. This may return multiple hosts
+    // Get all hosts for a specified identifier. This may return multiple hosts
     // for different subnets, but the number of hosts returned should be low
     // for different subnets, but the number of hosts returned should be low
     // because one host presumably doesn't show up in many subnets.
     // because one host presumably doesn't show up in many subnets.
     HostCollection hosts;
     HostCollection hosts;
-    getAllInternal<HostCollection>(hwaddr, duid, hosts);
+    getAllInternal<HostCollection>(identifier_type, identifier, identifier_len,
+                                   hosts);
 
 
     HostPtr host;
     HostPtr host;
     // Iterate over the returned hosts and select those for which the
     // Iterate over the returned hosts and select those for which the
@@ -393,10 +533,10 @@ CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
             } else {
             } else {
                 isc_throw(DuplicateHost,  "more than one reservation found"
                 isc_throw(DuplicateHost,  "more than one reservation found"
                           " for the host belonging to the subnet with id '"
                           " for the host belonging to the subnet with id '"
-                          << subnet_id << "' and using the HW address '"
-                          << (hwaddr ? hwaddr->toText(false) : "(null)")
-                          << "' and DUID '"
-                          << (duid ? duid->toText() : "(null)")
+                          << subnet_id << "' and using the identifier '"
+                          << Host::getIdentifierAsText(identifier_type,
+                                                       identifier,
+                                                       identifier_len)
                           << "'");
                           << "'");
             }
             }
         }
         }
@@ -404,24 +544,23 @@ CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
 
 
     if (host) {
     if (host) {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
         LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
-                  HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID)
+                  HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_HOST)
             .arg(subnet_id)
             .arg(subnet_id)
-            .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
-            .arg(duid ? duid->toText() : "(no-duid)")
+            .arg(Host::getIdentifierAsText(identifier_type, identifier,
+                                           identifier_len))
             .arg(host->toText());
             .arg(host->toText());
 
 
     } else {
     } else {
         LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
         LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
-                  HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID_NULL)
+                  HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_NULL)
             .arg(subnet_id)
             .arg(subnet_id)
-            .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
-            .arg(duid ? duid->toText() : "(no-duid)");
+            .arg(Host::getIdentifierAsText(identifier_type, identifier,
+                                           identifier_len));
     }
     }
 
 
     return (host);
     return (host);
 }
 }
 
 
-
 void
 void
 CfgHosts::add(const HostPtr& host) {
 CfgHosts::add(const HostPtr& host) {
     LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_ADD_HOST)
     LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_ADD_HOST)

+ 115 - 16
src/lib/dhcpsrv/cfg_hosts.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -69,6 +69,40 @@ public:
     virtual HostCollection
     virtual HostCollection
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr());
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr());
 
 
+    /// @brief Return all hosts connected to any subnet for which reservations
+    /// have been made using a specified identifier.
+    ///
+    /// This method returns all @c Host objects which represent reservations
+    /// for a specified identifier. This method may return multiple hosts
+    /// because a particular client may have reservations in multiple subnets.
+    ///
+    /// @param identifier_type One of the supported identifier types.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll(const Host::IdentifierType& identifier_type,
+           const uint8_t* identifier_begin, const size_t identifier_len) const;
+
+    /// @brief Non-const version of the @c getAll const method.
+    ///
+    /// This method returns all @c Host objects which represent reservations
+    /// for a specified identifier. This method may return multiple hosts
+    /// because a particular client may have reservations in multiple subnets.
+    ///
+    /// @param identifier_type One of the supported identifier types.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Collection of non-const @c Host objects.
+    virtual HostCollection
+    getAll(const Host::IdentifierType& identifier_type,
+           const uint8_t* identifier_begin,
+           const size_t identifier_len);
+
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     ///
     ///
     /// This method may return multiple @c Host objects if they are connected
     /// This method may return multiple @c Host objects if they are connected
@@ -143,6 +177,34 @@ public:
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
          const DuidPtr& duid = DuidPtr());
          const DuidPtr& duid = DuidPtr());
 
 
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Non-const @c Host object for which reservation has been made
+    /// using the specified identifier.
+    virtual HostPtr
+    get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len);
+
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// a reservation for a specified IPv4 address.
     /// a reservation for a specified IPv4 address.
     ///
     ///
@@ -183,12 +245,40 @@ public:
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
          const HWAddrPtr& hwaddr = HWAddrPtr());
          const HWAddrPtr& hwaddr = HWAddrPtr());
 
 
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Non-const @c Host object for which reservation has been made
+    /// using the specified identifier.
+    virtual HostPtr
+    get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len);
+
     /// @brief Returns a host using the specified IPv6 prefix.
     /// @brief Returns a host using the specified IPv6 prefix.
     ///
     ///
     /// @param prefix IPv6 prefix for which the @c Host object is searched.
     /// @param prefix IPv6 prefix for which the @c Host object is searched.
     /// @param prefix_len IPv6 prefix length.
     /// @param prefix_len IPv6 prefix length.
     ///
     ///
-    /// @throw isc::NotImplemented
+    /// @return Const @c Host object for which specified prefix is reserved.
     virtual ConstHostPtr
     virtual ConstHostPtr
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
 
 
@@ -197,7 +287,8 @@ public:
     /// @param prefix IPv6 prefix for which the @c Host object is searched.
     /// @param prefix IPv6 prefix for which the @c Host object is searched.
     /// @param prefix_len IPv6 prefix length.
     /// @param prefix_len IPv6 prefix length.
     ///
     ///
-    /// @throw isc::NotImplemented
+    /// @return Non-const @c Host object for which specified prefix is
+    /// reserved.
     virtual HostPtr
     virtual HostPtr
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len);
     get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len);
 
 
@@ -243,18 +334,19 @@ private:
     /// @brief Returns @c Host objects for the specific identifier and type.
     /// @brief Returns @c Host objects for the specific identifier and type.
     ///
     ///
     /// This private method is called by the @c CfgHosts::getAllInternal
     /// This private method is called by the @c CfgHosts::getAllInternal
-    /// method which finds the @c Host objects using HW address or DUID.
+    /// method which finds the @c Host objects using specified identifier.
     /// The retrieved objects are appended to the @c storage container.
     /// The retrieved objects are appended to the @c storage container.
     ///
     ///
-    /// @param identifier Binary representation of the HW addres or DUID (or
-    /// other future identifier).
     /// @param identifier_type The type of the supplied identifier.
     /// @param identifier_type The type of the supplied identifier.
+    /// @param identifier Pointer to a first byte of the identifier.
+    /// @param identifier_len Length of the identifier.
     /// @param [out] storage Container to which the retreived objects are
     /// @param [out] storage Container to which the retreived objects are
     /// appended.
     /// appended.
     /// @tparam One of the @c ConstHostCollection of @c HostCollection.
     /// @tparam One of the @c ConstHostCollection of @c HostCollection.
     template<typename Storage>
     template<typename Storage>
-    void getAllInternal(const std::vector<uint8_t>& identifier,
-                        const Host::IdentifierType& identifier_type,
+    void getAllInternal(const Host::IdentifierType& identifier_type,
+                        const uint8_t* identifier,
+                        const size_t identifier_len,
                         Storage& storage) const;
                         Storage& storage) const;
 
 
     /// @brief Returns @c Host objects for the specified HW address or DUID.
     /// @brief Returns @c Host objects for the specified HW address or DUID.
@@ -323,22 +415,25 @@ private:
 
 
     /// @brief Returns @c Host object connected to a subnet.
     /// @brief Returns @c Host object connected to a subnet.
     ///
     ///
-    /// This private method returns a pointer to the @c Host object identified
-    /// by the HW address or DUID and connected to an IPv4 or IPv6 subnet.
+    /// This private method returns a pointer to the @c Host object using
+    /// a specified identifier and connected to an IPv4 or IPv6 subnet.
     ///
     ///
     /// @param subnet_id IPv4 or IPv6 subnet identifier.
     /// @param subnet_id IPv4 or IPv6 subnet identifier.
     /// @param subnet6 A boolean flag which indicates if the subnet identifier
     /// @param subnet6 A boolean flag which indicates if the subnet identifier
     /// points to a IPv4 (if false) or IPv6 subnet (if true).
     /// points to a IPv4 (if false) or IPv6 subnet (if true).
-    /// @param hwaddr HW address identifying a host.
-    /// @param duid DUID identifying a host.
+    /// @param identifier_type Indentifier type.
+    /// @param identifier Pointer to a first byte of the buffer holding an
+    /// identifier.
+    /// @param identifier_len Identifier length.
     ///
     ///
     /// @return Pointer to the found host, or NULL if no host found.
     /// @return Pointer to the found host, or NULL if no host found.
     /// @throw isc::dhcp::DuplicateHost if method found more than one matching
     /// @throw isc::dhcp::DuplicateHost if method found more than one matching
     /// @c Host object.
     /// @c Host object.
-    HostPtr getHostInternal(const SubnetID& subnet_id,
-                            const bool subnet6,
-                            const HWAddrPtr& hwaddr,
-                            const DuidPtr& duid) const;
+    HostPtr
+    getHostInternal(const SubnetID& subnet_id, const bool subnet6,
+                    const Host::IdentifierType& identifier_type,
+                    const uint8_t* identifier,
+                    const size_t identifier_len) const;
 
 
     /// @brief Returns the @c Host object holding reservation for the IPv6
     /// @brief Returns the @c Host object holding reservation for the IPv6
     /// address and connected to the specific subnet.
     /// address and connected to the specific subnet.
@@ -357,6 +452,10 @@ private:
     ReturnType getHostInternal6(const SubnetID& subnet_id,
     ReturnType getHostInternal6(const SubnetID& subnet_id,
                                 const asiolink::IOAddress& adddress) const;
                                 const asiolink::IOAddress& adddress) const;
 
 
+    template<typename ReturnType>
+    ReturnType getHostInternal6(const asiolink::IOAddress& prefix,
+                                const uint8_t prefix_len) const;
+
     /// @brief Adds a new host to the v4 collection.
     /// @brief Adds a new host to the v4 collection.
     ///
     ///
     /// This is an internal method called by public @ref add.
     /// This is an internal method called by public @ref add.

+ 35 - 7
src/lib/dhcpsrv/host.cc

@@ -6,6 +6,7 @@
 
 
 #include <config.h>
 #include <config.h>
 #include <dhcpsrv/host.h>
 #include <dhcpsrv/host.h>
+#include <util/encode/hex.h>
 #include <util/strutil.h>
 #include <util/strutil.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <sstream>
 #include <sstream>
@@ -137,20 +138,47 @@ std::string
 Host::getIdentifierAsText() const {
 Host::getIdentifierAsText() const {
     std::string txt;
     std::string txt;
     if (hw_address_) {
     if (hw_address_) {
-        txt = "hwaddr=" + hw_address_->toText(false);
+        txt = getIdentifierAsText(IDENT_HWADDR, &hw_address_->hwaddr_[0],
+                                  hw_address_->hwaddr_.size());
+    } else if (duid_) {
+        txt = getIdentifierAsText(IDENT_DUID, &duid_->getDuid()[0],
+                                  duid_->getDuid().size());
     } else {
     } else {
-        txt = "duid=";
-        if (duid_) {
-            txt += duid_->toText();
-        } else {
-            txt += "(none)";
-        }
+        txt = "(none)";
     }
     }
 
 
     return (txt);
     return (txt);
 
 
 }
 }
 
 
+std::string
+Host::getIdentifierAsText(const IdentifierType& type, const uint8_t* value,
+                          const size_t length) {
+    // Length 0 doesn't make sense.
+    if (length == 0) {
+        isc_throw(BadValue, "invalid length 0 of the host identifier while"
+                  " converting the identifier to a textual form");
+    }
+
+    // Convert identifier into <type>=<value> form.
+    std::ostringstream s;
+    switch (type) {
+    case IDENT_HWADDR:
+        s << "hwaddr";
+        break;
+    case IDENT_DUID:
+        s << "duid";
+        break;
+    default:
+        isc_throw(BadValue, "requested conversion of the unsupported"
+                  " identifier into textual form");
+    }
+    std::vector<uint8_t> vec(value, value + length);
+    s << "=" << util::encode::encodeHex(vec);
+    return (s.str());
+}
+
+
 void
 void
 Host::setIdentifier(const uint8_t* identifier, const size_t len,
 Host::setIdentifier(const uint8_t* identifier, const size_t len,
                     const IdentifierType& type) {
                     const IdentifierType& type) {

+ 10 - 0
src/lib/dhcpsrv/host.h

@@ -307,6 +307,16 @@ public:
     /// @return text form of the identifier, including (duid= or mac=).
     /// @return text form of the identifier, including (duid= or mac=).
     std::string getIdentifierAsText() const;
     std::string getIdentifierAsText() const;
 
 
+    /// @brief Returns host identifier in textual form.
+    ///
+    /// @param type Identifier type.
+    /// @param value Pointer to a buffer holding identifier.
+    /// @param length Length of the identifier.
+    /// @return Identifier in the form of <type>=<value>.
+    static std::string getIdentifierAsText(const IdentifierType& type,
+                                           const uint8_t* value,
+                                           const size_t length);
+
     /// @brief Sets new IPv4 subnet identifier.
     /// @brief Sets new IPv4 subnet identifier.
     ///
     ///
     /// @param ipv4_subnet_id New subnet identifier.
     /// @param ipv4_subnet_id New subnet identifier.

+ 46 - 1
src/lib/dhcpsrv/host_mgr.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -77,6 +77,23 @@ HostMgr::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
 }
 }
 
 
 ConstHostCollection
 ConstHostCollection
+HostMgr::getAll(const Host::IdentifierType& identifier_type,
+                const uint8_t* identifier_begin,
+                const size_t identifier_len) const {
+    ConstHostCollection hosts = getCfgHosts()->getAll(identifier_type,
+                                                      identifier_begin,
+                                                      identifier_len);
+    if (alternate_source_) {
+        ConstHostCollection hosts_plus =
+            alternate_source_->getAll(identifier_type, identifier_begin,
+                                      identifier_len);
+        hosts.insert(hosts.end(), hosts_plus.begin(), hosts_plus.end());
+    }
+    return (hosts);
+}
+
+
+ConstHostCollection
 HostMgr::getAll4(const IOAddress& address) const {
 HostMgr::getAll4(const IOAddress& address) const {
     ConstHostCollection hosts = getCfgHosts()->getAll4(address);
     ConstHostCollection hosts = getCfgHosts()->getAll4(address);
     if (alternate_source_) {
     if (alternate_source_) {
@@ -108,6 +125,20 @@ HostMgr::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
 
 
 ConstHostPtr
 ConstHostPtr
 HostMgr::get4(const SubnetID& subnet_id,
 HostMgr::get4(const SubnetID& subnet_id,
+              const Host::IdentifierType& identifier_type,
+              const uint8_t* identifier_begin,
+              const size_t identifier_len) const {
+    ConstHostPtr host = getCfgHosts()->get4(subnet_id, identifier_type,
+                                            identifier_begin, identifier_len);
+    if (!host && alternate_source_) {
+        host = alternate_source_->get4(subnet_id, identifier_type,
+                                       identifier_begin, identifier_len);
+    }
+    return (host);
+}
+
+ConstHostPtr
+HostMgr::get4(const SubnetID& subnet_id,
               const asiolink::IOAddress& address) const {
               const asiolink::IOAddress& address) const {
     ConstHostPtr host = getCfgHosts()->get4(subnet_id, address);
     ConstHostPtr host = getCfgHosts()->get4(subnet_id, address);
     if (!host && alternate_source_) {
     if (!host && alternate_source_) {
@@ -156,6 +187,20 @@ HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
 
 
 ConstHostPtr
 ConstHostPtr
 HostMgr::get6(const SubnetID& subnet_id,
 HostMgr::get6(const SubnetID& subnet_id,
+              const Host::IdentifierType& identifier_type,
+              const uint8_t* identifier_begin,
+              const size_t identifier_len) const {
+    ConstHostPtr host = getCfgHosts()->get6(subnet_id, identifier_type,
+                                            identifier_begin, identifier_len);
+    if (!host && alternate_source_) {
+        host = alternate_source_->get6(subnet_id, identifier_type,
+                                       identifier_begin, identifier_len);
+    }
+    return (host);
+}
+
+ConstHostPtr
+HostMgr::get6(const SubnetID& subnet_id,
               const asiolink::IOAddress& addr) const {
               const asiolink::IOAddress& addr) const {
     ConstHostPtr host = getCfgHosts()->get6(subnet_id, addr);
     ConstHostPtr host = getCfgHosts()->get6(subnet_id, addr);
     if (!host && alternate_source_) {
     if (!host && alternate_source_) {

+ 64 - 3
src/lib/dhcpsrv/host_mgr.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -88,7 +88,9 @@ public:
     ///
     ///
     /// It retrieves reservations from both primary and alternate host data
     /// It retrieves reservations from both primary and alternate host data
     /// source as a single collection of @c Host objects, i.e. if matching
     /// source as a single collection of @c Host objects, i.e. if matching
-    /// reservations are in both sources, all of them are returned.
+    /// reservations are in both sources, all of them are returned. The
+    /// reservations from the primary data source are placed before the
+    /// reservations from the alternate source.
     ///
     ///
     /// Note that returned collection may contain duplicates. It is the
     /// Note that returned collection may contain duplicates. It is the
     /// caller's responsibility to check for duplicates.
     /// caller's responsibility to check for duplicates.
@@ -101,13 +103,38 @@ public:
     virtual ConstHostCollection
     virtual ConstHostCollection
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const;
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const;
 
 
+    /// @brief Return all hosts connected to any subnet for which reservations
+    /// have been made using a specified identifier.
+    ///
+    /// This method returns all @c Host objects representing reservations for
+    /// a specified identifier as documented in the
+    /// @c BaseHostDataSource::getAll.
+    ///
+    /// It retrieves reservations from both primary and alternate host data
+    /// source as a single collection of @c Host objects, i.e. if matching
+    /// reservations are in both sources, all of them are returned. The
+    /// reservations from the primary data source are placed before the
+    /// reservations from the alternate source.
+    ///
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll(const Host::IdentifierType& identifier_type,
+           const uint8_t* identifier_begin,
+           const size_t identifier_len) const;
+
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     ///
     ///
     /// This method may return multiple @c Host objects if they are connected to
     /// This method may return multiple @c Host objects if they are connected to
     /// different subnets.
     /// different subnets.
     ///
     ///
     /// If matching reservations are both in the primary and the alternate
     /// If matching reservations are both in the primary and the alternate
-    /// data source, all of them are returned.
+    /// data source, all of them are returned. The reservations from the
+    /// primary data source are placed before the reservations from the
+    /// alternate source.
     ///
     ///
     /// @param address IPv4 address for which the @c Host object is searched.
     /// @param address IPv4 address for which the @c Host object is searched.
     ///
     ///
@@ -131,6 +158,23 @@ public:
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
          const DuidPtr& duid = DuidPtr()) const;
          const DuidPtr& duid = DuidPtr()) const;
 
 
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// This method returns a single reservation for a particular host as
+    /// documneted in the @c BaseHostDataSource::get4.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// a reservation for a specified IPv4 address.
     /// a reservation for a specified IPv4 address.
     ///
     ///
@@ -161,6 +205,23 @@ public:
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
          const HWAddrPtr& hwaddr = HWAddrPtr()) const;
          const HWAddrPtr& hwaddr = HWAddrPtr()) const;
 
 
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// This method returns a host connected to the IPv6 subnet as described
+    /// in the @c BaseHostDataSource::get6.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
     /// @brief Returns a host using the specified IPv6 prefix.
     /// @brief Returns a host using the specified IPv6 prefix.
     ///
     ///
     /// This method returns a host using specified IPv6 prefix, as described
     /// This method returns a host using specified IPv6 prefix, as described

+ 25 - 11
src/lib/dhcpsrv/hosts_messages.mes

@@ -1,4 +1,4 @@
-# Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2015-2016 Internet Systems Consortium, Inc. ("ISC")
 #
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # 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
 # License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -76,10 +76,24 @@ subnet id and address. The arguments specify subnet id, address and
 found host details respectively.
 found host details respectively.
 
 
 % HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST using subnet id %1 and address %2, found host: %3
 % HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST using subnet id %1 and address %2, found host: %3
-This debug message include the details of the host found using the
+This debug message includes the details of the host found using the
 subnet id and address. The arguments specify subnet id, address and
 subnet id and address. The arguments specify subnet id, address and
 found host details respectively.
 found host details respectively.
 
 
+% HOSTS_CFG_GET_ONE_PREFIX get one host with reservation for prefix %1/%2
+This debug message is issued when starting to retrieve a host having a
+reservation for a specified prefix. The arguments specify a prefix and
+prefix length.
+
+% HOSTS_CFG_GET_ONE_PREFIX_HOST using prefix %1/%2, found host: %3
+This debug message includes the details of the host found using the
+specific prefix/prefix length. The arguments specify prefix, prefix
+length and host details respectively.
+
+% HOSTS_CFG_GET_ONE_PREFIX_NULL host not found using prefix %1/%2
+This debug messsage is issued when no host was found for a specified
+prefix and prefix length.
+
 % HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4 get one host with reservation for subnet id %1 and IPv4 address %2
 % HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4 get one host with reservation for subnet id %1 and IPv4 address %2
 This debug message is issued when starting to retrieve a host connected to the
 This debug message is issued when starting to retrieve a host connected to the
 specific subnet and having the specific IPv4 address reserved. The
 specific subnet and having the specific IPv4 address reserved. The
@@ -106,19 +120,19 @@ subnet id and IPv6 address.
 This debug message is issued when no host was found using the specified
 This debug message is issued when no host was found using the specified
 subnet if and IPv6 address.
 subnet if and IPv6 address.
 
 
-% HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID get one host with %1 reservation for subnet id %2, HWADDR %3, DUID %4
-This debug message is issued when starting to retrieve the host holding IPv4 or
-IPv6 reservations, which is connected to the specific subnet and is
-identified by the specific HW address or DUID. The first argument
+% HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER get one host with %1 reservation for subnet id %2, identified by %3
+This debug message is issued when starting to retrieve a host holding
+IPv4 or IPv6 reservations, which is connected to a specific subnet and
+is identified by a specific unique identifier. The first argument
 identifies if the IPv4 or IPv6 reservation is desired.
 identifies if the IPv4 or IPv6 reservation is desired.
 
 
-% HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID_HOST using subnet id %1, HWADDR %2 and DUID %3, found host: %4
-This debug message includes the details of the host found using the
-subnet id, HW address and/or DUID.
+% HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_HOST using subnet id %1 and identifier %2, found host: %3
+This debug message includes the details of a host found using a
+subnet id and specific host identifier.
 
 
-% HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID_NULL host not found using subnet id %1, HW address %2 and DUID %3
+% HOSTS_CFG_GET_ONE_SUBNET_ID_IDENTIFIER_NULL host not found using subnet id %1 and identifier %2
 This debug message is issued when no host was found using the specified
 This debug message is issued when no host was found using the specified
-subnet id, HW address and DUID.
+subnet id and host identifier.
 
 
 % HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_ADDRESS4 trying alternate source for host using subnet id %1 and address %2
 % HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_ADDRESS4 trying alternate source for host using subnet id %1 and address %2
 This debug message is issued when the Host Manager doesn't find the
 This debug message is issued when the Host Manager doesn't find the

+ 2 - 2
src/lib/dhcpsrv/mysql_connection.h

@@ -36,8 +36,8 @@ extern const int MLM_MYSQL_FETCH_FAILURE;
 
 
 /// @name Current database schema version values.
 /// @name Current database schema version values.
 //@{
 //@{
-const uint32_t CURRENT_VERSION_VERSION = 3;
-const uint32_t CURRENT_VERSION_MINOR = 0;
+const uint32_t CURRENT_VERSION_VERSION = 4;
+const uint32_t CURRENT_VERSION_MINOR = 2;
 
 
 //@}
 //@}
 
 

+ 146 - 160
src/lib/dhcpsrv/mysql_host_data_source.cc

@@ -64,7 +64,7 @@ TaggedStatement tagged_statements[] = {
     // having a specified identifier will be returned from those subnets.
     // having a specified identifier will be returned from those subnets.
     // Because LEFT JOIN clause is used, the number of rows returned for
     // Because LEFT JOIN clause is used, the number of rows returned for
     // a single host depends on the number of reservations.
     // a single host depends on the number of reservations.
-    {MySqlHostDataSource::GET_HOST_HWADDR_DUID,
+    {MySqlHostDataSource::GET_HOST_DHCPID,
             "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
             "SELECT h.host_id, h.dhcp_identifier, h.dhcp_identifier_type, "
                 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
                 "h.dhcp4_subnet_id, h.dhcp6_subnet_id, h.ipv4_address, "
                 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
                 "h.hostname, h.dhcp4_client_classes, h.dhcp6_client_classes, "
@@ -718,6 +718,8 @@ public:
         : MySqlHostExchange(), reserv_type_(0), reserv_type_null_(MLM_FALSE),
         : MySqlHostExchange(), reserv_type_(0), reserv_type_null_(MLM_FALSE),
           ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0) {
           ipv6_address_buffer_len_(0), prefix_len_(0), iaid_(0) {
 
 
+        memset(ipv6_address_buffer_, 0, sizeof(ipv6_address_buffer_));
+
         // Append additional columns returned by the queries.
         // Append additional columns returned by the queries.
         columns_.push_back("address");
         columns_.push_back("address");
         columns_.push_back("prefix_len");
         columns_.push_back("prefix_len");
@@ -922,7 +924,7 @@ public:
     ///
     ///
     /// Initialize class members representing a single IPv6 reservation.
     /// Initialize class members representing a single IPv6 reservation.
     MySqlIPv6ReservationExchange()
     MySqlIPv6ReservationExchange()
-        : host_id_(0), address_("::"), prefix_len_(0), type_(0),
+        : host_id_(0), address_("::"), address_len_(0), prefix_len_(0), type_(0),
           iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
           iaid_(0), resv_(IPv6Resrv::TYPE_NA, asiolink::IOAddress("::"), 128) {
 
 
         // Reset error table.
         // Reset error table.
@@ -1117,6 +1119,29 @@ public:
                            boost::shared_ptr<MySqlHostExchange> exchange,
                            boost::shared_ptr<MySqlHostExchange> exchange,
                            ConstHostCollection& result, bool single) const;
                            ConstHostCollection& result, bool single) const;
 
 
+    /// @brief Retrieves a host by subnet and client's unique identifier.
+    ///
+    /// This method is used by both MySqlHostDataSource::get4 and
+    /// MySqlHOstDataSource::get6 methods.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    /// @param stindex Statement index.
+    /// @param exchange Pointer to the exchange object used for the
+    /// particular query.
+    ///
+    /// @return Pointer to const instance of Host or null pointer if
+    /// no host found.
+    ConstHostPtr getHost(const SubnetID& subnet_id,
+                         const Host::IdentifierType& identifier_type,
+                         const uint8_t* identifier_begin,
+                         const size_t identifier_len,
+                         MySqlHostDataSource::StatementIndex stindex,
+                         boost::shared_ptr<MySqlHostExchange> exchange) const;
+
     /// @brief Pointer to the object representing an exchange which
     /// @brief Pointer to the object representing an exchange which
     /// can be used to retrieve DHCPv4 reservation.
     /// can be used to retrieve DHCPv4 reservation.
     boost::shared_ptr<MySqlHostExchange> host_exchange_;
     boost::shared_ptr<MySqlHostExchange> host_exchange_;
@@ -1276,6 +1301,50 @@ getHostCollection(MySqlHostDataSource::StatementIndex stindex,
     }
     }
 }
 }
 
 
+ConstHostPtr
+MySqlHostDataSourceImpl::
+getHost(const SubnetID& subnet_id,
+        const Host::IdentifierType& identifier_type,
+        const uint8_t* identifier_begin,
+        const size_t identifier_len,
+        MySqlHostDataSource::StatementIndex stindex,
+        boost::shared_ptr<MySqlHostExchange> exchange) const {
+
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[3];
+    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;
+
+    // Identifier value.
+    std::vector<char> identifier_vec(identifier_begin,
+                                     identifier_begin + identifier_len);
+    unsigned long length = identifier_vec.size();
+    inbind[2].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[2].buffer = &identifier_vec[0];
+    inbind[2].buffer_length = length;
+    inbind[2].length = &length;
+
+    // Identifier type.
+    char identifier_type_copy = static_cast<char>(identifier_type);
+    inbind[1].buffer_type = MYSQL_TYPE_TINY;
+    inbind[1].buffer = reinterpret_cast<char*>(&identifier_type_copy);
+    inbind[1].is_unsigned = MLM_TRUE;
+
+    ConstHostCollection collection;
+    getHostCollection(stindex, inbind, exchange, collection, true);
+
+    // Return single record if present, else clear the host.
+    ConstHostPtr result;
+    if (!collection.empty())
+        result = *collection.begin();
+
+    return (result);
+}
+
 
 
 MySqlHostDataSource::
 MySqlHostDataSource::
 MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
 MySqlHostDataSource(const MySqlConnection::ParameterMap& parameters)
@@ -1286,86 +1355,72 @@ MySqlHostDataSource::~MySqlHostDataSource() {
     delete impl_;
     delete impl_;
 }
 }
 
 
-bool
-MySqlHostDataSource::checkIfExists(const HostPtr& host){
-    /// @todo: Implement this as a single query get(identifier_type, identifier)
-    return (get4(host->getIPv4SubnetID(), host->getHWAddress(), host->getDuid()) ||
-            get6(host->getIPv6SubnetID(), host->getDuid(), host->getHWAddress()));
-}
-
 void
 void
 MySqlHostDataSource::add(const HostPtr& host) {
 MySqlHostDataSource::add(const HostPtr& host) {
-    // Check if the host is not a duplicate
-    if (checkIfExists(host)){
-        isc_throw(DuplicateEntry, "Host with same parameters already exists.");
-
-    } else {
-        // Create the MYSQL_BIND array for the host
-        std::vector<MYSQL_BIND> bind = impl_->host_exchange_->createBindForSend(host);
+    // Create the MYSQL_BIND array for the host
+    std::vector<MYSQL_BIND> bind = impl_->host_exchange_->createBindForSend(host);
 
 
-        // ... and call addHost() code.
-        impl_->addQuery(INSERT_HOST, bind);
+    // ... and call addHost() code.
+    impl_->addQuery(INSERT_HOST, bind);
 
 
-        IPv6ResrvRange v6resv = host->getIPv6Reservations();
-        if (std::distance(v6resv.first, v6resv.second) == 0) {
-            // If there are no v6 reservations, we're done here.
-            return;
-        }
+    IPv6ResrvRange v6resv = host->getIPv6Reservations();
+    if (std::distance(v6resv.first, v6resv.second) == 0) {
+        // If there are no v6 reservations, we're done here.
+        return;
+    }
 
 
-        // Gets the last inserted hosts id
-        uint64_t host_id = mysql_insert_id(impl_->conn_.mysql_);
-        for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second; ++resv) {
-            impl_->addResv(resv->second, host_id);
-        }
+    // Gets the last inserted hosts id
+    uint64_t host_id = mysql_insert_id(impl_->conn_.mysql_);
+    for (IPv6ResrvIterator resv = v6resv.first; resv != v6resv.second;
+         ++resv) {
+        impl_->addResv(resv->second, host_id);
     }
     }
 }
 }
 
 
 ConstHostCollection
 ConstHostCollection
-MySqlHostDataSource::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
+MySqlHostDataSource::getAll(const HWAddrPtr& hwaddr,
+                            const DuidPtr& duid) const {
 
 
-    // Set up the WHERE clause value
-    MYSQL_BIND inbind[2];
-    memset(inbind, 0, sizeof(inbind));
-
-    uint8_t dhcp_identifier_type = 0;
-    long unsigned int length = 0;
     if (duid){
     if (duid){
-        // DUID
-        // set proper dhcp_identifier_type
-        dhcp_identifier_type = BaseHostDataSource::ID_DUID; // 1
-        inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type);
-
-        const vector<uint8_t>& duid_vector = duid->getDuid();
-        length = duid_vector.size();
-        inbind[0].buffer_type = MYSQL_TYPE_BLOB;
-        inbind[0].buffer = reinterpret_cast<char*>
-            (const_cast<uint8_t*>(&duid_vector[0]));
-        inbind[0].buffer_length = length;
-        inbind[0].length = &length;
+        return (getAll(Host::IDENT_DUID, &duid->getDuid()[0],
+                       duid->getDuid().size()));
+
     } else if (hwaddr) {
     } else if (hwaddr) {
-        // HW Address
-        dhcp_identifier_type = BaseHostDataSource::ID_HWADDR; // 0
-        inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type);
-
-        const vector<uint8_t>& hwaddr_vector = hwaddr->hwaddr_;
-        length = hwaddr_vector.size();
-        inbind[0].buffer_type = MYSQL_TYPE_BLOB;
-        inbind[0].buffer = reinterpret_cast<char*>
-            (const_cast<uint8_t*>(&hwaddr_vector[0]));
-        inbind[0].buffer_length = length;
-        inbind[0].length = &length;
+        return (getAll(Host::IDENT_HWADDR,
+                       &hwaddr->hwaddr_[0],
+                       hwaddr->hwaddr_.size()));
     }
     }
 
 
-    // dhcp identifier type
+    return (ConstHostCollection());
+}
+
+ConstHostCollection
+MySqlHostDataSource::getAll(const Host::IdentifierType& identifier_type,
+                            const uint8_t* identifier_begin,
+                            const size_t identifier_len) const {
+    // Set up the WHERE clause value
+    MYSQL_BIND inbind[2];
+    memset(inbind, 0, sizeof(inbind));
+
+    // Identifier type.
+    char identifier_type_copy = static_cast<char>(identifier_type);
+    inbind[1].buffer = &identifier_type_copy;
     inbind[1].buffer_type = MYSQL_TYPE_TINY;
     inbind[1].buffer_type = MYSQL_TYPE_TINY;
-    inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type);
     inbind[1].is_unsigned = MLM_TRUE;
     inbind[1].is_unsigned = MLM_TRUE;
 
 
+    // Identifier value.
+    std::vector<char> identifier_vec(identifier_begin,
+                                     identifier_begin + identifier_len);
+    unsigned long int length = identifier_vec.size();
+    inbind[0].buffer_type = MYSQL_TYPE_BLOB;
+    inbind[0].buffer = &identifier_vec[0];
+    inbind[0].buffer_length = length;
+    inbind[0].length = &length;
+
     ConstHostCollection result;
     ConstHostCollection result;
-    impl_->getHostCollection(GET_HOST_HWADDR_DUID, inbind,
+    impl_->getHostCollection(GET_HOST_DHCPID, inbind,
                              impl_->host_ipv6_exchange_,
                              impl_->host_ipv6_exchange_,
                              result, false);
                              result, false);
-
     return (result);
     return (result);
 }
 }
 
 
@@ -1392,15 +1447,6 @@ ConstHostPtr
 MySqlHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
 MySqlHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
                           const DuidPtr& duid) const {
                           const DuidPtr& duid) const {
 
 
-    // Set up the WHERE clause value
-    MYSQL_BIND inbind[3];
-    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;
-
     /// @todo: Rethink the logic in BaseHostDataSource::get4(subnet, hwaddr, duid)
     /// @todo: Rethink the logic in BaseHostDataSource::get4(subnet, hwaddr, duid)
     if (hwaddr && duid) {
     if (hwaddr && duid) {
         isc_throw(BadValue, "MySQL host data source get4() called with both"
         isc_throw(BadValue, "MySQL host data source get4() called with both"
@@ -1411,53 +1457,28 @@ MySqlHostDataSource::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
                   "neither hwaddr or duid specified, one of them is required");
                   "neither hwaddr or duid specified, one of them is required");
     }
     }
 
 
-    unsigned long length = 0;
-    uint8_t dhcp_identifier_type_ = 0;
-
     // Choosing one of the identifiers
     // Choosing one of the identifiers
     if (hwaddr) {
     if (hwaddr) {
-        // set identifier type
-        dhcp_identifier_type_ = BaseHostDataSource::ID_HWADDR; // 0
-
-        // set identifier value
-        const vector<uint8_t>& hwaddr_vector = hwaddr->hwaddr_;
-        length = hwaddr_vector.size();
-        inbind[2].buffer_type = MYSQL_TYPE_BLOB;
-        inbind[2].buffer = reinterpret_cast<char*>
-            (const_cast<uint8_t*>(&hwaddr_vector[0]));
-        inbind[2].buffer_length = length;
-        inbind[2].length = &length;
+        return (get4(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
+                     hwaddr->hwaddr_.size()));
 
 
     } else if (duid) {
     } else if (duid) {
-        // set identifier type
-        dhcp_identifier_type_ = BaseHostDataSource::ID_DUID; // 1
-
-        // set identifier value
-        const vector<uint8_t>& duid_vector = duid->getDuid();
-        length = duid_vector.size();
-        inbind[2].buffer_type = MYSQL_TYPE_BLOB;
-        inbind[2].buffer = reinterpret_cast<char*>
-            (const_cast<uint8_t*>(&duid_vector[0]));
-        inbind[2].buffer_length = length;
-        inbind[2].length = &length;
+        return (get4(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
+                     duid->getDuid().size()));
     }
     }
 
 
-    // dhcp identifier type
-    inbind[1].buffer_type = MYSQL_TYPE_TINY;
-    inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
-    inbind[1].is_unsigned = MLM_TRUE;
-
-    ConstHostCollection collection;
-    impl_->getHostCollection(GET_HOST_SUBID4_DHCPID, inbind,
-                             impl_->host_exchange_, collection,
-                             true);
+    return (ConstHostPtr());
+}
 
 
-    // Return single record if present, else clear the host.
-    ConstHostPtr result;
-    if (!collection.empty())
-        result = *collection.begin();
+ConstHostPtr
+MySqlHostDataSource::get4(const SubnetID& subnet_id,
+                          const Host::IdentifierType& identifier_type,
+                          const uint8_t* identifier_begin,
+                          const size_t identifier_len) const {
 
 
-    return (result);
+    return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
+                   identifier_len, GET_HOST_SUBID4_DHCPID,
+                   impl_->host_exchange_));
 }
 }
 
 
 ConstHostPtr
 ConstHostPtr
@@ -1493,15 +1514,6 @@ ConstHostPtr
 MySqlHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
 MySqlHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                           const HWAddrPtr& hwaddr) const {
                           const HWAddrPtr& hwaddr) const {
 
 
-    // Set up the WHERE clause value
-    MYSQL_BIND inbind[3];
-    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;
-
     /// @todo: Rethink the logic in BaseHostDataSource::get6(subnet, hwaddr, duid)
     /// @todo: Rethink the logic in BaseHostDataSource::get6(subnet, hwaddr, duid)
     if (hwaddr && duid) {
     if (hwaddr && duid) {
         isc_throw(BadValue, "MySQL host data source get6() called with both"
         isc_throw(BadValue, "MySQL host data source get6() called with both"
@@ -1512,53 +1524,27 @@ MySqlHostDataSource::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                   "neither hwaddr or duid specified, one of them is required");
                   "neither hwaddr or duid specified, one of them is required");
     }
     }
 
 
-    unsigned long length = 0;
-    uint8_t dhcp_identifier_type_ = 0;
-
     // Choosing one of the identifiers
     // Choosing one of the identifiers
     if (hwaddr) {
     if (hwaddr) {
-        // set identifier type
-        dhcp_identifier_type_ = BaseHostDataSource::ID_HWADDR; // 0
-
-        // set identifier value
-        const vector<uint8_t>& hwaddr_vector = hwaddr->hwaddr_;
-        length = hwaddr_vector.size();
-        inbind[2].buffer_type = MYSQL_TYPE_BLOB;
-        inbind[2].buffer = reinterpret_cast<char*>
-            (const_cast<uint8_t*>(&hwaddr_vector[0]));
-        inbind[2].buffer_length = length;
-        inbind[2].length = &length;
+        return (get6(subnet_id, Host::IDENT_HWADDR, &hwaddr->hwaddr_[0],
+                     hwaddr->hwaddr_.size()));
     } else if (duid) {
     } else if (duid) {
-        // set identifier type
-        dhcp_identifier_type_ = BaseHostDataSource::ID_DUID; // 1
-
-        // set identifier value
-        const vector<uint8_t>& duid_vector = duid->getDuid();
-        length = duid_vector.size();
-        inbind[2].buffer_type = MYSQL_TYPE_BLOB;
-        inbind[2].buffer = reinterpret_cast<char*>
-            (const_cast<uint8_t*>(&duid_vector[0]));
-        inbind[2].buffer_length = length;
-        inbind[2].length = &length;
+        return (get6(subnet_id, Host::IDENT_DUID, &duid->getDuid()[0],
+                     duid->getDuid().size()));
     }
     }
 
 
-    // dhcp identifier type
-    inbind[1].buffer_type = MYSQL_TYPE_TINY;
-    inbind[1].buffer = reinterpret_cast<char*>(&dhcp_identifier_type_);
-    inbind[1].is_unsigned = MLM_TRUE;
-
-    ConstHostCollection collection;
-    impl_->getHostCollection(GET_HOST_SUBID6_DHCPID, inbind,
-                             impl_->host_ipv6_exchange_,
-                             collection, true);
-
-    // Return single record if present, else clear the host.
-    ConstHostPtr result;
-    if (!collection.empty()) {
-        result = *collection.begin();
-    }
+    return (ConstHostPtr());
+}
 
 
-    return (result);
+ConstHostPtr
+MySqlHostDataSource::get6(const SubnetID& subnet_id,
+                          const Host::IdentifierType& identifier_type,
+                          const uint8_t* identifier_begin,
+                          const size_t identifier_len) const {
+
+    return (impl_->getHost(subnet_id, identifier_type, identifier_begin,
+                   identifier_len, GET_HOST_SUBID6_DHCPID,
+                   impl_->host_ipv6_exchange_));
 }
 }
 
 
 ConstHostPtr
 ConstHostPtr

+ 45 - 6
src/lib/dhcpsrv/mysql_host_data_source.h

@@ -77,6 +77,22 @@ public:
     virtual ConstHostCollection
     virtual ConstHostCollection
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const;
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const;
 
 
+    /// @brief Return all hosts connected to any subnet for which reservations
+    /// have been made using a specified identifier.
+    ///
+    /// This method returns all @c Host objects which represent reservations
+    /// for a specified identifier. This method may return multiple hosts
+    /// because a particular client may have reservations in multiple subnets.
+    ///
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Collection of const @c Host objects.
+    virtual ConstHostCollection
+    getAll(const Host::IdentifierType& identifier_type,
+           const uint8_t* identifier_begin, const size_t identifier_len) const;
+
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     ///
     ///
     /// This method may return multiple @c Host objects if they are connected
     /// This method may return multiple @c Host objects if they are connected
@@ -106,6 +122,20 @@ public:
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
          const DuidPtr& duid = DuidPtr()) const;
          const DuidPtr& duid = DuidPtr()) const;
 
 
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// @brief Returns a host connected to the IPv4 subnet and having
     /// a reservation for a specified IPv4 address.
     /// a reservation for a specified IPv4 address.
     ///
     ///
@@ -143,6 +173,20 @@ public:
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
             const HWAddrPtr& hwaddr = HWAddrPtr()) const;
             const HWAddrPtr& hwaddr = HWAddrPtr()) const;
 
 
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Const @c Host object for which reservation has been made using
+    /// the specified identifier.
+    virtual ConstHostPtr
+    get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) const;
+
     /// @brief Returns a host using the specified IPv6 prefix.
     /// @brief Returns a host using the specified IPv6 prefix.
     ///
     ///
     /// @param prefix IPv6 prefix for which the @c Host object is searched.
     /// @param prefix IPv6 prefix for which the @c Host object is searched.
@@ -213,7 +257,7 @@ public:
     enum StatementIndex {
     enum StatementIndex {
         INSERT_HOST,            // Insert new host to collection
         INSERT_HOST,            // Insert new host to collection
         INSERT_V6_RESRV,        // Insert v6 reservation
         INSERT_V6_RESRV,        // Insert v6 reservation
-        GET_HOST_HWADDR_DUID,   // Gets hosts by DUID and/or HW address
+        GET_HOST_DHCPID,        // Gets hosts by host identifier
         GET_HOST_ADDR,          // Gets hosts by IPv4 address
         GET_HOST_ADDR,          // Gets hosts by IPv4 address
         GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
         GET_HOST_SUBID4_DHCPID, // Gets host by IPv4 SubnetID, HW address/DUID
         GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
         GET_HOST_SUBID6_DHCPID, // Gets host by IPv6 SubnetID, HW address/DUID
@@ -225,11 +269,6 @@ public:
 
 
 private:
 private:
 
 
-    /// @brief Checks if the specified host already exists in the database.
-    ///
-    /// @param host Pointer to the new @c Host object being added.
-    bool checkIfExists(const HostPtr& host);
-
     /// @brief Pointer to the implementation of the @ref MySqlHostDataSource.
     /// @brief Pointer to the implementation of the @ref MySqlHostDataSource.
     MySqlHostDataSourceImpl* impl_; 
     MySqlHostDataSourceImpl* impl_; 
 };
 };

+ 40 - 47
src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -117,17 +117,19 @@ TEST_F(CfgHostsTest, getAllNonRepeatingHosts) {
     // Try to retrieve each added reservation using HW address and DUID. Do it
     // Try to retrieve each added reservation using HW address and DUID. Do it
     // in the reverse order to make sure that the order doesn't matter.
     // in the reverse order to make sure that the order doesn't matter.
     for (int i = 24; i >= 0; --i) {
     for (int i = 24; i >= 0; --i) {
-        // Get host identified by HW address. The DUID is non-zero but it
-        // points to a host for which the reservation hasn't been added.
-        HostCollection hosts = cfg.getAll(hwaddrs_[i], duids_[i + 25]);
+        // Get host identified by HW address.
+        HostCollection hosts = cfg.getAll(Host::IDENT_HWADDR,
+                                          &hwaddrs_[i]->hwaddr_[0],
+                                          hwaddrs_[i]->hwaddr_.size());
         ASSERT_EQ(1, hosts.size());
         ASSERT_EQ(1, hosts.size());
         EXPECT_EQ(i % 10 + 1, hosts[0]->getIPv4SubnetID());
         EXPECT_EQ(i % 10 + 1, hosts[0]->getIPv4SubnetID());
         EXPECT_EQ(addressesa_[i].toText(),
         EXPECT_EQ(addressesa_[i].toText(),
                   hosts[0]->getIPv4Reservation().toText());
                   hosts[0]->getIPv4Reservation().toText());
 
 
-        // Get host identified by DUID. The HW address is non-null but it
-        // points to a host for which the reservation hasn't been added.
-        hosts = cfg.getAll(hwaddrs_[i + 25], duids_[i]);
+        // Get host identified by DUID.
+        hosts = cfg.getAll(Host::IDENT_DUID,
+                           &duids_[i]->getDuid()[0],
+                           duids_[i]->getDuid().size());
         ASSERT_EQ(1, hosts.size());
         ASSERT_EQ(1, hosts.size());
         EXPECT_EQ(i % 5 + 1, hosts[0]->getIPv4SubnetID());
         EXPECT_EQ(i % 5 + 1, hosts[0]->getIPv4SubnetID());
         EXPECT_EQ(addressesb_[i].toText(),
         EXPECT_EQ(addressesb_[i].toText(),
@@ -137,8 +139,10 @@ TEST_F(CfgHostsTest, getAllNonRepeatingHosts) {
     // Make sure that the reservations do not exist for the hardware addresses
     // Make sure that the reservations do not exist for the hardware addresses
     // and DUIDs from the range of 25 to 49.
     // and DUIDs from the range of 25 to 49.
     for (int i = 49; i >= 25; --i) {
     for (int i = 49; i >= 25; --i) {
-        EXPECT_TRUE(cfg.getAll(hwaddrs_[i]).empty());
-        EXPECT_TRUE(cfg.getAll(HWAddrPtr(), duids_[i]).empty());
+        EXPECT_TRUE(cfg.getAll(Host::IDENT_HWADDR, &hwaddrs_[i]->hwaddr_[0],
+                               hwaddrs_[i]->hwaddr_.size()).empty());
+        EXPECT_TRUE(cfg.getAll(Host::IDENT_DUID, &duids_[i]->getDuid()[0],
+                               duids_[i]->getDuid().size()).empty());
     }
     }
 }
 }
 
 
@@ -244,31 +248,24 @@ TEST_F(CfgHostsTest, get4) {
     }
     }
 
 
     for (unsigned i = 0; i < 25; ++i) {
     for (unsigned i = 0; i < 25; ++i) {
-        // Retrieve host by HW address. The DUID is non-null but there is no
-        // reservation made for the DUID so the reservation is returned for
-        // HW address.
-        HostPtr host = cfg.get4(SubnetID(1 + i % 2), hwaddrs_[i],
-                                duids_[i + 25]);
+        // Retrieve host by HW address.
+        HostPtr host = cfg.get4(SubnetID(1 + i % 2), Host::IDENT_HWADDR,
+                                &hwaddrs_[i]->hwaddr_[0],
+                                hwaddrs_[i]->hwaddr_.size());
         ASSERT_TRUE(host);
         ASSERT_TRUE(host);
         EXPECT_EQ(1 + i % 2, host->getIPv4SubnetID());
         EXPECT_EQ(1 + i % 2, host->getIPv4SubnetID());
         EXPECT_EQ(increase(IOAddress("192.0.2.5"), i),
         EXPECT_EQ(increase(IOAddress("192.0.2.5"), i),
                   host->getIPv4Reservation());
                   host->getIPv4Reservation());
 
 
-        // Retrieve host by DUID. The HW address is non-null but there is no
-        // reservation made for the HW address so the reservation is returned
-        // for the DUID.
-        host = cfg.get4(SubnetID(1 + i % 2), hwaddrs_[i + 25], duids_[i]);
+        // Retrieve host by DUID.
+        host = cfg.get4(SubnetID(1 + i % 2), Host::IDENT_DUID,
+                        &duids_[i]->getDuid()[0], duids_[i]->getDuid().size());
         ASSERT_TRUE(host);
         ASSERT_TRUE(host);
         EXPECT_EQ(1 + i % 2, host->getIPv4SubnetID());
         EXPECT_EQ(1 + i % 2, host->getIPv4SubnetID());
         EXPECT_EQ(increase(IOAddress("192.0.2.100"), i),
         EXPECT_EQ(increase(IOAddress("192.0.2.100"), i),
                   host->getIPv4Reservation());
                   host->getIPv4Reservation());
 
 
     }
     }
-
-    // Also check that when the get4 finds multiple Host objects that fulfil
-    // search criteria, it will throw an exception. Note that the reservation
-    // exist both for hwaddrs_[0] and duids_[0].
-    EXPECT_THROW(cfg.get4(SubnetID(1), hwaddrs_[0], duids_[0]), DuplicateHost);
 }
 }
 
 
 // This test checks that the reservations can be retrieved for the particular
 // This test checks that the reservations can be retrieved for the particular
@@ -298,11 +295,10 @@ TEST_F(CfgHostsTest, get6) {
     }
     }
 
 
     for (unsigned i = 0; i < 25; ++i) {
     for (unsigned i = 0; i < 25; ++i) {
-        // Retrieve host by HW address. The DUID is non-null but there is no
-        // reservation made for the DUID so the reservation is returned for
-        // HW address.
-        HostPtr host = cfg.get6(SubnetID(1 + i % 2), duids_[i + 25],
-                                hwaddrs_[i]);
+        // Retrieve host by HW address.
+        HostPtr host = cfg.get6(SubnetID(1 + i % 2), Host::IDENT_HWADDR,
+                                &hwaddrs_[i]->hwaddr_[0],
+                                hwaddrs_[i]->hwaddr_.size());
         ASSERT_TRUE(host);
         ASSERT_TRUE(host);
         EXPECT_EQ(1 + i % 2, host->getIPv6SubnetID());
         EXPECT_EQ(1 + i % 2, host->getIPv6SubnetID());
         IPv6ResrvRange reservations =
         IPv6ResrvRange reservations =
@@ -311,10 +307,9 @@ TEST_F(CfgHostsTest, get6) {
         EXPECT_EQ(increase(IOAddress("2001:db8:1::1"), i),
         EXPECT_EQ(increase(IOAddress("2001:db8:1::1"), i),
                   reservations.first->second.getPrefix());
                   reservations.first->second.getPrefix());
 
 
-        // Retrieve host by DUID. The HW address is non-null but there is no
-        // reservation made for the HW address so the reservation is returned
-        // for the DUID.
-        host = cfg.get6(SubnetID(1 + i % 2), duids_[i], hwaddrs_[i + 25]);
+        // Retrieve host by DUID.
+        host = cfg.get6(SubnetID(1 + i % 2), Host::IDENT_DUID,
+                        &duids_[i]->getDuid()[0], duids_[i]->getDuid().size());
         ASSERT_TRUE(host);
         ASSERT_TRUE(host);
         EXPECT_EQ(1 + i % 2, host->getIPv6SubnetID());
         EXPECT_EQ(1 + i % 2, host->getIPv6SubnetID());
         reservations = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
         reservations = host->getIPv6Reservations(IPv6Resrv::TYPE_NA);
@@ -322,11 +317,6 @@ TEST_F(CfgHostsTest, get6) {
         EXPECT_EQ(increase(IOAddress("2001:db8:2::1"), i),
         EXPECT_EQ(increase(IOAddress("2001:db8:2::1"), i),
                   reservations.first->second.getPrefix());
                   reservations.first->second.getPrefix());
     }
     }
-
-    // Also check that when the get6 finds multiple Host objects that fulfil
-    // search criteria, it will throw an exception. Note that the reservation
-    // exist both for hwaddrs_[0] and duids_[0].
-    EXPECT_THROW(cfg.get6(SubnetID(1), duids_[0], hwaddrs_[0]), DuplicateHost);
 }
 }
 
 
 // This test checks that the IPv6 reservations can be retrieved for a particular
 // This test checks that the IPv6 reservations can be retrieved for a particular
@@ -376,30 +366,31 @@ TEST_F(CfgHostsTest, get6MultipleAddrs) {
 
 
         // Generate 5 unique addresses for this host.
         // Generate 5 unique addresses for this host.
         for (int j = 0; j < 5; ++j) {
         for (int j = 0; j < 5; ++j) {
-            std::stringstream tmp;
-            tmp << "2001:db8:" << i << "::" << j;
-            host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA, tmp.str()));
+            std::stringstream address_stream;
+            address_stream << "2001:db8:" << i << "::" << j;
+            host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
+                                           address_stream.str()));
         }
         }
         cfg.add(host);
         cfg.add(host);
     }
     }
 
 
-    // We don't care about HW/MAC addresses for now.
-    HWAddrPtr hwaddr_not_used;
-
     // Now check if we can retrieve each of those 25 hosts by using each
     // Now check if we can retrieve each of those 25 hosts by using each
     // of their addresses.
     // of their addresses.
     for (unsigned i = 0; i < 25; ++i) {
     for (unsigned i = 0; i < 25; ++i) {
 
 
         // Check that the host is there.
         // Check that the host is there.
-        HostPtr by_duid = cfg.get6(SubnetID(1 + i % 2), duids_[i], hwaddr_not_used);
+        HostPtr by_duid = cfg.get6(SubnetID(1 + i % 2), Host::IDENT_DUID,
+                                   &duids_[i]->getDuid()[0],
+                                   duids_[i]->getDuid().size());
         ASSERT_TRUE(by_duid);
         ASSERT_TRUE(by_duid);
 
 
         for (unsigned j = 0; j < 5; ++j) {
         for (unsigned j = 0; j < 5; ++j) {
-            std::stringstream tmp;
-            tmp << "2001:db8:" << i << "::" << j;
+            std::stringstream address_stream;
+            address_stream << "2001:db8:" << i << "::" << j;
 
 
             // Retrieve host by (subnet-id,address).
             // Retrieve host by (subnet-id,address).
-            HostPtr by_addr = cfg.get6(SubnetID(1 + i % 2), tmp.str());
+            HostPtr by_addr = cfg.get6(SubnetID(1 + i % 2),
+                                       address_stream.str());
             ASSERT_TRUE(by_addr);
             ASSERT_TRUE(by_addr);
 
 
             // The pointers should match. Maybe we should compare contents
             // The pointers should match. Maybe we should compare contents
@@ -462,6 +453,8 @@ TEST_F(CfgHostsTest, add6Invalid2Hosts) {
     EXPECT_THROW(cfg.add(host2), isc::dhcp::DuplicateHost);
     EXPECT_THROW(cfg.add(host2), isc::dhcp::DuplicateHost);
 }
 }
 
 
+// Check that error is reported when trying to add a host with subnet
+// ids equal to zero.
 TEST_F(CfgHostsTest, zeroSubnetIDs) {
 TEST_F(CfgHostsTest, zeroSubnetIDs) {
     CfgHosts cfg;
     CfgHosts cfg;
     ASSERT_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
     ASSERT_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),

+ 118 - 53
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.cc

@@ -27,7 +27,7 @@ GenericHostDataSourceTest::~GenericHostDataSourceTest() {
 }
 }
 
 
 std::string
 std::string
-GenericHostDataSourceTest::generateHWAddr() {
+GenericHostDataSourceTest::generateHWAddr(const bool new_identifier) {
     /// @todo: Consider moving this somewhere to lib/testutils.
     /// @todo: Consider moving this somewhere to lib/testutils.
 
 
     // Let's use something that is easily printable. That's convenient
     // Let's use something that is easily printable. That's convenient
@@ -43,18 +43,20 @@ GenericHostDataSourceTest::generateHWAddr() {
             << static_cast<unsigned int>(hwaddr[i]);
             << static_cast<unsigned int>(hwaddr[i]);
     }
     }
 
 
-    // Increase the address for the next time we use it.
-    // This is primitive, but will work for 65k unique
-    // addresses.
-    hwaddr[sizeof(hwaddr) - 1]++;
-    if (hwaddr[sizeof(hwaddr) - 1] == 0) {
-        hwaddr[sizeof(hwaddr) - 2]++;
+    if (new_identifier) {
+        // Increase the address for the next time we use it.
+        // This is primitive, but will work for 65k unique
+        // addresses.
+        hwaddr[sizeof(hwaddr) - 1]++;
+        if (hwaddr[sizeof(hwaddr) - 1] == 0) {
+            hwaddr[sizeof(hwaddr) - 2]++;
+        }
     }
     }
     return (tmp.str());
     return (tmp.str());
 }
 }
 
 
 std::string
 std::string
-GenericHostDataSourceTest::generateDuid() {
+GenericHostDataSourceTest::generateDuid(const bool new_identifier) {
     /// @todo: Consider moving this somewhere to lib/testutils.
     /// @todo: Consider moving this somewhere to lib/testutils.
 
 
     // Let's use something that is easily printable. That's convenient
     // Let's use something that is easily printable. That's convenient
@@ -70,9 +72,11 @@ GenericHostDataSourceTest::generateDuid() {
     // Increase the DUID for the next time we use it.
     // Increase the DUID for the next time we use it.
     // This is primitive, but will work for 65k unique
     // This is primitive, but will work for 65k unique
     // DUIDs.
     // DUIDs.
-    duid[sizeof(duid) - 1]++;
-    if (duid[sizeof(duid) - 1] == 0) {
-        duid[sizeof(duid) - 2]++;
+    if (new_identifier) {
+        duid[sizeof(duid) - 1]++;
+        if (duid[sizeof(duid) - 1] == 0) {
+            duid[sizeof(duid) - 2]++;
+        }
     }
     }
     return (tmp.str());
     return (tmp.str());
 }
 }
@@ -105,18 +109,19 @@ HostPtr GenericHostDataSourceTest::initializeHost4(std::string address,
 }
 }
 
 
 HostPtr GenericHostDataSourceTest::initializeHost6(std::string address,
 HostPtr GenericHostDataSourceTest::initializeHost6(std::string address,
-                                                   BaseHostDataSource::IdType identifier,
-                                                   bool prefix) {
+                                                   Host::IdentifierType identifier,
+                                                   bool prefix,
+                                                   bool new_identifier) {
     string ident;
     string ident;
     string ident_type;
     string ident_type;
 
 
     switch (identifier) {
     switch (identifier) {
-    case BaseHostDataSource::ID_HWADDR:
-        ident = generateHWAddr();
+    case Host::IDENT_HWADDR:
+        ident = generateHWAddr(new_identifier);
         ident_type = "hw-address";
         ident_type = "hw-address";
         break;
         break;
-    case BaseHostDataSource::ID_DUID:
-        ident = generateDuid();
+    case Host::IDENT_DUID:
+        ident = generateDuid(new_identifier);
         ident_type = "duid";
         ident_type = "duid";
         break;
         break;
     default:
     default:
@@ -416,8 +421,14 @@ void GenericHostDataSourceTest::testGet4ByHWAddr() {
     SubnetID subnet1 = host1->getIPv4SubnetID();
     SubnetID subnet1 = host1->getIPv4SubnetID();
     SubnetID subnet2 = host2->getIPv4SubnetID();
     SubnetID subnet2 = host2->getIPv4SubnetID();
 
 
-    ConstHostPtr from_hds1 = hdsptr_->get4(subnet1, host1->getHWAddress());
-    ConstHostPtr from_hds2 = hdsptr_->get4(subnet2, host2->getHWAddress());
+    ConstHostPtr from_hds1 = hdsptr_->get4(subnet1,
+                                           Host::IDENT_HWADDR,
+                                           &host1->getIdentifier()[0],
+                                           host1->getIdentifier().size());
+    ConstHostPtr from_hds2 = hdsptr_->get4(subnet2,
+                                           Host::IDENT_HWADDR,
+                                           &host2->getIdentifier()[0],
+                                           host2->getIdentifier().size());
 
 
     // Now let's check if we got what we expected.
     // Now let's check if we got what we expected.
     ASSERT_TRUE(from_hds1);
     ASSERT_TRUE(from_hds1);
@@ -445,8 +456,15 @@ void GenericHostDataSourceTest::testGet4ByClientId() {
     SubnetID subnet1 = host1->getIPv4SubnetID();
     SubnetID subnet1 = host1->getIPv4SubnetID();
     SubnetID subnet2 = host2->getIPv4SubnetID();
     SubnetID subnet2 = host2->getIPv4SubnetID();
 
 
-    ConstHostPtr from_hds1 = hdsptr_->get4(subnet1, HWAddrPtr(), host1->getDuid());
-    ConstHostPtr from_hds2 = hdsptr_->get4(subnet2, HWAddrPtr(), host2->getDuid());
+    ConstHostPtr from_hds1 = hdsptr_->get4(subnet1,
+                                           Host::IDENT_DUID,
+                                           &host1->getIdentifier()[0],
+                                           host1->getIdentifier().size());
+
+    ConstHostPtr from_hds2 = hdsptr_->get4(subnet2,
+                                           Host::IDENT_DUID,
+                                           &host2->getIdentifier()[0],
+                                           host2->getIdentifier().size());
 
 
     // Now let's check if we got what we expected.
     // Now let's check if we got what we expected.
     ASSERT_TRUE(from_hds1);
     ASSERT_TRUE(from_hds1);
@@ -472,10 +490,14 @@ void GenericHostDataSourceTest::testHWAddrNotClientId() {
     DuidPtr duid = HWAddrToDuid(host->getHWAddress());
     DuidPtr duid = HWAddrToDuid(host->getHWAddress());
 
 
     // Get the host by HW address (should succeed)
     // Get the host by HW address (should succeed)
-    ConstHostPtr by_hwaddr = hdsptr_->get4(subnet, host->getHWAddress(), DuidPtr());
+    ConstHostPtr by_hwaddr = hdsptr_->get4(subnet, Host::IDENT_HWADDR,
+                                           &host->getIdentifier()[0],
+                                           host->getIdentifier().size());
 
 
     // Get the host by DUID (should fail)
     // Get the host by DUID (should fail)
-    ConstHostPtr by_duid   = hdsptr_->get4(subnet, HWAddrPtr(), duid);
+    ConstHostPtr by_duid   = hdsptr_->get4(subnet, Host::IDENT_DUID,
+                                           &host->getIdentifier()[0],
+                                           host->getIdentifier().size());
 
 
     // Now let's check if we got what we expected.
     // Now let's check if we got what we expected.
     EXPECT_TRUE(by_hwaddr);
     EXPECT_TRUE(by_hwaddr);
@@ -499,10 +521,15 @@ void GenericHostDataSourceTest::testClientIdNotHWAddr() {
     HWAddrPtr hwaddr = DuidToHWAddr(host->getDuid());
     HWAddrPtr hwaddr = DuidToHWAddr(host->getDuid());
 
 
     // Get the host by DUID (should succeed)
     // Get the host by DUID (should succeed)
-    ConstHostPtr by_duid   = hdsptr_->get4(subnet, HWAddrPtr(), host->getDuid());
+    ConstHostPtr by_duid   = hdsptr_->get4(subnet, Host::IDENT_DUID,
+                                           &host->getIdentifier()[0],
+                                           host->getIdentifier().size());
+
 
 
     // Get the host by HW address (should fail)
     // Get the host by HW address (should fail)
-    ConstHostPtr by_hwaddr = hdsptr_->get4(subnet, hwaddr, DuidPtr());
+    ConstHostPtr by_hwaddr = hdsptr_->get4(subnet, Host::IDENT_HWADDR,
+                                           &host->getIdentifier()[0],
+                                           host->getIdentifier().size());
 
 
     // Now let's check if we got what we expected.
     // Now let's check if we got what we expected.
     EXPECT_TRUE(by_duid);
     EXPECT_TRUE(by_duid);
@@ -588,7 +615,10 @@ GenericHostDataSourceTest::testMultipleSubnets(int subnets, bool hwaddr) {
         EXPECT_EQ(i + 1000, from_hds->getIPv4SubnetID());
         EXPECT_EQ(i + 1000, from_hds->getIPv4SubnetID());
 
 
         // Try to retrieve the host by either HW address of client-id
         // Try to retrieve the host by either HW address of client-id
-        from_hds = hdsptr_->get4(i + 1000, host->getHWAddress(), host->getDuid());
+        from_hds = hdsptr_->get4(i + 1000,
+                                 hwaddr ? Host::IDENT_HWADDR : Host::IDENT_DUID,
+                                 &host->getIdentifier()[0],
+                                 host->getIdentifier().size());
         ASSERT_TRUE(from_hds);
         ASSERT_TRUE(from_hds);
         EXPECT_EQ(i + 1000, from_hds->getIPv4SubnetID());
         EXPECT_EQ(i + 1000, from_hds->getIPv4SubnetID());
     }
     }
@@ -606,8 +636,10 @@ GenericHostDataSourceTest::testMultipleSubnets(int subnets, bool hwaddr) {
     }
     }
 
 
     // Finally, check that the hosts can be retrived by HW address or DUID
     // Finally, check that the hosts can be retrived by HW address or DUID
-    ConstHostCollection all_by_id = hdsptr_->getAll(host->getHWAddress(),
-                                                    host->getDuid());
+    ConstHostCollection all_by_id =
+        hdsptr_->getAll(hwaddr ? Host::IDENT_HWADDR : Host::IDENT_DUID,
+                        &host->getIdentifier()[0],
+                        host->getIdentifier().size());
     ASSERT_EQ(subnets, all_by_id.size());
     ASSERT_EQ(subnets, all_by_id.size());
 
 
     // Check that the returned values are as expected.
     // Check that the returned values are as expected.
@@ -624,8 +656,8 @@ void GenericHostDataSourceTest::testGet6ByHWAddr() {
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
 
 
     // Create a host reservations.
     // Create a host reservations.
-    HostPtr host1 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_HWADDR, true);
-    HostPtr host2 = initializeHost6("2001:db8::2", BaseHostDataSource::ID_HWADDR, true);
+    HostPtr host1 = initializeHost6("2001:db8::1", Host::IDENT_HWADDR, true);
+    HostPtr host2 = initializeHost6("2001:db8::2", Host::IDENT_HWADDR, true);
 
 
     // Sanity check: make sure the hosts have different HW addresses.
     // Sanity check: make sure the hosts have different HW addresses.
     ASSERT_TRUE(host1->getHWAddress());
     ASSERT_TRUE(host1->getHWAddress());
@@ -640,8 +672,13 @@ void GenericHostDataSourceTest::testGet6ByHWAddr() {
     SubnetID subnet1 = host1->getIPv6SubnetID();
     SubnetID subnet1 = host1->getIPv6SubnetID();
     SubnetID subnet2 = host2->getIPv6SubnetID();
     SubnetID subnet2 = host2->getIPv6SubnetID();
 
 
-    ConstHostPtr from_hds1 = hdsptr_->get6(subnet1, DuidPtr(), host1->getHWAddress());
-    ConstHostPtr from_hds2 = hdsptr_->get6(subnet2, DuidPtr(), host2->getHWAddress());
+    ConstHostPtr from_hds1 = hdsptr_->get6(subnet1, Host::IDENT_HWADDR,
+                                           &host1->getIdentifier()[0],
+                                           host1->getIdentifier().size());
+
+    ConstHostPtr from_hds2 = hdsptr_->get6(subnet2, Host::IDENT_HWADDR,
+                                           &host2->getIdentifier()[0],
+                                           host2->getIdentifier().size());
 
 
     // Now let's check if we got what we expected.
     // Now let's check if we got what we expected.
     ASSERT_TRUE(from_hds1);
     ASSERT_TRUE(from_hds1);
@@ -655,8 +692,8 @@ void GenericHostDataSourceTest::testGet6ByClientId() {
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
 
 
     // Create a host reservations.
     // Create a host reservations.
-    HostPtr host1 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
-    HostPtr host2 = initializeHost6("2001:db8::2", BaseHostDataSource::ID_DUID, true);
+    HostPtr host1 = initializeHost6("2001:db8::1", Host::IDENT_DUID, true);
+    HostPtr host2 = initializeHost6("2001:db8::2", Host::IDENT_DUID, true);
 
 
     // Sanity check: make sure the hosts have different HW addresses.
     // Sanity check: make sure the hosts have different HW addresses.
     ASSERT_TRUE(host1->getDuid());
     ASSERT_TRUE(host1->getDuid());
@@ -671,8 +708,13 @@ void GenericHostDataSourceTest::testGet6ByClientId() {
     SubnetID subnet1 = host1->getIPv6SubnetID();
     SubnetID subnet1 = host1->getIPv6SubnetID();
     SubnetID subnet2 = host2->getIPv6SubnetID();
     SubnetID subnet2 = host2->getIPv6SubnetID();
 
 
-    ConstHostPtr from_hds1 = hdsptr_->get6(subnet1, host1->getDuid(), HWAddrPtr());
-    ConstHostPtr from_hds2 = hdsptr_->get6(subnet2, host2->getDuid(), HWAddrPtr());
+    ConstHostPtr from_hds1 = hdsptr_->get6(subnet1, Host::IDENT_DUID,
+                                           &host1->getIdentifier()[0],
+                                           host1->getIdentifier().size());
+
+    ConstHostPtr from_hds2 = hdsptr_->get6(subnet2, Host::IDENT_DUID,
+                                           &host2->getIdentifier()[0],
+                                           host2->getIdentifier().size());
 
 
     // Now let's check if we got what we expected.
     // Now let's check if we got what we expected.
     ASSERT_TRUE(from_hds1);
     ASSERT_TRUE(from_hds1);
@@ -682,35 +724,43 @@ void GenericHostDataSourceTest::testGet6ByClientId() {
 }
 }
 
 
 void
 void
-GenericHostDataSourceTest::testSubnetId6(int subnets, BaseHostDataSource::IdType id) {
+GenericHostDataSourceTest::testSubnetId6(int subnets, Host::IdentifierType id) {
 
 
     // Make sure we have a pointer to the host data source.
     // Make sure we have a pointer to the host data source.
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
 
 
-    HostPtr host = initializeHost6("2001:db8::0", id, true);
-
+    HostPtr host;
+    IOAddress current_address("2001:db8::0");
     for (int i = 0; i < subnets; ++i) {
     for (int i = 0; i < subnets; ++i) {
+        // Last boolean value set to false indicates that the same identifier
+        // must be used for each generated host.
+        host = initializeHost6(current_address.toText(), id, true, false);
+
         host->setIPv4SubnetID(i + 1000);
         host->setIPv4SubnetID(i + 1000);
         host->setIPv6SubnetID(i + 1000);
         host->setIPv6SubnetID(i + 1000);
 
 
         // Check that the same host can have reservations in multiple subnets.
         // Check that the same host can have reservations in multiple subnets.
         EXPECT_NO_THROW(hdsptr_->add(host));
         EXPECT_NO_THROW(hdsptr_->add(host));
+
+        // Increase address to make sure we don't assign the same address
+        // in different subnets.
+        current_address = IOAddress::increase(current_address);
     }
     }
 
 
     // Check that the reservations can be retrieved from each subnet separately.
     // Check that the reservations can be retrieved from each subnet separately.
     for (int i = 0; i < subnets; ++i) {
     for (int i = 0; i < subnets; ++i) {
 
 
         // Try to retrieve the host
         // Try to retrieve the host
-        ConstHostPtr from_hds = hdsptr_->get6(i + 1000, host->getDuid(),
-                                              host->getHWAddress());
+        ConstHostPtr from_hds = hdsptr_->get6(i + 1000, id, &host->getIdentifier()[0],
+                                              host->getIdentifier().size());
 
 
-        ASSERT_TRUE(from_hds);
+        ASSERT_TRUE(from_hds) << "failed for i=" << i;
         EXPECT_EQ(i + 1000, from_hds->getIPv6SubnetID());
         EXPECT_EQ(i + 1000, from_hds->getIPv6SubnetID());
     }
     }
 
 
     // Check that the hosts can all be retrived by HW address or DUID
     // Check that the hosts can all be retrived by HW address or DUID
-    ConstHostCollection all_by_id = hdsptr_->getAll(host->getHWAddress(),
-                                                    host->getDuid());
+    ConstHostCollection all_by_id = hdsptr_->getAll(id, &host->getIdentifier()[0],
+                                                    host->getIdentifier().size());
     ASSERT_EQ(subnets, all_by_id.size());
     ASSERT_EQ(subnets, all_by_id.size());
 
 
     // Check that the returned values are as expected.
     // Check that the returned values are as expected.
@@ -722,7 +772,7 @@ GenericHostDataSourceTest::testSubnetId6(int subnets, BaseHostDataSource::IdType
     }
     }
 }
 }
 
 
-void GenericHostDataSourceTest::testGetByIPv6(BaseHostDataSource::IdType id,
+void GenericHostDataSourceTest::testGetByIPv6(Host::IdentifierType id,
                                               bool prefix) {
                                               bool prefix) {
     // Make sure we have a pointer to the host data source.
     // Make sure we have a pointer to the host data source.
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
@@ -770,7 +820,7 @@ void GenericHostDataSourceTest::testAddDuplicate6WithSameDUID() {
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
 
 
     // Create a host reservations.
     // Create a host reservations.
-    HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
+    HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_DUID, true);
 
 
     // Add this reservation once.
     // Add this reservation once.
     ASSERT_NO_THROW(hdsptr_->add(host));
     ASSERT_NO_THROW(hdsptr_->add(host));
@@ -784,7 +834,7 @@ void GenericHostDataSourceTest::testAddDuplicate6WithSameHWAddr() {
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
 
 
     // Create a host reservations.
     // Create a host reservations.
-    HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_HWADDR, true);
+    HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_HWADDR, true);
 
 
     // Add this reservation once.
     // Add this reservation once.
     ASSERT_NO_THROW(hdsptr_->add(host));
     ASSERT_NO_THROW(hdsptr_->add(host));
@@ -805,6 +855,17 @@ void GenericHostDataSourceTest::testAddDuplicate4() {
 
 
     // Then try to add it again, it should throw an exception.
     // Then try to add it again, it should throw an exception.
     ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
     ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
+
+    // This time use a different host identifier and try again.
+    // This update should be rejected because of duplicated
+    // address.
+    ASSERT_NO_THROW(host->setIdentifier("01:02:03:04:05:06", "hw-address"));
+    ASSERT_THROW(hdsptr_->add(host), DuplicateEntry);
+
+    // Modify address to avoid its duplication and make sure
+    // we can now add the host.
+    ASSERT_NO_THROW(host->setIPv4Reservation(IOAddress("192.0.2.3")));
+    EXPECT_NO_THROW(hdsptr_->add(host));
 }
 }
 
 
 void GenericHostDataSourceTest::testAddr6AndPrefix(){
 void GenericHostDataSourceTest::testAddr6AndPrefix(){
@@ -812,7 +873,7 @@ void GenericHostDataSourceTest::testAddr6AndPrefix(){
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
 
 
     // Create a host reservations with prefix reservation (prefix = true)
     // Create a host reservations with prefix reservation (prefix = true)
-    HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, true);
+    HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_DUID, true);
 
 
     // Create IPv6 reservation (for an address) and add it to the host
     // Create IPv6 reservation (for an address) and add it to the host
     IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::2"), 128);
     IPv6Resrv resv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::2"), 128);
@@ -822,13 +883,17 @@ void GenericHostDataSourceTest::testAddr6AndPrefix(){
     ASSERT_NO_THROW(hdsptr_->add(host));
     ASSERT_NO_THROW(hdsptr_->add(host));
 
 
     // Get this host by DUID
     // Get this host by DUID
-    ConstHostPtr from_hds = hdsptr_->get6(host->getIPv6SubnetID(), host->getDuid(), HWAddrPtr());
+    ConstHostPtr from_hds = hdsptr_->get6(host->getIPv6SubnetID(),
+                                          Host::IDENT_DUID,
+                                          &host->getIdentifier()[0],
+                                          host->getIdentifier().size());
 
 
     // Make sure we got something back
     // Make sure we got something back
     ASSERT_TRUE(from_hds);
     ASSERT_TRUE(from_hds);
 
 
     // Check if reservations are the same
     // Check if reservations are the same
-    compareReservations6(host->getIPv6Reservations(), from_hds->getIPv6Reservations());
+    compareReservations6(host->getIPv6Reservations(),
+                         from_hds->getIPv6Reservations());
 }
 }
 
 
 void GenericHostDataSourceTest::testMultipleReservations(){
 void GenericHostDataSourceTest::testMultipleReservations(){
@@ -836,7 +901,7 @@ void GenericHostDataSourceTest::testMultipleReservations(){
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
     uint8_t len = 128;
     uint8_t len = 128;
 
 
-    HostPtr host = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, false);
+    HostPtr host = initializeHost6("2001:db8::1", Host::IDENT_DUID, false);
 
 
     // Add some reservations
     // Add some reservations
     IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), len);
     IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), len);
@@ -866,8 +931,8 @@ void GenericHostDataSourceTest::testMultipleReservationsDifferentOrder(){
     ASSERT_TRUE(hdsptr_);
     ASSERT_TRUE(hdsptr_);
     uint8_t len = 128;
     uint8_t len = 128;
 
 
-    HostPtr host1 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, false);
-    HostPtr host2 = initializeHost6("2001:db8::1", BaseHostDataSource::ID_DUID, false);
+    HostPtr host1 = initializeHost6("2001:db8::1", Host::IDENT_DUID, false);
+    HostPtr host2 = initializeHost6("2001:db8::1", Host::IDENT_DUID, false);
 
 
     // Add some reservations
     // Add some reservations
     IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), len);
     IPv6Resrv resv1(IPv6Resrv::TYPE_NA, IOAddress("2001:db8::6"), len);

+ 17 - 9
src/lib/dhcpsrv/tests/generic_host_data_source_unittest.h

@@ -8,6 +8,7 @@
 #define GENERIC_HOST_DATA_SOURCE_UNITTEST_H
 #define GENERIC_HOST_DATA_SOURCE_UNITTEST_H
 
 
 #include <dhcpsrv/base_host_data_source.h>
 #include <dhcpsrv/base_host_data_source.h>
+#include <dhcpsrv/host.h>
 #include <dhcp/classify.h>
 #include <dhcp/classify.h>
 #include <gtest/gtest.h>
 #include <gtest/gtest.h>
 #include <vector>
 #include <vector>
@@ -46,21 +47,28 @@ public:
     /// @brief Creates a host reservation for specified IPv6 address.
     /// @brief Creates a host reservation for specified IPv6 address.
     ///
     ///
     /// @param address IPv6 address to be reserved
     /// @param address IPv6 address to be reserved
-    /// @param id type of identifier (ID_DUID or ID_HWADDR are supported)
+    /// @param id type of identifier (IDENT_DUID or IDENT_HWADDR are supported)
     /// @param prefix reservation type (true = prefix, false = address)
     /// @param prefix reservation type (true = prefix, false = address)
+    /// @param new_identifier Boolean value indicating if new host
+    /// identifier should be generated or the same as previously.
     ///
     ///
     /// @return generated Host object
     /// @return generated Host object
-    HostPtr initializeHost6(std::string address, BaseHostDataSource::IdType id,
-                            bool prefix);
+    HostPtr initializeHost6(std::string address, Host::IdentifierType id,
+                            bool prefix, bool new_identifier = true);
 
 
     /// @brief Generates a hardware address in text version.
     /// @brief Generates a hardware address in text version.
     ///
     ///
+    /// @param increase A boolean value indicating if new address (increased)
+    /// must be generated or the same address as previously.
     /// @return HW address in textual form acceptable by Host constructor
     /// @return HW address in textual form acceptable by Host constructor
-    std::string generateHWAddr();
+    std::string generateHWAddr(const bool new_identifier = true);
 
 
     /// @brief Generates a hardware address in text version.
     /// @brief Generates a hardware address in text version.
+    ///
+    /// @param increase A boolean value indicating if new DUID (increased)
+    /// must be generated or the same DUID as previously.
     /// @return DUID in textual form acceptable by Host constructor
     /// @return DUID in textual form acceptable by Host constructor
-    std::string generateDuid();
+    std::string generateDuid(const bool new_identifier = true);
 
 
     /// @brief Compares hardware addresses of the two hosts.
     /// @brief Compares hardware addresses of the two hosts.
     ///
     ///
@@ -183,9 +191,9 @@ public:
     ///        checks that they can be retrieved properly.
     ///        checks that they can be retrieved properly.
     ///
     ///
     /// Uses gtest macros to report failures.
     /// Uses gtest macros to report failures.
-    /// @param id type of the identifier to be used (HWAddr or DUID)
+    /// @param id type of the identifier to be used (IDENT_HWADDR or IDENT_DUID)
     /// @param prefix true - reserve IPv6 prefix, false - reserve IPv6 address
     /// @param prefix true - reserve IPv6 prefix, false - reserve IPv6 address
-    void testGetByIPv6(BaseHostDataSource::IdType id, bool prefix);
+    void testGetByIPv6(Host::IdentifierType id, bool prefix);
 
 
     /// @brief Test that hosts can be retrieved by hardware address.
     /// @brief Test that hosts can be retrieved by hardware address.
     ///
     ///
@@ -216,8 +224,8 @@ public:
     /// Uses gtest macros to report failures.
     /// Uses gtest macros to report failures.
     ///
     ///
     /// @param subnets number of subnets to test
     /// @param subnets number of subnets to test
-    /// @param id identifier type (ID_HWADDR or ID_DUID)
-    void testSubnetId6(int subnets, BaseHostDataSource::IdType id);
+    /// @param id identifier type (IDENT_HWADDR or IDENT_DUID)
+    void testSubnetId6(int subnets, Host::IdentifierType id);
 
 
     /// @brief Test if the duplicate host with same DUID can't be inserted.
     /// @brief Test if the duplicate host with same DUID can't be inserted.
     ///
     ///

+ 0 - 6
src/lib/dhcpsrv/tests/generic_lease_mgr_unittest.cc

@@ -2213,9 +2213,6 @@ GenericLeaseMgrTest::testGetDeclinedLeases4() {
     EXPECT_NE(0, declined_state);
     EXPECT_NE(0, declined_state);
     EXPECT_NE(0, default_state);
     EXPECT_NE(0, default_state);
 
 
-    // Remember expired leases returned.
-    std::vector<Lease4Ptr> saved_expired_leases = expired_leases;
-
     // Remove expired leases again.
     // Remove expired leases again.
     expired_leases.clear();
     expired_leases.clear();
 
 
@@ -2366,9 +2363,6 @@ GenericLeaseMgrTest::testGetDeclinedLeases6() {
     EXPECT_NE(0, declined_state);
     EXPECT_NE(0, declined_state);
     EXPECT_NE(0, default_state);
     EXPECT_NE(0, default_state);
 
 
-    // Remember expired leases returned.
-    std::vector<Lease6Ptr> saved_expired_leases = expired_leases;
-
     // Remove expired leases again.
     // Remove expired leases again.
     expired_leases.clear();
     expired_leases.clear();
 
 

+ 48 - 14
src/lib/dhcpsrv/tests/host_mgr_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -99,9 +99,13 @@ TEST_F(HostMgrTest, getAll) {
 
 
     // If there non-matching HW address is specified, nothing should be
     // If there non-matching HW address is specified, nothing should be
     // returned.
     // returned.
-    ASSERT_TRUE(HostMgr::instance().getAll(hwaddrs_[1]).empty());
+    ASSERT_TRUE(HostMgr::instance().getAll(Host::IDENT_HWADDR,
+                                           &hwaddrs_[1]->hwaddr_[0],
+                                           hwaddrs_[1]->hwaddr_.size()).empty());
     // For the correct HW address, there should be two reservations.
     // For the correct HW address, there should be two reservations.
-    hosts = HostMgr::instance().getAll(hwaddrs_[0]);
+    hosts = HostMgr::instance().getAll(Host::IDENT_HWADDR,
+                                       &hwaddrs_[0]->hwaddr_[0],
+                                       hwaddrs_[0]->hwaddr_.size());
     ASSERT_EQ(2, hosts.size());
     ASSERT_EQ(2, hosts.size());
 
 
     // We don't know the order in which the reservations are returned so
     // We don't know the order in which the reservations are returned so
@@ -137,9 +141,8 @@ TEST_F(HostMgrTest, getAll) {
 
 
 // This test verifies that it is possible to gather all reservations for the
 // This test verifies that it is possible to gather all reservations for the
 // specified IPv4 address from the HostMgr. The reservations are specified in
 // specified IPv4 address from the HostMgr. The reservations are specified in
-// the server's configuration. Note: this test is currently disabled because the
-// getAll4 method is not implemented in the CfgHosts object.
-TEST_F(HostMgrTest, DISABLED_getAll4) {
+// the server's configuration.
+TEST_F(HostMgrTest, getAll4) {
     ConstHostCollection hosts =
     ConstHostCollection hosts =
         HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
         HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
     ASSERT_TRUE(hosts.empty());
     ASSERT_TRUE(hosts.empty());
@@ -156,8 +159,12 @@ TEST_F(HostMgrTest, DISABLED_getAll4) {
     hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
     hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
     ASSERT_EQ(2, hosts.size());
     ASSERT_EQ(2, hosts.size());
 
 
-    /// @todo Extend this test to sanity check the hosts, once the test
-    /// is enabled.
+    // Make sure that IPv4 address is correct.
+    EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText());
+    EXPECT_EQ("192.0.2.5", hosts[1]->getIPv4Reservation().toText());
+
+    // Make sure that two different hosts were returned.
+    EXPECT_NE(hosts[0]->getIPv4SubnetID(), hosts[1]->getIPv4SubnetID());
 }
 }
 
 
 // This test verifies that it is possible to retrieve a reservation for the
 // This test verifies that it is possible to retrieve a reservation for the
@@ -173,7 +180,9 @@ TEST_F(HostMgrTest, get4) {
                                         IOAddress("192.0.2.5"))));
                                         IOAddress("192.0.2.5"))));
     CfgMgr::instance().commit();
     CfgMgr::instance().commit();
 
 
-    host = HostMgr::instance().get4(SubnetID(1), hwaddrs_[0]);
+    host = HostMgr::instance().get4(SubnetID(1), Host::IDENT_HWADDR,
+                                    &hwaddrs_[0]->hwaddr_[0],
+                                    hwaddrs_[0]->hwaddr_.size());
     ASSERT_TRUE(host);
     ASSERT_TRUE(host);
     EXPECT_EQ(1, host->getIPv4SubnetID());
     EXPECT_EQ(1, host->getIPv4SubnetID());
     EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText());
     EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText());
@@ -193,31 +202,56 @@ TEST_F(HostMgrTest, get6) {
     getCfgHosts()->add(new_host);
     getCfgHosts()->add(new_host);
     CfgMgr::instance().commit();
     CfgMgr::instance().commit();
 
 
-    host = HostMgr::instance().get6(SubnetID(2), duids_[0]);
+    host = HostMgr::instance().get6(SubnetID(2), Host::IDENT_DUID,
+                                    &duids_[0]->getDuid()[0],
+                                    duids_[0]->getDuid().size());
     ASSERT_TRUE(host);
     ASSERT_TRUE(host);
     EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
     EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
                                                IOAddress("2001:db8:1::1"))));
                                                IOAddress("2001:db8:1::1"))));
 }
 }
 
 
 // This test verifies that it is possible to retrieve the reservation of the
 // This test verifies that it is possible to retrieve the reservation of the
-// particular IPv6 prefix using HostMgr. Note: this test is currently disabled
-// because the get6(prefix, prefix_len) method is not implemented in the
-// CfgHosts class.
-TEST_F(HostMgrTest, DISABLED_get6ByPrefix) {
+// particular IPv6 prefix using HostMgr.
+TEST_F(HostMgrTest, get6ByPrefix) {
     ConstHostPtr host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
     ConstHostPtr host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
     ASSERT_FALSE(host);
     ASSERT_FALSE(host);
 
 
+    // Add a host with a reservation for a prefix 2001:db8:1::/64.
     HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1),
     HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1),
                               SubnetID(2), IOAddress("0.0.0.0")));
                               SubnetID(2), IOAddress("0.0.0.0")));
     new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
     new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
                                        IOAddress("2001:db8:1::"), 64));
                                        IOAddress("2001:db8:1::"), 64));
     getCfgHosts()->add(new_host);
     getCfgHosts()->add(new_host);
+
+    // Add another host having a reservation for prefix 2001:db8:1:0:6::/72.
+    new_host.reset(new Host(duids_[1]->toText(), "duid", SubnetID(2),
+                            SubnetID(3), IOAddress::IPV4_ZERO_ADDRESS()));
+    new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
+                                       IOAddress("2001:db8:1:0:6::"), 72));
+    getCfgHosts()->add(new_host);
     CfgMgr::instance().commit();
     CfgMgr::instance().commit();
 
 
+    // Retrieve first reservation.
     host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
     host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
     ASSERT_TRUE(host);
     ASSERT_TRUE(host);
     EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
     EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
     IOAddress("2001:db8:1::"), 64)));
     IOAddress("2001:db8:1::"), 64)));
+
+    // Make sure the first reservation is not retrieved when the prefix
+    // length is incorrect.
+    host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 72);
+    EXPECT_FALSE(host);
+
+    // Retrieve second reservation.
+    host = HostMgr::instance().get6(IOAddress("2001:db8:1:0:6::"), 72);
+    ASSERT_TRUE(host);
+    EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
+    IOAddress("2001:db8:1:0:6::"), 72)));
+
+    // Make sure the second reservation is not retrieved when the prefix
+    // length is incorrect.
+    host = HostMgr::instance().get6(IOAddress("2001:db8:1:0:6::"), 64);
+    EXPECT_FALSE(host);
 }
 }
 
 
 } // end of anonymous namespace
 } // end of anonymous namespace

+ 9 - 7
src/lib/dhcpsrv/tests/host_unittest.cc

@@ -631,16 +631,18 @@ TEST(HostTest, addOptions6) {
     EXPECT_TRUE(options->empty());
     EXPECT_TRUE(options->empty());
 }
 }
 
 
+// This test verifies that it is possible to retrieve a textual
+// representation of the host identifier.
 TEST(HostTest, getIdentifierAsText) {
 TEST(HostTest, getIdentifierAsText) {
     Host host1("01:02:03:04:05:06", "hw-address",
     Host host1("01:02:03:04:05:06", "hw-address",
                SubnetID(1), SubnetID(2),
                SubnetID(1), SubnetID(2),
                IOAddress("192.0.2.3"));
                IOAddress("192.0.2.3"));
-    EXPECT_EQ("hwaddr=01:02:03:04:05:06", host1.getIdentifierAsText());
+    EXPECT_EQ("hwaddr=010203040506", host1.getIdentifierAsText());
 
 
     Host host2("0a:0b:0c:0d:0e:0f:ab:cd:ef", "duid",
     Host host2("0a:0b:0c:0d:0e:0f:ab:cd:ef", "duid",
                SubnetID(1), SubnetID(2),
                SubnetID(1), SubnetID(2),
                IOAddress("192.0.2.3"));
                IOAddress("192.0.2.3"));
-    EXPECT_EQ("duid=0a:0b:0c:0d:0e:0f:ab:cd:ef",
+    EXPECT_EQ("duid=0A0B0C0D0E0FABCDEF",
               host2.getIdentifierAsText());
               host2.getIdentifierAsText());
 }
 }
 
 
@@ -666,7 +668,7 @@ TEST(HostTest, toText) {
     );
     );
 
 
     // Make sure that the output is correct,
     // Make sure that the output is correct,
-    EXPECT_EQ("hwaddr=01:02:03:04:05:06 ipv4_subnet_id=1 ipv6_subnet_id=2"
+    EXPECT_EQ("hwaddr=010203040506 ipv4_subnet_id=1 ipv6_subnet_id=2"
               " hostname=myhost.example.com"
               " hostname=myhost.example.com"
               " ipv4_reservation=192.0.2.3"
               " ipv4_reservation=192.0.2.3"
               " ipv6_reservation0=2001:db8:1::cafe"
               " ipv6_reservation0=2001:db8:1::cafe"
@@ -680,7 +682,7 @@ TEST(HostTest, toText) {
     host->removeIPv4Reservation();
     host->removeIPv4Reservation();
     host->setIPv4SubnetID(0);
     host->setIPv4SubnetID(0);
 
 
-    EXPECT_EQ("hwaddr=01:02:03:04:05:06 ipv6_subnet_id=2"
+    EXPECT_EQ("hwaddr=010203040506 ipv6_subnet_id=2"
               " hostname=(empty) ipv4_reservation=(no)"
               " hostname=(empty) ipv4_reservation=(no)"
               " ipv6_reservation0=2001:db8:1::cafe"
               " ipv6_reservation0=2001:db8:1::cafe"
               " ipv6_reservation1=2001:db8:1::1"
               " ipv6_reservation1=2001:db8:1::1"
@@ -695,14 +697,14 @@ TEST(HostTest, toText) {
                                         IOAddress::IPV4_ZERO_ADDRESS(),
                                         IOAddress::IPV4_ZERO_ADDRESS(),
                                         "myhost")));
                                         "myhost")));
 
 
-    EXPECT_EQ("duid=11:12:13:14:15 hostname=myhost ipv4_reservation=(no)"
+    EXPECT_EQ("duid=1112131415 hostname=myhost ipv4_reservation=(no)"
               " ipv6_reservations=(none)", host->toText());
               " ipv6_reservations=(none)", host->toText());
 
 
     // Add some classes.
     // Add some classes.
     host->addClientClass4("modem");
     host->addClientClass4("modem");
     host->addClientClass4("router");
     host->addClientClass4("router");
 
 
-    EXPECT_EQ("duid=11:12:13:14:15 hostname=myhost ipv4_reservation=(no)"
+    EXPECT_EQ("duid=1112131415 hostname=myhost ipv4_reservation=(no)"
               " ipv6_reservations=(none)"
               " ipv6_reservations=(none)"
               " dhcp4_class0=modem dhcp4_class1=router",
               " dhcp4_class0=modem dhcp4_class1=router",
               host->toText());
               host->toText());
@@ -710,7 +712,7 @@ TEST(HostTest, toText) {
     host->addClientClass6("hub");
     host->addClientClass6("hub");
     host->addClientClass6("device");
     host->addClientClass6("device");
 
 
-    EXPECT_EQ("duid=11:12:13:14:15 hostname=myhost ipv4_reservation=(no)"
+    EXPECT_EQ("duid=1112131415 hostname=myhost ipv4_reservation=(no)"
               " ipv6_reservations=(none)"
               " ipv6_reservations=(none)"
               " dhcp4_class0=modem dhcp4_class1=router"
               " dhcp4_class0=modem dhcp4_class1=router"
               " dhcp6_class0=device dhcp6_class1=hub",
               " dhcp6_class0=device dhcp6_class1=hub",

+ 6 - 5
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc

@@ -9,6 +9,7 @@
 #include <asiolink/io_address.h>
 #include <asiolink/io_address.h>
 #include <dhcpsrv/tests/test_utils.h>
 #include <dhcpsrv/tests/test_utils.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
+#include <dhcpsrv/host.h>
 #include <dhcpsrv/mysql_connection.h>
 #include <dhcpsrv/mysql_connection.h>
 #include <dhcpsrv/mysql_host_data_source.h>
 #include <dhcpsrv/mysql_host_data_source.h>
 #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
 #include <dhcpsrv/tests/generic_host_data_source_unittest.h>
@@ -272,25 +273,25 @@ TEST_F(MySqlHostDataSourceTest, DISABLED_hwaddrOrClientId2) {
 // Test verifies that host with IPv6 address and DUID can be added and
 // Test verifies that host with IPv6 address and DUID can be added and
 // later retrieved by IPv6 address.
 // later retrieved by IPv6 address.
 TEST_F(MySqlHostDataSourceTest, get6AddrWithDuid) {
 TEST_F(MySqlHostDataSourceTest, get6AddrWithDuid) {
-    testGetByIPv6(BaseHostDataSource::ID_DUID, false);
+    testGetByIPv6(Host::IDENT_DUID, false);
 }
 }
 
 
 // Test verifies that host with IPv6 address and HWAddr can be added and
 // Test verifies that host with IPv6 address and HWAddr can be added and
 // later retrieved by IPv6 address.
 // later retrieved by IPv6 address.
 TEST_F(MySqlHostDataSourceTest, get6AddrWithHWAddr) {
 TEST_F(MySqlHostDataSourceTest, get6AddrWithHWAddr) {
-    testGetByIPv6(BaseHostDataSource::ID_HWADDR, false);
+    testGetByIPv6(Host::IDENT_HWADDR, false);
 }
 }
 
 
 // Test verifies that host with IPv6 prefix and DUID can be added and
 // Test verifies that host with IPv6 prefix and DUID can be added and
 // later retrieved by IPv6 prefix.
 // later retrieved by IPv6 prefix.
 TEST_F(MySqlHostDataSourceTest, get6PrefixWithDuid) {
 TEST_F(MySqlHostDataSourceTest, get6PrefixWithDuid) {
-    testGetByIPv6(BaseHostDataSource::ID_DUID, true);
+    testGetByIPv6(Host::IDENT_DUID, true);
 }
 }
 
 
 // Test verifies that host with IPv6 prefix and HWAddr can be added and
 // Test verifies that host with IPv6 prefix and HWAddr can be added and
 // later retrieved by IPv6 prefix.
 // later retrieved by IPv6 prefix.
 TEST_F(MySqlHostDataSourceTest, get6PrefixWithHWaddr) {
 TEST_F(MySqlHostDataSourceTest, get6PrefixWithHWaddr) {
-    testGetByIPv6(BaseHostDataSource::ID_HWADDR, true);
+    testGetByIPv6(Host::IDENT_HWADDR, true);
 }
 }
 
 
 // Test verifies if a host reservation can be added and later retrieved by
 // Test verifies if a host reservation can be added and later retrieved by
@@ -373,7 +374,7 @@ TEST_F(MySqlHostDataSourceTest, multipleSubnetsClientId) {
 // Insert 10 host reservations for different subnets. Make sure that
 // Insert 10 host reservations for different subnets. Make sure that
 // get6(subnet-id, ...) calls return correct reservation.
 // get6(subnet-id, ...) calls return correct reservation.
 TEST_F(MySqlHostDataSourceTest, subnetId6) {
 TEST_F(MySqlHostDataSourceTest, subnetId6) {
-    testSubnetId6(10, BaseHostDataSource::ID_HWADDR);
+    testSubnetId6(10, Host::IDENT_HWADDR);
 }
 }
 
 
 // Test if the duplicate host instances can't be inserted. The test logic is as
 // Test if the duplicate host instances can't be inserted. The test logic is as

+ 38 - 0
src/lib/dhcpsrv/testutils/schema_mysql_copy.h

@@ -35,6 +35,7 @@ const char* destroy_statement[] = {
     "DROP TABLE hosts",
     "DROP TABLE hosts",
     "DROP TABLE dhcp4_options",
     "DROP TABLE dhcp4_options",
     "DROP TABLE dhcp6_options",
     "DROP TABLE dhcp6_options",
+    "DROP TABLE host_identifier_type",
 
 
     "DROP TRIGGER host_BDEL",
     "DROP TRIGGER host_BDEL",
     NULL
     NULL
@@ -255,6 +256,43 @@ const char* create_statement[] = {
 
 
     // Schema upgrade to 4.0 ends here.
     // Schema upgrade to 4.0 ends here.
 
 
+    "DROP INDEX key_dhcp4_identifier_subnet_id ON hosts",
+    "CREATE UNIQUE INDEX key_dhcp4_identifier_subnet_id "
+      "ON hosts "
+        "(dhcp_identifier ASC , dhcp_identifier_type ASC , dhcp4_subnet_id ASC)",
+
+    "DROP INDEX key_dhcp6_identifier_subnet_id ON hosts",
+    "CREATE UNIQUE INDEX key_dhcp6_identifier_subnet_id "
+      "ON hosts "
+        "(dhcp_identifier ASC , dhcp_identifier_type ASC , dhcp6_subnet_id ASC)",
+
+    "CREATE UNIQUE INDEX key_dhcp4_ipv4_address_subnet_id "
+      "ON hosts "
+        "(ipv4_address ASC, dhcp4_subnet_id ASC)",
+
+    "CREATE UNIQUE INDEX key_dhcp6_address_prefix_len "
+      "ON ipv6_reservations (address ASC , prefix_len ASC)",
+
+    "CREATE TABLE IF NOT EXISTS host_identifier_type ("
+      "type TINYINT PRIMARY KEY NOT NULL,"
+      "name VARCHAR(32)"
+    ") ENGINE = INNODB",
+
+    "START TRANSACTION",
+    "INSERT INTO host_identifier_type VALUES (0, \"hw-address\")",
+    "INSERT INTO host_identifier_type VALUES (1, \"duid\")",
+    "INSERT INTO host_identifier_type VALUES (2, \"circuit-id\")",
+    "COMMIT",
+
+    "ALTER TABLE hosts "
+    "ADD CONSTRAINT fk_host_identifier_type FOREIGN KEY (dhcp_identifier_type) "
+      "REFERENCES host_identifier_type (type)",
+
+    "UPDATE schema_version "
+      "SET version = '4', minor = '2'",
+
+    // Schema upgrade to 4.2 ends here.
+
     NULL
     NULL
 };
 };
 
 

+ 45 - 1
src/lib/dhcpsrv/writable_host_data_source.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2014-2016 Internet Systems Consortium, Inc. ("ISC")
 //
 //
 // This Source Code Form is subject to the terms of the Mozilla Public
 // 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
 // License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -40,6 +40,22 @@ public:
     virtual HostCollection
     virtual HostCollection
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) = 0;
     getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) = 0;
 
 
+    /// @brief Non-const version of the @c getAll const method.
+    ///
+    /// This method returns all @c Host objects which represent reservations
+    /// for a specified identifier. This method may return multiple hosts
+    /// because a particular client may have reservations in multiple subnets.
+    ///
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Collection of non-const @c Host objects.
+    virtual HostCollection
+    getAll(const Host::IdentifierType& identifier_type,
+           const uint8_t* identifier_begin,
+           const size_t identifier_len) = 0;
+
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     /// @brief Returns a collection of hosts using the specified IPv4 address.
     ///
     ///
     /// This method may return multiple @c Host objects if they are connected
     /// This method may return multiple @c Host objects if they are connected
@@ -69,6 +85,20 @@ public:
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
     get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
          const DuidPtr& duid = DuidPtr()) = 0;
          const DuidPtr& duid = DuidPtr()) = 0;
 
 
+    /// @brief Returns a host connected to the IPv4 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Non-const @c Host object for which reservation has been made
+    /// using the specified identifier.
+    virtual HostPtr
+    get4(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) = 0;
+
     /// @brief Returns a host connected to the IPv6 subnet.
     /// @brief Returns a host connected to the IPv6 subnet.
     ///
     ///
     /// Implementations of this method should guard against the case when
     /// Implementations of this method should guard against the case when
@@ -87,6 +117,20 @@ public:
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
     get6(const SubnetID& subnet_id, const DuidPtr& duid,
          const HWAddrPtr& hwaddr = HWAddrPtr()) = 0;
          const HWAddrPtr& hwaddr = HWAddrPtr()) = 0;
 
 
+    /// @brief Returns a host connected to the IPv6 subnet.
+    ///
+    /// @param subnet_id Subnet identifier.
+    /// @param identifier_type Identifier type.
+    /// @param identifier_begin Pointer to a begining of a buffer containing
+    /// an identifier.
+    /// @param identifier_len Identifier length.
+    ///
+    /// @return Non-const @c Host object for which reservation has been made
+    /// using the specified identifier.
+    virtual HostPtr
+    get6(const SubnetID& subnet_id, const Host::IdentifierType& identifier_type,
+         const uint8_t* identifier_begin, const size_t identifier_len) = 0;
+
     /// @brief Returns a host using the specified IPv6 prefix.
     /// @brief Returns a host using the specified IPv6 prefix.
     ///
     ///
     /// @param prefix IPv6 prefix for which the @c Host object is searched.
     /// @param prefix IPv6 prefix for which the @c Host object is searched.