libkea_dhcpsrv_la_SOURCES += dhcp_parsers.cc dhcp_parsers.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 += host_reservation_parser.cc host_reservation_parser.h
libkea_dhcpsrv_la_SOURCES += key_from_key.h
libkea_dhcpsrv_la_SOURCES += lease.cc lease.h
libkea_dhcpsrv_la_SOURCES += subnet_selector.h
libkea_dhcpsrv_la_SOURCES += triplet.h
libkea_dhcpsrv_la_SOURCES += utils.h
+libkea_dhcpsrv_la_SOURCES += writable_host_data_source.h
nodist_libkea_dhcpsrv_la_SOURCES = dhcpsrv_messages.h dhcpsrv_messages.cc
virtual ConstHostCollection
getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const = 0;
- /// @brief Non-const version of the @c getAll const method.
- ///
- /// Specifying both hardware address and DUID is allowed for this method
- /// and results in returning all objects that are associated with hardware
- /// address OR duid. For example: if one host is associated with the
- /// specified hardware address and another host is associated with the
- /// specified DUID, two hosts will be returned.
- ///
- /// @param hwaddr HW address of the client or NULL if no HW address
- /// available.
- /// @param duid client id or NULL if not available, e.g. DHCPv4 client case.
- ///
- /// @return Collection of non-const @c Host objects.
- virtual HostCollection
- getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) = 0;
-
/// @brief Returns a collection of hosts using the specified IPv4 address.
///
/// This method may return multiple @c Host objects if they are connected
virtual ConstHostCollection
getAll4(const asiolink::IOAddress& address) const = 0;
- /// @brief Returns a collection of hosts using the specified IPv4 address.
- ///
- /// This method may return multiple @c Host objects if they are connected
- /// to different subnets.
- ///
- /// @param address IPv4 address for which the @c Host object is searched.
- ///
- /// @return Collection of @c Host objects.
- virtual HostCollection
- getAll4(const asiolink::IOAddress& address) = 0;
-
/// @brief Returns a host connected to the IPv4 subnet.
///
/// Implementations of this method should guard against the case when
get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
const DuidPtr& duid = DuidPtr()) const = 0;
- /// @brief Returns a host connected to the IPv4 subnet.
- ///
- /// Implementations of this method should guard against the case when
- /// mutliple instances of the @c Host are present, e.g. when two
- /// @c Host objects are found, one for the DUID, another one for the
- /// HW address. In such case, an implementation of this method
- /// should throw an exception.
- ///
- /// @param subnet_id Subnet identifier.
- /// @param hwaddr HW address of the client or NULL if no HW address
- /// available.
- /// @param duid client id or NULL if not available.
- ///
- /// @return Non-const @c Host object using a specified HW address or DUID.
- virtual HostPtr
- get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
- const DuidPtr& duid = DuidPtr()) = 0;
-
/// @brief Returns a host connected to the IPv6 subnet.
///
/// Implementations of this method should guard against the case when
get6(const SubnetID& subnet_id, const DuidPtr& duid,
const HWAddrPtr& hwaddr = HWAddrPtr()) const = 0;
- /// @brief Returns a host connected to the IPv6 subnet.
- ///
- /// Implementations of this method should guard against the case when
- /// mutliple instances of the @c Host are present, e.g. when two
- /// @c Host objects are found, one for the DUID, another one for the
- /// HW address. In such case, an implementation of this method
- /// should throw an exception.
- ///
- /// @param subnet_id Subnet identifier.
- /// @param hwaddr HW address of the client or NULL if no HW address
- /// available.
- /// @param duid DUID or NULL if not available.
- ///
- /// @return Non-const @c Host object using a specified HW address or DUID.
- virtual HostPtr
- get6(const SubnetID& subnet_id, const DuidPtr& duid,
- const HWAddrPtr& hwaddr = HWAddrPtr()) = 0;
/// @brief Returns a host using the specified IPv6 prefix.
///
virtual ConstHostPtr
get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const = 0;
- /// @brief Returns a host using the specified IPv6 prefix.
- ///
- /// @param prefix IPv6 prefix for which the @c Host object is searched.
- /// @param prefix_len IPv6 prefix length.
- ///
- /// @return Non-const @c Host object using a specified HW address or DUID.
- virtual HostPtr
- get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) = 0;
-
/// @brief Adds a new host to the collection.
///
/// The implementations of this method should guard against duplicate
isc_throw(BadValue, "specified host object must not be NULL when it"
" is added to the configuration");
}
- /// @todo This may need further sanity checks. For example, a duplicate
- /// should be rejected.
+ // 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"
+ " 0 when adding new host reservation");
+ }
+ /// @todo This may need further sanity checks.
HWAddrPtr hwaddr = host->getHWAddress();
DuidPtr duid = host->getDuid();
// Check for duplicates for the specified IPv4 subnet.
- if (get4(host->getIPv4SubnetID(), hwaddr, duid)) {
+ if ((host->getIPv4SubnetID() > 0) &&
+ get4(host->getIPv4SubnetID(), hwaddr, duid)) {
isc_throw(DuplicateHost, "failed to add new host using the HW"
" address '" << (hwaddr ? hwaddr->toText(false) : "(null)")
<< " and DUID '" << (duid ? duid->toText() : "(null)")
<< "' as this host has already been added");
// Checek for duplicates for the specified IPv6 subnet.
- } else if (get6(host->getIPv6SubnetID(), duid, hwaddr)) {
+ } else if (host->getIPv6SubnetID() &&
+ get6(host->getIPv6SubnetID(), duid, hwaddr)) {
isc_throw(DuplicateHost, "failed to add new host using the HW"
" address '" << (hwaddr ? hwaddr->toText(false) : "(null)")
<< " and DUID '" << (duid ? duid->toText() : "(null)")
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
-#include <dhcpsrv/base_host_data_source.h>
#include <dhcpsrv/host.h>
#include <dhcpsrv/host_container.h>
#include <dhcpsrv/subnet_id.h>
+#include <dhcpsrv/writable_host_data_source.h>
#include <boost/shared_ptr.hpp>
#include <vector>
/// when the new configuration is applied for the server. The reservations
/// are retrieved by the @c HostMgr class when the server is allocating or
/// renewing an address or prefix for the particular client.
-class CfgHosts : public BaseHostDataSource {
+class CfgHosts : public WritableHostDataSource {
public:
/// @brief Return all hosts for the specified HW address or DUID.
--- /dev/null
+// Copyright (C) 2014 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.
+
+#include <dhcpsrv/cfg_hosts.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/host_mgr.h>
+
+namespace {
+
+/// @brief Convenience function returning a pointer to the hosts configuration.
+///
+/// This function is called by the @c HostMgr methods requiring access to the
+/// host reservations specified in the DHCP server configuration.
+///
+/// @return A pointer to the const hosts reservation configuration.
+isc::dhcp::ConstCfgHostsPtr getCfgHosts() {
+ return (isc::dhcp::CfgMgr::instance().getCurrentCfg()->getCfgHosts());
+}
+
+} // end of anonymous namespace
+
+namespace isc {
+namespace dhcp {
+
+using namespace isc::asiolink;
+
+boost::scoped_ptr<HostMgr>&
+HostMgr::getHostMgrPtr() {
+ static boost::scoped_ptr<HostMgr> host_mgr_ptr;
+ return (host_mgr_ptr);
+}
+
+void
+HostMgr::create(const std::string&) {
+ getHostMgrPtr().reset(new HostMgr());
+}
+
+HostMgr&
+HostMgr::instance() {
+ boost::scoped_ptr<HostMgr>& host_mgr_ptr = getHostMgrPtr();
+ if (!host_mgr_ptr) {
+ create();
+ }
+ return (*host_mgr_ptr);
+}
+
+ConstHostCollection
+HostMgr::getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid) const {
+ return (getCfgHosts()->getAll(hwaddr, duid));
+}
+
+ConstHostCollection
+HostMgr::getAll4(const IOAddress& address) const {
+ return (getCfgHosts()->getAll4(address));
+}
+
+ConstHostPtr
+HostMgr::get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
+ const DuidPtr& duid) const {
+ return (getCfgHosts()->get4(subnet_id, hwaddr, duid));
+}
+
+ConstHostPtr
+HostMgr::get6(const SubnetID& subnet_id, const DuidPtr& duid,
+ const HWAddrPtr& hwaddr) const {
+ return (getCfgHosts()->get6(subnet_id, duid, hwaddr));
+}
+
+ConstHostPtr
+HostMgr::get6(const IOAddress& prefix, const uint8_t prefix_len) const {
+ return (getCfgHosts()->get6(prefix, prefix_len));
+}
+
+void
+HostMgr::add(const HostPtr&) {
+ isc_throw(isc::NotImplemented, "HostMgr::add is not implemented");
+}
+
+} // end of isc::dhcp namespace
+} // end of isc namespace
--- /dev/null
+// Copyright (C) 2014 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 HOST_MGR_H
+#define HOST_MGR_H
+
+#include <boost/scoped_ptr.hpp>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Host Manager.
+///
+/// This is a singleton class which provides access to multiple sources of
+/// information about static host reservations. These sources are also referred
+/// to as host data sources. Each source derives (directly or indirectly) from
+/// the @c BaseHostDataSource.
+///
+/// The @c HostMgr is a central point for providing information about the host
+/// reservations. Internally, it relays the queries (calls to the appropriate
+/// methods declared in the @c BaseHostDataSource) to the data sources it is
+/// connected to. The @c HostMgr is always connected to the server's
+/// configuration, accessible through the @c CfgHosts object in the @c CfgMgr.
+/// The @c CfgHosts object holds all reservations specified in the DHCP server
+/// configuration file. If a particular reservation is not found in the
+/// @c CfgHosts object, the @c HostMgr will try to find it using the alternate
+/// host data storage. The alternate host data storage is usually a database
+/// (e.g. SQL database), accessible through a dedicated host data source
+/// object (a.k.a. database backend). This datasource is responsible for
+/// managing the connection with the database and forming appropriate queries
+/// to retrieve (or update) the information about the reservations.
+///
+/// The use of the alternate host data source is optional and usually requires
+/// additional configuration to be specified by the server administrator.
+/// For example, for the SQL database the user's credentials, database address,
+/// and database name are required. The @c HostMgr passes these parameters
+/// to an appropriate datasource which is responsible for opening a connection
+/// and maintaing it.
+///
+/// It is possible to switch to a different alternate data source or disable
+/// the use of the alternate datasource, e.g. as a result of server's
+/// reconfiguration. However, the use of the primary host data source (i.e.
+/// reservations specified in the configuration file) can't be disabled.
+///
+/// @todo Implement alternate host data sources: MySQL, PostgreSQL, etc.
+class HostMgr : public BaseHostDataSource {
+public:
+
+ /// @brief Creates new instance of the @c HostMgr.
+ ///
+ /// If an instance of the @c HostMgr already exists, it will be replaced
+ /// by the new instance. Thus, any instances of the alternate host data
+ /// sources will be dropped.
+ ///
+ /// @param access Host data source access parameters for the alternate
+ /// host data source. It holds "keyword=value" pairs, separated by spaces.
+ /// The supported values are specific to the alternate data source in use.
+ /// However, the "type" parameter is common and it specifies which data
+ /// source is to be used.
+ static void create(const std::string& access = "");
+
+ /// @brief Returns a sole instance of the @c HostMgr.
+ ///
+ /// This method should be used to retrieve an instance of the @c HostMgr
+ /// to be used to gather/manage host reservations. It returns an instance
+ /// of the @c HostMgr created by the @c create method. If such instance
+ /// doesn't exist yet, it is created using the @c create method with the
+ /// default c
+ static HostMgr& instance();
+
+ /// @brief Returns all hosts for the specified HW address or DUID.
+ ///
+ /// This method returns all @c Host objects representing reservations for
+ /// the specified HW address or DUID as documented in the
+ /// @c BaseHostDataSource::getAll.
+ ///
+ /// It retrieves reservations from both primary and alternate host data
+ /// source as a single collection of @c Host objects. Note that this
+ /// collection may contain duplicates. It is the caller's responsibility
+ /// to check for duplicates.
+ ///
+ /// @param hwaddr HW address of the client or NULL if no HW address
+ /// available.
+ /// @param duid client id or NULL of not available.
+ ///
+ /// @return Collection of const @c Host objects.
+ virtual ConstHostCollection
+ getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) const;
+
+ /// @brief Returns a collection of hosts using the specified IPv4 address.
+ ///
+ /// This method may return multiple @c Host objects if they are connected to
+ /// different subnets.
+ ///
+ /// @param address IPv4 address for which the @c Host object is searched.
+ ///
+ /// @return Collection of const @c Host objects.
+ virtual ConstHostCollection
+ getAll4(const asiolink::IOAddress& address) const;
+
+ /// @brief Returns a host connected to the IPv4 subnet.
+ ///
+ /// This method returns a single reservation for the particular host
+ /// (identified by the HW address or DUID) as documented in the
+ /// @c BaseHostDataSource::get4.
+ ///
+ /// @param subnet_id Subnet identifier.
+ /// @param hwaddr HW address of the client or NULL if no HW address
+ /// available.
+ /// @param duid client id or NULL if not available.
+ ///
+ /// @return Const @c Host object using a specified HW address or DUID.
+ virtual ConstHostPtr
+ get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
+ const DuidPtr& duid = DuidPtr()) const;
+
+ /// @brief Returns a host connected to the IPv6 subnet.
+ ///
+ /// This method returns a host connected to the IPv6 subnet and identified
+ /// by the HW address or DUID, as described in the
+ /// @c BaseHostDataSource::get6.
+ ///
+ /// @param subnet_id Subnet identifier.
+ /// @param hwaddr HW address of the client or NULL if no HW address
+ /// available.
+ /// @param duid DUID or NULL if not available.
+ ///
+ /// @return Const @c Host object using a specified HW address or DUID.
+ virtual ConstHostPtr
+ get6(const SubnetID& subnet_id, const DuidPtr& duid,
+ const HWAddrPtr& hwaddr = HWAddrPtr()) const;
+
+ /// @brief Returns a host using the specified IPv6 prefix.
+ ///
+ /// This method returns a host using specified IPv6 prefix, as described
+ /// in the @c BaseHostDataSource::get6.
+ ///
+ /// @param prefix IPv6 prefix for which the @c Host object is searched.
+ /// @param prefix_len IPv6 prefix length.
+ ///
+ /// @return Const @c Host object using a specified HW address or DUID.
+ virtual ConstHostPtr
+ get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) const;
+
+ /// @brief Adds a new host to the alternate data source.
+ ///
+ /// This method will throw an exception if no alternate data source is
+ /// in use.
+ ///
+ /// @param Pointer to the new @c Host object being added.
+ virtual void add(const HostPtr& host);
+
+private:
+
+ /// @brief Returns a pointer to the currently used instance of the
+ /// @c HostMgr.
+ static boost::scoped_ptr<HostMgr>& getHostMgrPtr();
+};
+}
+}
+
+#endif // HOST_MGR_H
libdhcpsrv_unittests_SOURCES += d2_udp_unittest.cc
libdhcpsrv_unittests_SOURCES += daemon_unittest.cc
libdhcpsrv_unittests_SOURCES += dbaccess_parser_unittest.cc
+libdhcpsrv_unittests_SOURCES += host_mgr_unittest.cc
libdhcpsrv_unittests_SOURCES += host_unittest.cc
libdhcpsrv_unittests_SOURCES += host_reservation_parser_unittest.cc
libdhcpsrv_unittests_SOURCES += lease_file_io.cc lease_file_io.h
for (int i = 0; i < 25; ++i) {
cfg.add(HostPtr(new Host(hwaddrs_[i]->toText(false),
"hw-address",
- SubnetID(i % 10), SubnetID(i % 5),
+ SubnetID(i % 10 + 1), SubnetID(i % 5 + 1),
IOAddress("192.0.2.5"))));
cfg.add(HostPtr(new Host(duids_[i]->toText(), "duid",
- SubnetID(i % 5), SubnetID(i % 10),
+ SubnetID(i % 5 + 1), SubnetID(i % 10 + 1),
IOAddress("192.0.2.10"))));
}
// points to a host for which the reservation hasn't been added.
HostCollection hosts = cfg.getAll(hwaddrs_[i], duids_[i + 25]);
ASSERT_EQ(1, hosts.size());
- EXPECT_EQ(i % 10, hosts[0]->getIPv4SubnetID());
+ EXPECT_EQ(i % 10 + 1, hosts[0]->getIPv4SubnetID());
EXPECT_EQ("192.0.2.5", hosts[0]->getIPv4Reservation().toText());
// Get host identified by DUID. The HW address is non-null but it
// points to a host for which the reservation hasn't been added.
hosts = cfg.getAll(hwaddrs_[i + 25], duids_[i]);
ASSERT_EQ(1, hosts.size());
- EXPECT_EQ(i % 5, hosts[0]->getIPv4SubnetID());
+ EXPECT_EQ(i % 5 + 1, hosts[0]->getIPv4SubnetID());
EXPECT_EQ("192.0.2.10", hosts[0]->getIPv4Reservation().toText());
}
EXPECT_THROW(cfg.get6(SubnetID(1), duids_[0], hwaddrs_[0]), DuplicateHost);
}
+TEST_F(CfgHostsTest, zeroSubnetIDs) {
+ CfgHosts cfg;
+ ASSERT_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
+ "hw-address",
+ SubnetID(0), SubnetID(0),
+ IOAddress("10.0.0.1")))),
+ isc::BadValue);
+}
+
// This test verifies that it is not possible to add the same Host to the
// same IPv4 subnet twice.
TEST_F(CfgHostsTest, duplicatesSubnet4HWAddr) {
// Add a host.
ASSERT_NO_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
"hw-address",
- SubnetID(10), SubnetID(1),
+ SubnetID(10), SubnetID(0),
IOAddress("10.0.0.1")))));
// Try to add the host with the same HW address to the same subnet. The fact
// that the IP address is different here shouldn't really matter.
EXPECT_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
"hw-address",
- SubnetID(10), SubnetID(12),
+ SubnetID(10), SubnetID(0),
IOAddress("10.0.0.10")))),
isc::dhcp::DuplicateHost);
// Now try to add it to a different subnet. It should go through.
EXPECT_NO_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
"hw-address",
- SubnetID(11), SubnetID(12),
+ SubnetID(11), SubnetID(0),
IOAddress("10.0.0.10")))));
}
// Add a host.
ASSERT_NO_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(),
"duid",
- SubnetID(10), SubnetID(1),
+ SubnetID(10), SubnetID(0),
IOAddress("10.0.0.1")))));
// Try to add the host with the same DUID to the same subnet. The fact
// that the IP address is different here shouldn't really matter.
EXPECT_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(),
"duid",
- SubnetID(10), SubnetID(12),
+ SubnetID(10), SubnetID(0),
IOAddress("10.0.0.10")))),
isc::dhcp::DuplicateHost);
// Now try to add it to a different subnet. It should go through.
EXPECT_NO_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(),
"duid",
- SubnetID(11), SubnetID(12),
+ SubnetID(11), SubnetID(0),
IOAddress("10.0.0.10")))));
}
// Add a host.
ASSERT_NO_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
"hw-address",
- SubnetID(10), SubnetID(1),
+ SubnetID(0), SubnetID(1),
IOAddress("0.0.0.0")))));
// Try to add the host with the same HW address to the same subnet. The fact
// that the IP address is different here shouldn't really matter.
EXPECT_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
"hw-address",
- SubnetID(11), SubnetID(1),
+ SubnetID(0), SubnetID(1),
IOAddress("0.0.0.0")))),
isc::dhcp::DuplicateHost);
// Now try to add it to a different subnet. It should go through.
EXPECT_NO_THROW(cfg.add(HostPtr(new Host(hwaddrs_[0]->toText(false),
"hw-address",
- SubnetID(11), SubnetID(2),
+ SubnetID(0), SubnetID(2),
IOAddress("0.0.0.0")))));
}
// Add a host.
ASSERT_NO_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(),
"duid",
- SubnetID(10), SubnetID(1),
+ SubnetID(0), SubnetID(1),
IOAddress("0.0.0.0")))));
// Try to add the host with the same DUID to the same subnet. The fact
// that the IP address is different here shouldn't really matter.
EXPECT_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(),
"duid",
- SubnetID(11), SubnetID(1),
+ SubnetID(0), SubnetID(1),
IOAddress("0.0.0.0")))),
isc::dhcp::DuplicateHost);
// Now try to add it to a different subnet. It should go through.
EXPECT_NO_THROW(cfg.add(HostPtr(new Host(duids_[0]->toText(),
"duid",
- SubnetID(11), SubnetID(2),
+ SubnetID(0), SubnetID(2),
IOAddress("0.0.0.0")))));
}
--- /dev/null
+// Copyright (C) 2014 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.
+
+#include <config.h>
+#include <dhcp/duid.h>
+#include <dhcp/hwaddr.h>
+#include <dhcpsrv/cfgmgr.h>
+#include <dhcpsrv/host.h>
+#include <dhcpsrv/host_mgr.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+using namespace isc;
+using namespace isc::dhcp;
+using namespace isc::asiolink;
+
+namespace {
+
+/// @brief Test fixture class for @c HostMgr class.
+class HostMgrTest : public ::testing::Test {
+protected:
+
+ /// @brief Prepares the class for a test.
+ ///
+ /// This method crates a handful of unique HW address and DUID objects
+ /// for use in unit tests. These objects are held in the @c hwaddrs_ and
+ /// @c duids_ members respectively.
+ ///
+ /// This method also resets the @c CfgMgr configuration and re-creates
+ /// the @c HostMgr object.
+ virtual void SetUp();
+
+ /// @brief Convenience method returning a pointer to the @c CfgHosts object
+ /// in the @c CfgMgr.
+ CfgHostsPtr getCfgHosts() const;
+
+ /// @brief HW addresses to be used by the tests.
+ std::vector<HWAddrPtr> hwaddrs_;
+ /// @brief DUIDs to be used by the tests.
+ std::vector<DuidPtr> duids_;
+};
+
+void
+HostMgrTest::SetUp() {
+ // Remove all configuration which may be dangling from the previous test.
+ CfgMgr::instance().clear();
+ // Recreate HostMgr instance. It drops any previous state.
+ HostMgr::create();
+ // Create HW addresses from the template.
+ const uint8_t mac_template[] = {
+ 0x01, 0x02, 0x0A, 0xBB, 0x03, 0x00
+ };
+ for (int i = 0; i < 10; ++i) {
+ std::vector<uint8_t> vec(mac_template,
+ mac_template + sizeof(mac_template));
+ vec[vec.size() - 1] = i;
+ HWAddrPtr hwaddr(new HWAddr(vec, HTYPE_ETHER));
+ hwaddrs_.push_back(hwaddr);
+ }
+ // Create DUIDs from the template.
+ const uint8_t duid_template[] = {
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x00
+ };
+ for (int i = 0; i < 10; ++i) {
+ std::vector<uint8_t> vec(duid_template,
+ duid_template + sizeof(mac_template));
+ vec[vec.size() - 1] = i;
+ DuidPtr duid(new DUID(vec));
+ duids_.push_back(duid);
+ }
+}
+
+CfgHostsPtr
+HostMgrTest::getCfgHosts() const {
+ return (CfgMgr::instance().getStagingCfg()->getCfgHosts());
+}
+
+/// This test verifies that HostMgr returns all reservations for the
+/// specified HW address. The reservations are defined in the server's
+/// configuration.
+TEST_F(HostMgrTest, getAll) {
+ // Initially, no reservations should be present.
+ ConstHostCollection hosts = HostMgr::instance().getAll(hwaddrs_[0]);
+ ASSERT_TRUE(hosts.empty());
+
+ // Add two reservations for the same HW address. They differ by the IP
+ // address reserved and the IPv4 subnet.
+ getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
+ "hw-address", SubnetID(1), SubnetID(0),
+ IOAddress("192.0.2.5"))));
+ getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
+ "hw-address", SubnetID(10), SubnetID(0),
+ IOAddress("192.0.3.10"))));
+ CfgMgr::instance().commit();
+
+ // If there non-matching HW address is specified, nothing should be
+ // returned.
+ ASSERT_TRUE(HostMgr::instance().getAll(hwaddrs_[1]).empty());
+ // For the correct HW address, there should be two reservations.
+ hosts = HostMgr::instance().getAll(hwaddrs_[0]);
+ ASSERT_EQ(2, hosts.size());
+
+ // We don't know the order in which the reservations are returned so
+ // we have to match with any of the two reservations returned.
+
+ // Look for the first reservation.
+ bool found = false;
+ for (int i = 0; i < 2; ++i) {
+ if (hosts[0]->getIPv4Reservation() == IOAddress("192.0.2.5")) {
+ ASSERT_EQ(1, hosts[0]->getIPv4SubnetID());
+ found = true;
+ }
+ }
+ if (!found) {
+ ADD_FAILURE() << "Reservation for the IPv4 address 192.0.2.5"
+ " not found using getAll method";
+ }
+
+ // Look for the second reservation.
+ found = false;
+ for (int i = 0; i < 2; ++i) {
+ if (hosts[1]->getIPv4Reservation() == IOAddress("192.0.3.10")) {
+ ASSERT_EQ(10, hosts[1]->getIPv4SubnetID());
+ found = true;
+ }
+ }
+ if (!found) {
+ ADD_FAILURE() << "Reservation for the IPv4 address 192.0.3.10"
+ " not found using getAll method";
+ }
+
+}
+
+// This test verifies that it is possible to gather all reservations for the
+// specified IPv4 address from the HostMgr. The reservations are specified in
+// the server's configuration. Note: this test is currently disabled because the
+// getAll4 method is not implemented in the CfgHosts object.
+TEST_F(HostMgrTest, DISABLED_getAll4) {
+ ConstHostCollection hosts =
+ HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
+ ASSERT_TRUE(hosts.empty());
+
+ getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
+ "hw-address", SubnetID(1), SubnetID(0),
+ IOAddress("192.0.2.5"))));
+
+ getCfgHosts()->add(HostPtr(new Host(hwaddrs_[1]->toText(false),
+ "hw-address", SubnetID(10), SubnetID(0),
+ IOAddress("192.0.2.5"))));
+ CfgMgr::instance().commit();
+
+ hosts = HostMgr::instance().getAll4(IOAddress("192.0.2.5"));
+ ASSERT_EQ(2, hosts.size());
+
+ /// @todo Extend this test to sanity check the hosts, once the test
+ /// is enabled.
+}
+
+// This test verifies that it is possible to retrieve a reservation for the
+// particular host using HostMgr. The reservation is specified in the server's
+// configuration.
+TEST_F(HostMgrTest, get4) {
+ ConstHostPtr host = HostMgr::instance().get4(SubnetID(1), hwaddrs_[0]);
+ ASSERT_FALSE(host);
+
+ getCfgHosts()->add(HostPtr(new Host(hwaddrs_[0]->toText(false),
+ "hw-address",
+ SubnetID(1), SubnetID(2),
+ IOAddress("192.0.2.5"))));
+ CfgMgr::instance().commit();
+
+ host = HostMgr::instance().get4(SubnetID(1), hwaddrs_[0]);
+ ASSERT_TRUE(host);
+ EXPECT_EQ(1, host->getIPv4SubnetID());
+ EXPECT_EQ("192.0.2.5", host->getIPv4Reservation().toText());
+}
+
+// This test verifies that it is possible to retrieve IPv6 reservations for
+// the particular host using HostMgr. The reservation is specified in the
+// server's configuration.
+TEST_F(HostMgrTest, get6) {
+ ConstHostPtr host = HostMgr::instance().get6(SubnetID(2), duids_[0]);
+ ASSERT_FALSE(host);
+
+ HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1),
+ SubnetID(2), IOAddress("0.0.0.0")));
+ new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
+ IOAddress("2001:db8:1::1")));
+ getCfgHosts()->add(new_host);
+ CfgMgr::instance().commit();
+
+ host = HostMgr::instance().get6(SubnetID(2), duids_[0]);
+ ASSERT_TRUE(host);
+ EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_NA,
+ IOAddress("2001:db8:1::1"))));
+}
+
+// This test verifies that it is possible to retrieve the reservation of the
+// particular IPv6 prefix using HostMgr. Note: this test is currently disabled
+// because the get6(prefix, prefix_len) method is not implemented in the
+// CfgHosts class.
+TEST_F(HostMgrTest, DISABLED_get6ByPrefix) {
+ ConstHostPtr host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
+ ASSERT_FALSE(host);
+
+ HostPtr new_host(new Host(duids_[0]->toText(), "duid", SubnetID(1),
+ SubnetID(2), IOAddress("0.0.0.0")));
+ new_host->addReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
+ IOAddress("2001:db8:1::"), 64));
+ getCfgHosts()->add(new_host);
+ CfgMgr::instance().commit();
+
+ host = HostMgr::instance().get6(IOAddress("2001:db8:1::"), 64);
+ ASSERT_TRUE(host);
+ EXPECT_TRUE(host->hasReservation(IPv6Resrv(IPv6Resrv::TYPE_PD,
+ IOAddress("2001:db8:1::"), 64)));
+}
+
+} // end of anonymous namespace
--- /dev/null
+// Copyright (C) 2014 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 WRITABLE_HOST_DATA_SOURCE_H
+#define WRITABLE_HOST_DATA_SOURCE_H
+
+#include <dhcpsrv/base_host_data_source.h>
+
+namespace isc {
+namespace dhcp {
+
+/// @brief Interface for retrieving writable host reservations.
+///
+/// This interface extends the @c BaseHostDataSource with methods which return
+/// pointers to the @c Host objects, which can be modified.
+class WritableHostDataSource : public BaseHostDataSource {
+public:
+
+ using BaseHostDataSource::getAll;
+ using BaseHostDataSource::getAll4;
+ using BaseHostDataSource::get4;
+ using BaseHostDataSource::get6;
+
+ /// @brief Non-const version of the @c getAll const method.
+ ///
+ /// Specifying both hardware address and DUID is allowed for this method
+ /// and results in returning all objects that are associated with hardware
+ /// address OR duid. For example: if one host is associated with the
+ /// specified hardware address and another host is associated with the
+ /// specified DUID, two hosts will be returned.
+ ///
+ /// @param hwaddr HW address of the client or NULL if no HW address
+ /// available.
+ /// @param duid client id or NULL if not available, e.g. DHCPv4 client case.
+ ///
+ /// @return Collection of non-const @c Host objects.
+ virtual HostCollection
+ getAll(const HWAddrPtr& hwaddr, const DuidPtr& duid = DuidPtr()) = 0;
+
+ /// @brief Returns a collection of hosts using the specified IPv4 address.
+ ///
+ /// This method may return multiple @c Host objects if they are connected
+ /// to different subnets.
+ ///
+ /// @param address IPv4 address for which the @c Host object is searched.
+ ///
+ /// @return Collection of @c Host objects.
+ virtual HostCollection
+ getAll4(const asiolink::IOAddress& address) = 0;
+
+ /// @brief Returns a host connected to the IPv4 subnet.
+ ///
+ /// Implementations of this method should guard against the case when
+ /// mutliple instances of the @c Host are present, e.g. when two
+ /// @c Host objects are found, one for the DUID, another one for the
+ /// HW address. In such case, an implementation of this method
+ /// should throw an exception.
+ ///
+ /// @param subnet_id Subnet identifier.
+ /// @param hwaddr HW address of the client or NULL if no HW address
+ /// available.
+ /// @param duid client id or NULL if not available.
+ ///
+ /// @return Non-const @c Host object using a specified HW address or DUID.
+ virtual HostPtr
+ get4(const SubnetID& subnet_id, const HWAddrPtr& hwaddr,
+ const DuidPtr& duid = DuidPtr()) = 0;
+
+ /// @brief Returns a host connected to the IPv6 subnet.
+ ///
+ /// Implementations of this method should guard against the case when
+ /// mutliple instances of the @c Host are present, e.g. when two
+ /// @c Host objects are found, one for the DUID, another one for the
+ /// HW address. In such case, an implementation of this method
+ /// should throw an exception.
+ ///
+ /// @param subnet_id Subnet identifier.
+ /// @param hwaddr HW address of the client or NULL if no HW address
+ /// available.
+ /// @param duid DUID or NULL if not available.
+ ///
+ /// @return Non-const @c Host object using a specified HW address or DUID.
+ virtual HostPtr
+ get6(const SubnetID& subnet_id, const DuidPtr& duid,
+ const HWAddrPtr& hwaddr = HWAddrPtr()) = 0;
+
+ /// @brief Returns a host using the specified IPv6 prefix.
+ ///
+ /// @param prefix IPv6 prefix for which the @c Host object is searched.
+ /// @param prefix_len IPv6 prefix length.
+ ///
+ /// @return Non-const @c Host object using a specified HW address or DUID.
+ virtual HostPtr
+ get6(const asiolink::IOAddress& prefix, const uint8_t prefix_len) = 0;
+
+};
+
+}
+}
+
+#endif // WRITABLE_HOST_DATA_SOURCE_H