/dhcpsrv_messages.cc
/dhcpsrv_messages.h
+/hosts_messages.cc
+/hosts_messages.h
/s-messages
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.
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
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
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)
# 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
// PERFORMANCE OF THIS SOFTWARE.
#include <dhcpsrv/cfg_hosts.h>
+#include <dhcpsrv/hosts_log.h>
#include <exceptions/exceptions.h>
#include <ostream>
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);
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);
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);
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);
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);
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);
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 =
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);
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"
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"
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));
}
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) {
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));
}
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));
}
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:
<< subnet_id << "' and using the address '"
<< address.toText() << "'");
}
+
}
template<typename Storage>
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"
}
}
-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.
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"
void
CfgHosts::add4(const HostPtr& host) {
-
/// @todo This may need further sanity checks.
HWAddrPtr hwaddr = host->getHWAddress();
DuidPtr duid = host->getDuid();
// 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) {
void
CfgHosts::add6(const HostPtr& host) {
-
/// @todo This may need further sanity checks.
HWAddrPtr hwaddr = host->getHWAddress();
DuidPtr duid = host->getDuid();
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.
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
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.
--- /dev/null
+// 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
+
--- /dev/null
+// 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
--- /dev/null
+# 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.
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