Browse Source

[master] Merge branch 'trac3699'

Marcin Siodelski 10 years ago
parent
commit
75b75c89db

+ 12 - 0
doc/guide/logging.xml

@@ -171,6 +171,12 @@
             libraries will be logged using this logger.</simpara>
             libraries will be logged using this logger.</simpara>
           </listitem>
           </listitem>
           <listitem>
           <listitem>
+            <simpara><command>kea-dhcp4.hosts</command> - this logger is used
+            within the libdhcpsrv and it logs messages related to the management
+            of the DHCPv4 host reservations, i.e. retrieval of the resevations
+            and adding new reservations.</simpara>
+          </listitem>
+          <listitem>
             <simpara><command>kea-dhcp6</command> - this is the root logger for
             <simpara><command>kea-dhcp6</command> - this is the root logger for
             the DHCPv6 server. All components used by the DHCPv6 server inherit
             the DHCPv6 server. All components used by the DHCPv6 server inherit
             the settings from this logger if there is no specialized logger
             the settings from this logger if there is no specialized logger
@@ -193,6 +199,12 @@
             libraries will be logged using this logger.</simpara>
             libraries will be logged using this logger.</simpara>
           </listitem>
           </listitem>
           <listitem>
           <listitem>
+            <simpara><command>kea-dhcp6.hosts</command> - this logger is used
+            within the libdhcpsrv and it logs messages related to the management
+            of the DHCPv6 host reservations, i.e. retrieval of the resevations
+            and adding new reservations.</simpara>
+          </listitem>
+          <listitem>
             <simpara><command>kea-dhcp-ddns</command> - this is the root logger for
             <simpara><command>kea-dhcp-ddns</command> - this is the root logger for
             the kea-dhcp-ddns deamon. All components used by this deamon inherit
             the kea-dhcp-ddns deamon. All components used by this deamon inherit
             the settings from this logger if there is no specialized logger
             the settings from this logger if there is no specialized logger

+ 2 - 0
src/lib/dhcpsrv/.gitignore

@@ -1,3 +1,5 @@
 /dhcpsrv_messages.cc
 /dhcpsrv_messages.cc
 /dhcpsrv_messages.h
 /dhcpsrv_messages.h
+/hosts_messages.cc
+/hosts_messages.h
 /s-messages
 /s-messages

+ 14 - 7
src/lib/dhcpsrv/Makefile.am

@@ -35,19 +35,22 @@ EXTRA_DIST += parsers/host_reservation_parser.h
 EXTRA_DIST += parsers/host_reservations_list_parser.h
 EXTRA_DIST += parsers/host_reservations_list_parser.h
 
 
 # Define rule to build logging source files from message file
 # Define rule to build logging source files from message file
-dhcpsrv_messages.h dhcpsrv_messages.cc: s-messages
+dhcpsrv_messages.h dhcpsrv_messages.cc hosts_messages.h hosts_messages.cc: s-messages
 
 
-s-messages: dhcpsrv_messages.mes
+s-messages: dhcpsrv_messages.mes hosts_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/dhcpsrv/dhcpsrv_messages.mes
 	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/dhcpsrv/dhcpsrv_messages.mes
 	touch $@
 	touch $@
+	$(top_builddir)/src/lib/log/compiler/message $(top_srcdir)/src/lib/dhcpsrv/hosts_messages.mes
+	touch $@
 
 
-# Tell Automake that the dhcpsrv_messages.{cc,h} source files are created in the
-# build process, so it must create these before doing anything else.  Although
-# they are a dependency of the library (so will be created from the message file
-# anyway), there is no guarantee as to exactly _when_ in the build they will be
+# Tell Automake that the {dhcpsrv,hosts}_messages.{cc,h} source files are created
+# in the build process, so it must create these before doing anything else.
+# Although they are a dependency of the library (so will be created from the message
+# file anyway), there is no guarantee as to exactly _when_ in the build they will be
 # created.  As the .h file is included in other sources file (so must be
 # created.  As the .h file is included in other sources file (so must be
 # present when they are compiled), the safest option is to create it first.
 # present when they are compiled), the safest option is to create it first.
 BUILT_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
 BUILT_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
+BUILT_SOURCES += hosts_messages.h hosts_messages.cc
 
 
 # Some versions of GCC warn about some versions of Boost regarding
 # Some versions of GCC warn about some versions of Boost regarding
 # missing initializer for members in its posix_time.
 # missing initializer for members in its posix_time.
@@ -56,7 +59,8 @@ BUILT_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
 AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 AM_CXXFLAGS += $(WARNING_NO_MISSING_FIELD_INITIALIZERS_CFLAG)
 
 
 # Make sure the generated files are deleted in a "clean" operation
 # Make sure the generated files are deleted in a "clean" operation
-CLEANFILES = *.gcno *.gcda dhcpsrv_messages.h dhcpsrv_messages.cc s-messages
+CLEANFILES = *.gcno *.gcda dhcpsrv_messages.h dhcpsrv_messages.cc
+CLEANFILES += hosts_messages.h hosts_messages.cc s-messages
 # Remove CSV files created by the CSVLeaseFile6 and CSVLeaseFile4 unit tests.
 # Remove CSV files created by the CSVLeaseFile6 and CSVLeaseFile4 unit tests.
 CLEANFILES += *.csv
 CLEANFILES += *.csv
 
 
@@ -84,6 +88,7 @@ libkea_dhcpsrv_la_SOURCES += dhcpsrv_log.cc dhcpsrv_log.h
 libkea_dhcpsrv_la_SOURCES += host.cc host.h
 libkea_dhcpsrv_la_SOURCES += host.cc host.h
 libkea_dhcpsrv_la_SOURCES += host_container.h
 libkea_dhcpsrv_la_SOURCES += host_container.h
 libkea_dhcpsrv_la_SOURCES += host_mgr.cc host_mgr.h
 libkea_dhcpsrv_la_SOURCES += host_mgr.cc host_mgr.h
+libkea_dhcpsrv_la_SOURCES += hosts_log.cc hosts_log.h
 libkea_dhcpsrv_la_SOURCES += key_from_key.h
 libkea_dhcpsrv_la_SOURCES += key_from_key.h
 libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
 libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
 libkea_dhcpsrv_la_SOURCES += lease_file_loader.h
 libkea_dhcpsrv_la_SOURCES += lease_file_loader.h
@@ -125,6 +130,7 @@ libkea_dhcpsrv_la_SOURCES += parsers/ifaces_config_parser.h
 
 
 
 
 nodist_libkea_dhcpsrv_la_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
 nodist_libkea_dhcpsrv_la_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
+nodist_libkea_dhcpsrv_la_SOURCES += hosts_messages.h hosts_messages.cc
 
 
 libkea_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
 libkea_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
 libkea_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
 libkea_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
@@ -154,6 +160,7 @@ endif
 
 
 # The message file should be in the distribution
 # The message file should be in the distribution
 EXTRA_DIST += dhcpsrv_messages.mes
 EXTRA_DIST += dhcpsrv_messages.mes
+EXTRA_DIST += hosts_messages.mes
 
 
 # Distribute backend documentation
 # Distribute backend documentation
 # Database schema creation script moved to src/bin/admin
 # Database schema creation script moved to src/bin/admin

+ 165 - 22
src/lib/dhcpsrv/cfg_hosts.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 // PERFORMANCE OF THIS SOFTWARE.
 
 
 #include <dhcpsrv/cfg_hosts.h>
 #include <dhcpsrv/cfg_hosts.h>
+#include <dhcpsrv/hosts_log.h>
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 #include <ostream>
 #include <ostream>
 
 
@@ -23,6 +24,8 @@ namespace dhcp {
 
 
 ConstHostCollection
 ConstHostCollection
 CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
 CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal method.
     ConstHostCollection collection;
     ConstHostCollection collection;
     getAllInternal<ConstHostCollection>(hwaddr, duid, collection);
     getAllInternal<ConstHostCollection>(hwaddr, duid, collection);
     return (collection);
     return (collection);
@@ -30,6 +33,8 @@ CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
 
 
 HostCollection
 HostCollection
 CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) {
 CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal method.
     HostCollection collection;
     HostCollection collection;
     getAllInternal<HostCollection>(hwaddr, duid, collection);
     getAllInternal<HostCollection>(hwaddr, duid, collection);
     return (collection);
     return (collection);
@@ -37,6 +42,8 @@ CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) {
 
 
 ConstHostCollection
 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
+    // the getAllInternal4 method.
     ConstHostCollection collection;
     ConstHostCollection collection;
     getAllInternal4<ConstHostCollection>(address, collection);
     getAllInternal4<ConstHostCollection>(address, collection);
     return (collection);
     return (collection);
@@ -44,6 +51,8 @@ CfgHosts::getAll4(const IOAddress& address) const {
 
 
 HostCollection
 HostCollection
 CfgHosts::getAll4(const IOAddress& address) {
 CfgHosts::getAll4(const IOAddress& address) {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal4 method.
     HostCollection collection;
     HostCollection collection;
     getAllInternal4<HostCollection>(address, collection);
     getAllInternal4<HostCollection>(address, collection);
     return (collection);
     return (collection);
@@ -51,6 +60,8 @@ CfgHosts::getAll4(const IOAddress& address) {
 
 
 ConstHostCollection
 ConstHostCollection
 CfgHosts::getAll6(const IOAddress& address) const {
 CfgHosts::getAll6(const IOAddress& address) const {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal6 method.
     ConstHostCollection collection;
     ConstHostCollection collection;
     getAllInternal6<ConstHostCollection>(address, collection);
     getAllInternal6<ConstHostCollection>(address, collection);
     return (collection);
     return (collection);
@@ -58,6 +69,8 @@ CfgHosts::getAll6(const IOAddress& address) const {
 
 
 HostCollection
 HostCollection
 CfgHosts::getAll6(const IOAddress& address) {
 CfgHosts::getAll6(const IOAddress& address) {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal6 method.
     HostCollection collection;
     HostCollection collection;
     getAllInternal6<HostCollection>(address, collection);
     getAllInternal6<HostCollection>(address, collection);
     return (collection);
     return (collection);
@@ -68,6 +81,35 @@ void
 CfgHosts::getAllInternal(const std::vector<uint8_t>& identifier,
 CfgHosts::getAllInternal(const std::vector<uint8_t>& identifier,
                          const Host::IdentifierType& identifier_type,
                          const Host::IdentifierType& identifier_type,
                          Storage& storage) const {
                          Storage& storage) const {
+    // We will need to transform the identifier into the textual format.
+    // Until we do it, we mark it as 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.
+        }
+
+    }
+    // This will log that we're invoking this function with the specified
+    // identifier. The identifier may also be marked as (invalid) if it
+    // had 0 length or its type is unsupported.
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_IDENTIFIER)
+        .arg(identifier_text);
+
+    // Do nothing if the identifier specified is invalid.
+    if (identifier_text == "(invalid)") {
+        return;
+    }
+
     // 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 =
@@ -76,14 +118,27 @@ CfgHosts::getAllInternal(const std::vector<uint8_t>& identifier,
     // 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,
+                  HOSTS_CFG_GET_ALL_IDENTIFIER_HOST)
+            .arg(identifier_text)
+            .arg((*host)->toText());
         storage.push_back(*host);
         storage.push_back(*host);
     }
     }
+
+    // Log how many hosts have been found.
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_IDENTIFIER_COUNT)
+        .arg(identifier_text)
+        .arg(storage.size());
 }
 }
 
 
 template<typename Storage>
 template<typename Storage>
 void
 void
 CfgHosts::getAllInternal(const HWAddrPtr& hwaddr, const DuidPtr& duid,
 CfgHosts::getAllInternal(const HWAddrPtr& hwaddr, const DuidPtr& duid,
                          Storage& storage) const {
                          Storage& storage) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_HWADDR_DUID)
+        .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
+        .arg(duid ? duid->toText() : "(no-duid)");
+
     // Get hosts using HW address.
     // Get hosts using HW address.
     if (hwaddr) {
     if (hwaddr) {
         getAllInternal<Storage>(hwaddr->hwaddr_, Host::IDENT_HWADDR, storage);
         getAllInternal<Storage>(hwaddr->hwaddr_, Host::IDENT_HWADDR, storage);
@@ -97,6 +152,9 @@ CfgHosts::getAllInternal(const HWAddrPtr& hwaddr, const DuidPtr& duid,
 template<typename Storage>
 template<typename Storage>
 void
 void
 CfgHosts::getAllInternal4(const IOAddress& address, Storage& storage) const {
 CfgHosts::getAllInternal4(const IOAddress& address, Storage& storage) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_ADDRESS4)
+        .arg(address.toText());
+
     // Must not specify address other than IPv4.
     // Must not specify address other than IPv4.
     if (!address.isV4()) {
     if (!address.isV4()) {
         isc_throw(BadHostAddress, "must specify an IPv4 address when searching"
         isc_throw(BadHostAddress, "must specify an IPv4 address when searching"
@@ -108,13 +166,24 @@ CfgHosts::getAllInternal4(const IOAddress& address, Storage& storage) const {
     // Append each Host object to the storage.
     // Append each Host object to the storage.
     for (HostContainerIndex1::iterator host = r.first; host != r.second;
     for (HostContainerIndex1::iterator host = r.first; host != r.second;
          ++host) {
          ++host) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
+                  HOSTS_CFG_GET_ALL_ADDRESS4_HOST)
+            .arg(address.toText())
+            .arg((*host)->toText());
         storage.push_back(*host);
         storage.push_back(*host);
     }
     }
+
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_ADDRESS4_COUNT)
+        .arg(address.toText())
+        .arg(storage.size());
 }
 }
 
 
 template<typename Storage>
 template<typename Storage>
 void
 void
 CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const {
 CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_ADDRESS6)
+        .arg(address.toText());
+
     // Must not specify address other than IPv6.
     // Must not specify address other than IPv6.
     if (!address.isV6()) {
     if (!address.isV6()) {
         isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
         isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
@@ -126,14 +195,23 @@ CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const {
     // Append each Host object to the storage.
     // Append each Host object to the storage.
     for (HostContainerIndex1::iterator host = r.first; host != r.second;
     for (HostContainerIndex1::iterator host = r.first; host != r.second;
          ++host) {
          ++host) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
+                  HOSTS_CFG_GET_ALL_ADDRESS6_HOST)
+            .arg(address.toText())
+            .arg((*host)->toText());
         storage.push_back(*host);
         storage.push_back(*host);
     }
     }
+
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ALL_ADDRESS6_COUNT)
+        .arg(address.toText())
+        .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.
     // 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));
     return (getHostInternal(subnet_id, false, hwaddr, duid));
 }
 }
@@ -141,19 +219,31 @@ CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
 HostPtr
 HostPtr
 CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
 CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
                const DuidPtr& duid) {
                const DuidPtr& duid) {
+    // 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));
     return (getHostInternal(subnet_id, false, hwaddr, duid));
 }
 }
 
 
 ConstHostPtr
 ConstHostPtr
 CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const {
 CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4)
+        .arg(subnet_id).arg(address.toText());
+
     ConstHostCollection hosts = getAll4(address);
     ConstHostCollection hosts = getAll4(address);
     for (ConstHostCollection::const_iterator host = hosts.begin();
     for (ConstHostCollection::const_iterator host = hosts.begin();
          host != hosts.end(); ++host) {
          host != hosts.end(); ++host) {
         if ((*host)->getIPv4SubnetID() == subnet_id) {
         if ((*host)->getIPv4SubnetID() == subnet_id) {
+            LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+                      HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_HOST)
+                .arg(subnet_id)
+                .arg(address.toText())
+                .arg((*host)->toText());
             return (*host);
             return (*host);
         }
         }
     }
     }
+
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_NULL)
+        .arg(subnet_id).arg(address.toText());
     return (ConstHostPtr());
     return (ConstHostPtr());
 }
 }
 
 
@@ -161,6 +251,7 @@ CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const {
 ConstHostPtr
 ConstHostPtr
 CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
 CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) const {
                const HWAddrPtr& hwaddr) const {
+    // 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));
     return (getHostInternal(subnet_id, true, hwaddr, duid));
 }
 }
@@ -168,6 +259,7 @@ CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
 HostPtr
 HostPtr
 CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
 CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) {
                const HWAddrPtr& hwaddr) {
+    // 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));
     return (getHostInternal(subnet_id, true, hwaddr, duid));
 }
 }
@@ -186,20 +278,49 @@ CfgHosts::get6(const IOAddress&, const uint8_t) {
 ConstHostPtr
 ConstHostPtr
 CfgHosts::get6(const SubnetID& subnet_id,
 CfgHosts::get6(const SubnetID& subnet_id,
                const asiolink::IOAddress& address) const {
                const asiolink::IOAddress& address) const {
-    ConstHostCollection storage;
-    getAllInternal6(subnet_id, address, storage);
+    // Do not log here because getHostInternal6 logs.
+    return (getHostInternal6<ConstHostPtr, ConstHostCollection>(subnet_id, address));
+}
 
 
+HostPtr
+CfgHosts::get6(const SubnetID& subnet_id,
+               const asiolink::IOAddress& address) {
+    // Do not log here because getHostInternal6 logs.
+    return (getHostInternal6<HostPtr, HostCollection>(subnet_id, address));
+}
+
+template<typename ReturnType, typename Storage>
+ReturnType
+CfgHosts::getHostInternal6(const SubnetID& subnet_id,
+                           const asiolink::IOAddress& address) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6)
+        .arg(subnet_id).arg(address.toText());
+
+    Storage storage;
+    getAllInternal6<Storage>(subnet_id, address, storage);
     switch (storage.size()) {
     switch (storage.size()) {
     case 0:
     case 0:
-        return (ConstHostPtr());
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+                  HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_NULL)
+            .arg(subnet_id)
+            .arg(address.toText());
+        return (HostPtr());
+
     case 1:
     case 1:
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+                  HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_HOST)
+            .arg(subnet_id)
+            .arg(address.toText())
+            .arg((*storage.begin())->toText());
         return (*storage.begin());
         return (*storage.begin());
+
     default:
     default:
         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 address '"
                   << subnet_id << "' and using the address '"
                   << address.toText() << "'");
                   << address.toText() << "'");
     }
     }
+
 }
 }
 
 
 template<typename Storage>
 template<typename Storage>
@@ -207,6 +328,9 @@ void
 CfgHosts::getAllInternal6(const SubnetID& subnet_id,
 CfgHosts::getAllInternal6(const SubnetID& subnet_id,
                           const asiolink::IOAddress& address,
                           const asiolink::IOAddress& address,
                           Storage& storage) const {
                           Storage& storage) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6)
+        .arg(subnet_id).arg(address.toText());
+
     // Must not specify address other than IPv6.
     // Must not specify address other than IPv6.
     if (!address.isV6()) {
     if (!address.isV6()) {
         isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
         isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
@@ -223,30 +347,30 @@ CfgHosts::getAllInternal6(const SubnetID& subnet_id,
     // multiple addresses reserved, but for each (address, subnet_id) there should
     // multiple addresses reserved, but for each (address, subnet_id) there should
     // be at most one host reserving it).
     // be at most one host reserving it).
     for(HostContainer6Index1::iterator resrv = r.first; resrv != r.second; ++resrv) {
     for(HostContainer6Index1::iterator resrv = r.first; resrv != r.second; ++resrv) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE_DETAIL_DATA,
+                  HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_HOST)
+            .arg(subnet_id)
+            .arg(address.toText())
+            .arg(resrv->host_);
         storage.push_back(resrv->host_);
         storage.push_back(resrv->host_);
     }
     }
-}
 
 
-HostPtr
-CfgHosts::get6(const SubnetID& subnet_id, const asiolink::IOAddress& address) {
-    HostCollection storage;
-    getAllInternal6<HostCollection>(subnet_id, address, storage);
-    switch (storage.size()) {
-    case 0:
-        return (HostPtr());
-    case 1:
-        return (*storage.begin());
-    default:
-        isc_throw(DuplicateHost,  "more than one reservation found"
-                  " for the host belonging to the subnet with id '"
-                  << subnet_id << "' and using the address '"
-                  << address.toText() << "'");
-    }
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+              HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT)
+        .arg(subnet_id)
+        .arg(address.toText())
+        .arg(storage.size());
 }
 }
 
 
 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 {
                           const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID)
+        .arg(subnet6 ? "IPv6" : "IPv4")
+        .arg(subnet_id)
+        .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
+        .arg(duid ? duid->toText() : "(no-duid)");
+
     // Get all hosts for the HW address and DUID. This may return multiple hosts
     // Get all hosts for the HW address and DUID. 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.
@@ -284,17 +408,38 @@ CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
             }
             }
         }
         }
     }
     }
+
+    if (host) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+                  HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID)
+            .arg(subnet_id)
+            .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
+            .arg(duid ? duid->toText() : "(no-duid)")
+            .arg(host->toText());
+
+    } else {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_RESULTS,
+                  HOSTS_CFG_GET_ONE_SUBNET_ID_HWADDR_DUID_NULL)
+            .arg(subnet_id)
+            .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
+            .arg(duid ? duid->toText() : "(no-duid)");
+    }
+
     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)
+        .arg(host ? host->toText() : "(no-host)");
+
     // Sanity check that the host is non-null.
     // Sanity check that the host is non-null.
     if (!host) {
     if (!host) {
         isc_throw(BadValue, "specified host object must not be NULL when it"
         isc_throw(BadValue, "specified host object must not be NULL when it"
                   " is added to the configuration");
                   " is added to the configuration");
     }
     }
+
     // At least one subnet ID must be non-zero
     // At least one subnet ID must be non-zero
     if (host->getIPv4SubnetID() == 0 && host->getIPv6SubnetID() == 0) {
     if (host->getIPv4SubnetID() == 0 && host->getIPv6SubnetID() == 0) {
         isc_throw(BadValue, "must not use both IPv4 and IPv6 subnet ids of"
         isc_throw(BadValue, "must not use both IPv4 and IPv6 subnet ids of"
@@ -308,7 +453,6 @@ CfgHosts::add(const HostPtr& host) {
 
 
 void
 void
 CfgHosts::add4(const HostPtr& host) {
 CfgHosts::add4(const HostPtr& host) {
-
     /// @todo This may need further sanity checks.
     /// @todo This may need further sanity checks.
     HWAddrPtr hwaddr = host->getHWAddress();
     HWAddrPtr hwaddr = host->getHWAddress();
     DuidPtr duid = host->getDuid();
     DuidPtr duid = host->getDuid();
@@ -316,7 +460,7 @@ CfgHosts::add4(const HostPtr& host) {
     // There should be at least one resource reserved: hostname, IPv4
     // There should be at least one resource reserved: hostname, IPv4
     // address, IPv6 address or prefix.
     // address, IPv6 address or prefix.
     if (host->getHostname().empty() &&
     if (host->getHostname().empty() &&
-        (host->getIPv4Reservation() == IOAddress("0.0.0.0")) &&
+        (host->getIPv4Reservation().isV4Zero()) &&
         (!host->hasIPv6Reservation())) {
         (!host->hasIPv6Reservation())) {
         std::ostringstream s;
         std::ostringstream s;
         if (hwaddr) {
         if (hwaddr) {
@@ -357,7 +501,6 @@ CfgHosts::add4(const HostPtr& host) {
 
 
 void
 void
 CfgHosts::add6(const HostPtr& host) {
 CfgHosts::add6(const HostPtr& host) {
-
     /// @todo This may need further sanity checks.
     /// @todo This may need further sanity checks.
     HWAddrPtr hwaddr = host->getHWAddress();
     HWAddrPtr hwaddr = host->getHWAddress();
     DuidPtr duid = host->getDuid();
     DuidPtr duid = host->getDuid();

+ 17 - 0
src/lib/dhcpsrv/cfg_hosts.h

@@ -339,6 +339,23 @@ private:
                             const HWAddrPtr& hwaddr,
                             const HWAddrPtr& hwaddr,
                             const DuidPtr& duid) const;
                             const DuidPtr& duid) const;
 
 
+    /// @brief Returns the @c Host object holding reservation for the IPv6
+    /// address and connected to the specific subnet.
+    ///
+    /// This private method is called by the public @c get6 method variants.
+    ///
+    /// @param subnet_id IPv6 subnet identifier.
+    /// @param address IPv6 address.
+    /// @tparam ReturnType One of @c HostPtr or @c ConstHostPtr
+    /// @tparam One of the @c ConstHostCollection or @c HostCollection.
+    ///
+    /// @return Pointer to the found host, or NULL if no host found.
+    /// @throw isc::dhcp::DuplicateHost if method found more than one matching
+    /// @c Host object.
+    template<typename ReturnType, typename Storage>
+    ReturnType getHostInternal6(const SubnetID& subnet_id,
+                                const asiolink::IOAddress& adddress) 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.

+ 67 - 10
src/lib/dhcpsrv/host.cc

@@ -138,6 +138,24 @@ Host::getIdentifierType() const {
     return (IDENT_DUID);
     return (IDENT_DUID);
 }
 }
 
 
+std::string
+Host::getIdentifierAsText() const {
+    std::string txt;
+    if (hw_address_) {
+        txt = "hwaddr=" + hw_address_->toText(false);
+    } else {
+        txt = "duid=";
+        if (duid_) {
+            txt += duid_->toText();
+        } else {
+            txt += "(none)";
+        }
+    }
+
+    return (txt);
+
+}
+
 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) {
@@ -252,20 +270,59 @@ Host::addClientClassInternal(ClientClasses& classes,
 }
 }
 
 
 std::string
 std::string
-Host::getIdentifierAsText() const {
-    std::string txt;
-    if (hw_address_) {
-        txt = "hwaddr=" + hw_address_->toText(false);
+Host::toText() const {
+    std::ostringstream s;
+
+    // Add HW address or DUID.
+    s << getIdentifierAsText();
+
+    // Add IPv4 subnet id if exists (non-zero).
+    if (ipv4_subnet_id_) {
+        s << " ipv4_subnet_id=" << ipv4_subnet_id_;
+    }
+
+    // Add IPv6 subnet id if exists (non-zero).
+    if (ipv6_subnet_id_) {
+        s << " ipv6_subnet_id=" << ipv6_subnet_id_;
+    }
+
+    // Add hostname.
+    s << " hostname=" << (hostname_.empty() ? "(empty)" : hostname_);
+
+    // Add IPv4 reservation.
+    s << " ipv4_reservation=" << (ipv4_reservation_.isV4Zero() ? "(no)" :
+                                  ipv4_reservation_.toText());
+
+    if (ipv6_reservations_.empty()) {
+        s << " ipv6_reservations=(none)";
+
     } else {
     } else {
-        txt = "duid=";
-        if (duid_) {
-            txt += duid_->toText();
-        } else {
-            txt += "(none)";
+        // Add all IPv6 reservations.
+        for (IPv6ResrvIterator resrv = ipv6_reservations_.begin();
+             resrv != ipv6_reservations_.end(); ++resrv) {
+            s << " ipv6_reservation"
+              << std::distance(ipv6_reservations_.begin(), resrv)
+              << "=" << resrv->second.toText();
         }
         }
     }
     }
 
 
-    return (txt);
+    // Add DHCPv4 client classes.
+    for (ClientClasses::const_iterator cclass = dhcp4_client_classes_.begin();
+         cclass != dhcp4_client_classes_.end(); ++cclass) {
+        s << " dhcp4_class"
+          << std::distance(dhcp4_client_classes_.begin(), cclass)
+          << "=" << *cclass;
+    }
+
+    // Add DHCPv6 client classes.
+    for (ClientClasses::const_iterator cclass = dhcp6_client_classes_.begin();
+         cclass != dhcp6_client_classes_.end(); ++cclass) {
+        s << " dhcp6_class"
+          << std::distance(dhcp6_client_classes_.begin(), cclass)
+          << "=" << *cclass;
+    }
+
+    return (s.str());
 }
 }
 
 
 } // end of namespace isc::dhcp
 } // end of namespace isc::dhcp

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

@@ -417,6 +417,9 @@ public:
         return (dhcp6_client_classes_);
         return (dhcp6_client_classes_);
     }
     }
 
 
+    /// @brief Returns information about the host in the textual format.
+    std::string toText() const;
+
 private:
 private:
 
 
     /// @brief Adds new client class for DHCPv4 or DHCPv6.
     /// @brief Adds new client class for DHCPv4 or DHCPv6.

+ 23 - 0
src/lib/dhcpsrv/host_mgr.cc

@@ -15,6 +15,7 @@
 #include <dhcpsrv/cfg_hosts.h>
 #include <dhcpsrv/cfg_hosts.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/cfgmgr.h>
 #include <dhcpsrv/host_mgr.h>
 #include <dhcpsrv/host_mgr.h>
+#include <dhcpsrv/hosts_log.h>
 
 
 namespace {
 namespace {
 
 
@@ -83,6 +84,11 @@ HostMgr::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
               const DuidPtr& duid) const {
               const DuidPtr& duid) const {
     ConstHostPtr host = getCfgHosts()->get4(subnet_id, hwaddr, duid);
     ConstHostPtr host = getCfgHosts()->get4(subnet_id, hwaddr, duid);
     if (!host && alternate_source) {
     if (!host && alternate_source) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+                  HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_HWADDR_DUID)
+            .arg(subnet_id)
+            .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)")
+            .arg(duid ? duid->toText() : "(duid)");
         host = alternate_source->get4(subnet_id, hwaddr, duid);
         host = alternate_source->get4(subnet_id, hwaddr, duid);
     }
     }
     return (host);
     return (host);
@@ -93,6 +99,10 @@ 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) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+                  HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_ADDRESS4)
+            .arg(subnet_id)
+            .arg(address.toText());
         host = alternate_source->get4(subnet_id, address);
         host = alternate_source->get4(subnet_id, address);
     }
     }
     return (host);
     return (host);
@@ -104,6 +114,11 @@ HostMgr::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) const {
                const HWAddrPtr& hwaddr) const {
     ConstHostPtr host = getCfgHosts()->get6(subnet_id, duid, hwaddr);
     ConstHostPtr host = getCfgHosts()->get6(subnet_id, duid, hwaddr);
     if (!host && alternate_source) {
     if (!host && alternate_source) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+                  HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_DUID_HWADDR)
+            .arg(subnet_id)
+            .arg(duid ? duid->toText() : "(duid)")
+            .arg(hwaddr ? hwaddr->toText() : "(no-hwaddr)");
         host = alternate_source->get6(subnet_id, duid, hwaddr);
         host = alternate_source->get6(subnet_id, duid, hwaddr);
     }
     }
     return (host);
     return (host);
@@ -113,6 +128,10 @@ ConstHostPtr
 HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
 HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
     ConstHostPtr host = getCfgHosts()->get6(prefix, prefix_len);
     ConstHostPtr host = getCfgHosts()->get6(prefix, prefix_len);
     if (!host && alternate_source) {
     if (!host && alternate_source) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+                  HOSTS_MGR_ALTERNATE_GET6_PREFIX)
+            .arg(prefix.toText())
+            .arg(static_cast<int>(prefix_len));
         host = alternate_source->get6(prefix, prefix_len);
         host = alternate_source->get6(prefix, prefix_len);
     }
     }
     return (host);
     return (host);
@@ -123,6 +142,10 @@ 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) {
+        LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE,
+                  HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_ADDRESS6)
+            .arg(subnet_id)
+            .arg(addr.toText());
         host = alternate_source->get6(subnet_id, addr);
         host = alternate_source->get6(subnet_id, addr);
     }
     }
     return (host);
     return (host);

+ 26 - 0
src/lib/dhcpsrv/hosts_log.cc

@@ -0,0 +1,26 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+/// @file Defines the logger used by the @c isc::dhcp::HostMgr
+
+#include "dhcpsrv/hosts_log.h"
+
+namespace isc {
+namespace dhcp {
+
+isc::log::Logger hosts_logger("hosts");
+
+} // namespace dhcp
+} // namespace isc
+

+ 64 - 0
src/lib/dhcpsrv/hosts_log.h

@@ -0,0 +1,64 @@
+// Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef HOSTS_LOG_H
+#define HOSTS_LOG_H
+
+#include <dhcpsrv/hosts_messages.h>
+#include <log/macros.h>
+
+namespace isc {
+namespace dhcp {
+
+///@{
+/// \brief Logging levels for the host reservations management.
+///
+/// Defines the levels used to output debug messages during the host
+/// reservations management, i.e. retrieving and adding host reservations.
+/// Note that higher numbers equate to more verbose(and detailed) output.
+
+/// @brief Traces normal operations
+///
+/// An example of the normal operation is the call to one of the functions
+/// which retrieve the reservations or add new reservation.
+const int HOSTS_DBG_TRACE = DBGLVL_TRACE_BASIC;
+
+/// @brief Records the results of the lookups
+///
+/// Messages logged at this level will typically contain summary of the
+/// data retrieved.
+const int HOSTS_DBG_RESULTS = DBGLVL_TRACE_BASIC_DATA;
+
+/// @brief Record detailed traces
+///
+/// Messages logged at this level will log detailed tracing information.
+const int HOSTS_DBG_TRACE_DETAIL = DBGLVL_TRACE_DETAIL;
+
+/// @brief Records detailed results of lookups.
+///
+/// Messages logged at this level will contain detailed results.
+const int HOSTS_DBG_TRACE_DETAIL_DATA = DBGLVL_TRACE_DETAIL_DATA;
+
+///@}
+
+/// @brief Logger for the @c HostMgr and the code it calls.
+///
+/// Define the logger used to log messages in @c HostMgr and the code it
+/// calls to manage host reservations.
+extern isc::log::Logger hosts_logger;
+
+} // namespace dhcp
+} // namespace isc
+
+#endif // HOSTS_LOG_H

+ 155 - 0
src/lib/dhcpsrv/hosts_messages.mes

@@ -0,0 +1,155 @@
+# Copyright (C) 2015  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+$NAMESPACE isc::dhcp
+
+% HOSTS_CFG_ADD_HOST add the host for reservations: %1
+This debug message is issued when new host (with reservations) is added to
+the server's configuration. The argument describes the host and its
+reservations in detail.
+
+% HOSTS_CFG_GET_ALL_ADDRESS4 get all hosts with reservations for IPv4 address %1
+This debug message is issued when starting to retrieve all hosts, holding the
+reservation for the specific IPv4 address, from the configuration. The
+argument specifies the IPv4 address used to search the hosts.
+
+% HOSTS_CFG_GET_ALL_ADDRESS4_HOST using address %1 found host: %2
+This debug message is issued when found host with the reservation
+for the specified IPv4 addres. The arguments specify the IPv4 address
+and the detailed description of the host found.
+
+% HOSTS_CFG_GET_ALL_ADDRESS4_COUNT using address %1, found %2 host(s)
+This debug message logs the number of hosts found using the specified
+IPv4 address. The arguments specify the IPv4 address used and the number
+of hosts found respectively.
+
+% HOSTS_CFG_GET_ALL_ADDRESS6 get all hosts with reservations for IPv6 address %1
+This debug message is issued when starting to retrieve all hosts, holding the
+reservation for the specific IPv6 address, from the configuration.
+The argument specifies the IPv6 address used to search the hosts.
+
+% HOSTS_CFG_GET_ALL_ADDRESS6_HOST using address %1 found host: %2
+This debug message is issued when found host with the reservation
+for the specified IPv6 address. The arguments specify the IPv6 address
+and the detailed description of the host found.
+
+% HOSTS_CFG_GET_ALL_ADDRESS6_COUNT using address %1, found %2 host(s)
+This debug message logs the number of hosts found using the specified
+IPv6 address. The arguments specify the IPv6 address used and the number
+of hosts found respectively.
+
+% HOSTS_CFG_GET_ALL_HWADDR_DUID get all hosts with reservations for HWADDR %1 and DUID %2
+This debug message is issued when starting to retrieve reservations for all hosts
+using specific HW address or DUID. The arguments specify the HW address and
+DUID respectively. The argument specify the HW address and DUID respectively.
+
+% HOSTS_CFG_GET_ALL_IDENTIFIER get all hosts with reservations using identifier: %1
+This debug message is issued when starting to retrieve reservations for all hosts
+identified by HW address or DUID. The argument holds both the identifier
+type and the value.
+
+% HOSTS_CFG_GET_ALL_IDENTIFIER_HOST using identifier: %1, found host: %2
+This debug message is issued when found host identified by the specific
+identifier. The arguments specify the identifier and the detailed
+description of the host found.
+
+% HOSTS_CFG_GET_ALL_IDENTIFIER_COUNT using identifier %1, found %2 host(s)
+This debug message logs the number of hosts found using the specified
+identifier. The arguments specify the identifier used and the number
+of hosts found respectively.
+
+% HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6 get all hosts with reservations for subnet id %1 and IPv6 address %2
+This debug message is issued when starting to retrieve all hosts connected to
+the specific subnet and having the specific IPv6 address reserved.
+The arguments specify subnet id and IPv6 address respectively.
+
+% 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
+subnet id and address. The arguments specify subnet id, address and
+found host details respectively.
+
+% HOSTS_CFG_GET_ALL_SUBNET_ID_ADDRESS6_COUNT using subnet id %1 and address %2, found %3 host(s)
+This debug message include the details of the host found using the
+subnet id and address. The arguments specify subnet id, address and
+found host details respectively.
+
+% 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
+specific subnet and having the specific IPv4 address reserved. The
+arguments specify subnet id and IPv4 address respectively.
+
+% HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_HOST using subnet id %1 and address %2, found host: %3
+This debug message logs the details of the host found using the
+subnet id and IPv4 address.
+
+% HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4_NULL host not found using subnet id %1 and address %2
+This debug message is issued when no host was found for the specified
+subnet id and IPv4 address.
+
+% HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6 get one host with reservation for subnet id %1 and including IPv6 address %2
+This debug message is issued when starting to retrieve a host connected to the
+specific subnet and having the specific IPv6 address reserved. The
+arguments specify subnet id and IPv6 address respectively.
+
+% HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_HOST using subnet id %1 and address %2, found host: %3
+This debug message logs the details of the host found using the
+subnet id and IPv6 address.
+
+% HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS6_NULL host not found using subnet id %1 and address %2
+This debug message is issued when no host was found using the specified
+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
+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_HWADDR_DUID_NULL host not found using subnet id %1, HW address %2 and DUID %3
+This debug message is issued when no host was found using the specified
+subnet id, HW address and DUID.
+
+% HOSTS_MGR_ALTERNATE_GET4_SUBNET_ID_HWADDR_DUID trying alternate source for host using subnet id %1, HWADDR %2, DUID%3
+This debug message is issued when the Host Manager doesn't find the
+host connected to the specific subnet and identified by the HW address
+or DUID, and it is starting to search for this host in the alternate
+host data source.
+
+% 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
+host connected to the specific subnet and having the reservation for
+the specific IPv4 address, and it is starting to search for this host
+in the alternate host data source.
+
+% HOSTS_MGR_ALTERNATE_GET6_PREFIX trying alternate source for host using prefix %1/%2
+This debug message is issued when the Host Manager doesn't find the
+host connected to the specific subnet and having the reservation for
+the specified prefix, and it is starting to search for this host in
+the alternate host data source.
+
+% HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_ADDRESS6 trying alternate source for host using subnet id %1 and IPv6 address %2
+This debug message is issued when the Host Manager doesn't find the
+host connected to teh specific subnet and having the reservation for
+the specified IPv6 address, and it is starting to search for this
+host in the alternate host data source.
+
+% HOSTS_MGR_ALTERNATE_GET6_SUBNET_ID_DUID_HWADDR trying alternate source for host using subnet id %1, DUID %2, HWADDR %3
+This debug message is issued when the Host Manager doesn't find the
+host connected to the specific subnet and identified by the specified
+DUID or HW Address, and it is starting to search for this host in the
+alternate host data source.

+ 73 - 0
src/lib/dhcpsrv/tests/host_unittest.cc

@@ -524,4 +524,77 @@ TEST(HostTest, getIdentifierAsText) {
               host2.getIdentifierAsText());
               host2.getIdentifierAsText());
 }
 }
 
 
+// This test checks that Host object is correctly described in the
+// textual format using the toText method.
+TEST(HostTest, toText) {
+    boost::scoped_ptr<Host> host;
+    ASSERT_NO_THROW(host.reset(new Host("01:02:03:04:05:06", "hw-address",
+                                        SubnetID(1), SubnetID(2),
+                                        IOAddress("192.0.2.3"),
+                                        "myhost.example.com")));
+
+    // Add 4 reservations: 2 for NAs, 2 for PDs.
+    ASSERT_NO_THROW(
+        host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
+                                       IOAddress("2001:db8:1::cafe")));
+        host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
+                                       IOAddress("2001:db8:1:1::"), 64));
+        host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
+                                       IOAddress("2001:db8:1:2::"), 64));
+        host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
+                                       IOAddress("2001:db8:1::1")));
+    );
+
+    // Make sure that the output is correct,
+    EXPECT_EQ("hwaddr=01:02:03:04:05:06 ipv4_subnet_id=1 ipv6_subnet_id=2"
+              " hostname=myhost.example.com"
+              " ipv4_reservation=192.0.2.3"
+              " ipv6_reservation0=2001:db8:1::cafe"
+              " ipv6_reservation1=2001:db8:1::1"
+              " ipv6_reservation2=2001:db8:1:1::/64"
+              " ipv6_reservation3=2001:db8:1:2::/64",
+              host->toText());
+
+    // Reset some of the data and make sure that the output is affected.
+    host->setHostname("");
+    host->removeIPv4Reservation();
+    host->setIPv4SubnetID(0);
+
+    EXPECT_EQ("hwaddr=01:02:03:04:05:06 ipv6_subnet_id=2"
+              " hostname=(empty) ipv4_reservation=(no)"
+              " ipv6_reservation0=2001:db8:1::cafe"
+              " ipv6_reservation1=2001:db8:1::1"
+              " ipv6_reservation2=2001:db8:1:1::/64"
+              " ipv6_reservation3=2001:db8:1:2::/64",
+              host->toText());
+
+    // Create host identified by DUID, instead of HWADDR, with a very
+    // basic configuration.
+    ASSERT_NO_THROW(host.reset(new Host("11:12:13:14:15", "duid",
+                                        SubnetID(0), SubnetID(0),
+                                        IOAddress::IPV4_ZERO_ADDRESS(),
+                                        "myhost")));
+
+    EXPECT_EQ("duid=11:12:13:14:15 hostname=myhost ipv4_reservation=(no)"
+              " ipv6_reservations=(none)", host->toText());
+
+    // Add some classes.
+    host->addClientClass4("modem");
+    host->addClientClass4("router");
+
+    EXPECT_EQ("duid=11:12:13:14:15 hostname=myhost ipv4_reservation=(no)"
+              " ipv6_reservations=(none)"
+              " dhcp4_class0=modem dhcp4_class1=router",
+              host->toText());
+
+    host->addClientClass6("hub");
+    host->addClientClass6("device");
+
+    EXPECT_EQ("duid=11:12:13:14:15 hostname=myhost ipv4_reservation=(no)"
+              " ipv6_reservations=(none)"
+              " dhcp4_class0=modem dhcp4_class1=router"
+              " dhcp6_class0=device dhcp6_class1=hub",
+              host->toText());
+}
+
 } // end of anonymous namespace
 } // end of anonymous namespace