Browse Source

[3699] Added trace messages to the CfgHosts class.

Marcin Siodelski 10 years ago
parent
commit
c3d50f8759

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

@@ -1,3 +1,5 @@
 /dhcpsrv_messages.cc
 /dhcpsrv_messages.h
+/hosts_messages.cc
+/hosts_messages.h
 /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
 
 # 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
 	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
 # present when they are compiled), the safest option is to create it first.
 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
 # 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)
 
 # 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.
 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_container.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 += lease.cc lease.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 += hosts_messages.h hosts_messages.cc
 
 libkea_dhcpsrv_la_CXXFLAGS = $(AM_CXXFLAGS)
 libkea_dhcpsrv_la_CPPFLAGS = $(AM_CPPFLAGS) $(LOG4CPLUS_INCLUDES)
@@ -154,6 +160,7 @@ endif
 
 # The message file should be in the distribution
 EXTRA_DIST += dhcpsrv_messages.mes
+EXTRA_DIST += hosts_messages.mes
 
 # Distribute backend documentation
 # Database schema creation script moved to src/bin/admin

+ 67 - 23
src/lib/dhcpsrv/cfg_hosts.cc

@@ -13,6 +13,7 @@
 // PERFORMANCE OF THIS SOFTWARE.
 
 #include <dhcpsrv/cfg_hosts.h>
+#include <dhcpsrv/hosts_log.h>
 #include <exceptions/exceptions.h>
 #include <ostream>
 
@@ -23,6 +24,8 @@ namespace dhcp {
 
 ConstHostCollection
 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;
     getAllInternal<ConstHostCollection>(hwaddr, duid, collection);
     return (collection);
@@ -30,6 +33,8 @@ CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
 
 HostCollection
 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;
     getAllInternal<HostCollection>(hwaddr, duid, collection);
     return (collection);
@@ -37,6 +42,8 @@ CfgHosts::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) {
 
 ConstHostCollection
 CfgHosts::getAll4(const IOAddress& address) const {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal4 method.
     ConstHostCollection collection;
     getAllInternal4<ConstHostCollection>(address, collection);
     return (collection);
@@ -44,6 +51,8 @@ CfgHosts::getAll4(const IOAddress& address) const {
 
 HostCollection
 CfgHosts::getAll4(const IOAddress& address) {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal4 method.
     HostCollection collection;
     getAllInternal4<HostCollection>(address, collection);
     return (collection);
@@ -51,6 +60,8 @@ CfgHosts::getAll4(const IOAddress& address) {
 
 ConstHostCollection
 CfgHosts::getAll6(const IOAddress& address) const {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal6 method.
     ConstHostCollection collection;
     getAllInternal6<ConstHostCollection>(address, collection);
     return (collection);
@@ -58,6 +69,8 @@ CfgHosts::getAll6(const IOAddress& address) const {
 
 HostCollection
 CfgHosts::getAll6(const IOAddress& address) {
+    // Do not issue logging message here because it will be logged by
+    // the getAllInternal6 method.
     HostCollection collection;
     getAllInternal6<HostCollection>(address, collection);
     return (collection);
@@ -68,6 +81,8 @@ void
 CfgHosts::getAllInternal(const std::vector<uint8_t>& identifier,
                          const Host::IdentifierType& identifier_type,
                          Storage& storage) const {
+    // HOST_RESRV_GET_ALL_IDENTIFIER
+
     // Use the identifier and identifier type as a composite key.
     const HostContainerIndex0& idx = hosts_.get<0>();
     boost::tuple<const std::vector<uint8_t>, const Host::IdentifierType> t =
@@ -84,6 +99,10 @@ template<typename Storage>
 void
 CfgHosts::getAllInternal(const HWAddrPtr& hwaddr, const DuidPtr& duid,
                          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.
     if (hwaddr) {
         getAllInternal<Storage>(hwaddr->hwaddr_, Host::IDENT_HWADDR, storage);
@@ -97,6 +116,9 @@ CfgHosts::getAllInternal(const HWAddrPtr& hwaddr, const DuidPtr& duid,
 template<typename Storage>
 void
 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.
     if (!address.isV4()) {
         isc_throw(BadHostAddress, "must specify an IPv4 address when searching"
@@ -115,6 +137,9 @@ CfgHosts::getAllInternal4(const IOAddress& address, Storage& storage) const {
 template<typename Storage>
 void
 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.
     if (!address.isV6()) {
         isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
@@ -134,6 +159,7 @@ CfgHosts::getAllInternal6(const IOAddress& address, Storage& storage) const {
 ConstHostPtr
 CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
                const DuidPtr& duid) const {
+    // Do not log here because getHostInternal logs.
     // The false value indicates that it is an IPv4 subnet.
     return (getHostInternal(subnet_id, false, hwaddr, duid));
 }
@@ -141,12 +167,16 @@ CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
 HostPtr
 CfgHosts::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
                const DuidPtr& duid) {
+    // Do not log here because getHostInternal logs.
     // The false value indicates that it is an IPv4 subnet.
     return (getHostInternal(subnet_id, false, hwaddr, duid));
 }
 
 ConstHostPtr
 CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const {
+    LOG_DEBUG(hosts_logger, HOSTS_DBG_TRACE, HOSTS_CFG_GET_ONE_SUBNET_ID_ADDRESS4)
+        .arg(subnet_id).arg(address.toText());
+
     ConstHostCollection hosts = getAll4(address);
     for (ConstHostCollection::const_iterator host = hosts.begin();
          host != hosts.end(); ++host) {
@@ -161,6 +191,7 @@ CfgHosts::get4(const SubnetID& subnet_id, const IOAddress& address) const {
 ConstHostPtr
 CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) const {
+    // Do not log here because getHostInternal logs.
     // The true value indicates that it is an IPv6 subnet.
     return (getHostInternal(subnet_id, true, hwaddr, duid));
 }
@@ -168,6 +199,7 @@ CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
 HostPtr
 CfgHosts::get6(const SubnetID& subnet_id, const DuidPtr& duid,
                const HWAddrPtr& hwaddr) {
+    // Do not log here because getHostInternal logs.
     // The true value indicates that it is an IPv6 subnet.
     return (getHostInternal(subnet_id, true, hwaddr, duid));
 }
@@ -186,12 +218,29 @@ CfgHosts::get6(const IOAddress&, const uint8_t) {
 ConstHostPtr
 CfgHosts::get6(const SubnetID& subnet_id,
                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()) {
     case 0:
-        return (ConstHostPtr());
+        return (HostPtr());
     case 1:
         return (*storage.begin());
     default:
@@ -200,6 +249,7 @@ CfgHosts::get6(const SubnetID& subnet_id,
                   << subnet_id << "' and using the address '"
                   << address.toText() << "'");
     }
+
 }
 
 template<typename Storage>
@@ -207,6 +257,9 @@ void
 CfgHosts::getAllInternal6(const SubnetID& subnet_id,
                           const asiolink::IOAddress& address,
                           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.
     if (!address.isV6()) {
         isc_throw(BadHostAddress, "must specify an IPv6 address when searching"
@@ -228,25 +281,14 @@ CfgHosts::getAllInternal6(const SubnetID& subnet_id,
 }
 
 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() << "'");
-    }
-}
-
-HostPtr
 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)
+        .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
     // for different subnets, but the number of hosts returned should be low
     // because one host presumably doesn't show up in many subnets.
@@ -290,11 +332,15 @@ CfgHosts::getHostInternal(const SubnetID& subnet_id, const bool subnet6,
 
 void
 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.
     if (!host) {
         isc_throw(BadValue, "specified host object must not be NULL when it"
                   " is added to the configuration");
     }
+
     // At least one subnet ID must be non-zero
     if (host->getIPv4SubnetID() == 0 && host->getIPv6SubnetID() == 0) {
         isc_throw(BadValue, "must not use both IPv4 and IPv6 subnet ids of"
@@ -308,7 +354,6 @@ CfgHosts::add(const HostPtr& host) {
 
 void
 CfgHosts::add4(const HostPtr& host) {
-
     /// @todo This may need further sanity checks.
     HWAddrPtr hwaddr = host->getHWAddress();
     DuidPtr duid = host->getDuid();
@@ -316,7 +361,7 @@ CfgHosts::add4(const HostPtr& host) {
     // There should be at least one resource reserved: hostname, IPv4
     // address, IPv6 address or prefix.
     if (host->getHostname().empty() &&
-        (host->getIPv4Reservation() == IOAddress("0.0.0.0")) &&
+        (host->getIPv4Reservation().isV4Zero()) &&
         (!host->hasIPv6Reservation())) {
         std::ostringstream s;
         if (hwaddr) {
@@ -357,7 +402,6 @@ CfgHosts::add4(const HostPtr& host) {
 
 void
 CfgHosts::add6(const HostPtr& host) {
-
     /// @todo This may need further sanity checks.
     HWAddrPtr hwaddr = host->getHWAddress();
     DuidPtr duid = host->getDuid();

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

@@ -339,6 +339,23 @@ private:
                             const HWAddrPtr& hwaddr,
                             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.
     ///
     /// This is an internal method called by public @ref add.

+ 56 - 0
src/lib/dhcpsrv/host.cc

@@ -268,5 +268,61 @@ Host::getIdentifierAsText() const {
     return (txt);
 }
 
+std::string
+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 {
+        // 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();
+        }
+    }
+
+    // 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

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

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

+ 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

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

@@ -0,0 +1,56 @@
+# 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 retrieving 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_ADDRESS6 get all hosts with reservations for IPv6 address %1
+This debug message is issued when retrieving 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_HWADDR_DUID get all hosts with reservations for HWADDR %1 and DUID %2
+This debug message is issued when retrieving 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_SUBNET_ID_ADDRESS6 get all hosts with reservations for subnet id %1 and IPv6 address %2
+This debug message is issued when retrieving 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_ONE_SUBNET_ID_ADDRESS4 get one host with reservation for subnet id %1 and IPv4 address %2
+This debug message is issued when retrieving 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_ADDRESS6 get one host with reservation for subnet id %1 and including IPv6 address %2
+This debug message is issued when retrieveing 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_HWADDR_DUID get one host with %1 reservation for subnet id %2, HWADDR %3, DUID %4
+This debug message is issued when retrieving 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.

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

@@ -524,4 +524,77 @@ TEST(HostTest, 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