// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
+
#include <asiolink/io_address.h>
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/cfg_hosts_util.h>
#include <dhcpsrv/host.h>
#include <dhcpsrv/cfgmgr.h>
+#include <testutils/gtest_utils.h>
+
#include <gtest/gtest.h>
+
#include <sstream>
#include <set>
"foo.example.com"))));
}
+// Checks that updates work correctly.
+TEST_F(CfgHostsTest, update) {
+ CfgHosts cfg;
+
+ HostPtr const host(boost::make_shared<Host>(duids_[0]->toText(), "duid", SUBNET_ID_UNUSED,
+ SubnetID(1), IOAddress("0.0.0.0"),
+ "foo.example.com"));
+
+ // Updating any host currently throws because it relies on delete being
+ // implemented which is not.
+ EXPECT_THROW_MSG(cfg.update(host), NotImplemented, "sorry, not implemented");
+
+ // Temporary return. Remove it and the preceding EXPECT_THROW_MSG when delete gets implemented.
+ return;
+
+ // Updating a host that doesn't exist should throw.
+ EXPECT_THROW_MSG(cfg.update(host), HostNotFound, "Host not updated (not found).");
+
+ // There should be no hosts.
+ HostCollection hosts(cfg.getAll6(SubnetID(1)));
+ EXPECT_EQ(0, hosts.size());
+
+ // Add a host.
+ EXPECT_NO_THROW(cfg.add(host));
+
+ // The host should be in the config.
+ hosts = cfg.getAll6(SubnetID(1));
+ ASSERT_EQ(1, hosts.size());
+ EXPECT_EQ("duid=010203040500 ipv6_subnet_id=1 hostname=foo.example.com "
+ "ipv4_reservation=(no) siaddr=(no) sname=(empty) file=(empty) "
+ "key=(empty) ipv6_reservations=(none)", hosts[0]->toText());
+
+ // Update the host. Change nothing.
+ EXPECT_NO_THROW(cfg.update(host));
+
+ // The same host should be in the config.
+ hosts = cfg.getAll6(SubnetID(1));
+ ASSERT_EQ(1, hosts.size());
+ EXPECT_EQ("duid=010203040500 ipv6_subnet_id=1 hostname=foo.example.com "
+ "ipv4_reservation=(no) siaddr=(no) sname=(empty) file=(empty) "
+ "key=(empty) ipv6_reservations=(none)", hosts[0]->toText());
+
+ // Update the host with new hostname.
+ host->setHostname("bar.example.com");
+ EXPECT_NO_THROW(cfg.update(host));
+
+ // The change should be reflected in the config.
+ hosts = cfg.getAll6(SubnetID(1));
+ ASSERT_EQ(1, hosts.size());
+ EXPECT_EQ("duid=010203040500 ipv6_subnet_id=1 hostname=bar.example.com "
+ "ipv4_reservation=(no) siaddr=(no) sname=(empty) file=(empty) "
+ "key=(empty) ipv6_reservations=(none)", hosts[0]->toText());
+
+ // Remove hostname from host.
+ host->setHostname("");
+ EXPECT_NO_THROW(cfg.update(host));
+
+ // The change should be reflected in the config.
+ hosts = cfg.getAll6(SubnetID(1));
+ ASSERT_EQ(1, hosts.size());
+ EXPECT_EQ("duid=010203040500 ipv6_subnet_id=1 hostname=(empty) "
+ "ipv4_reservation=(no) siaddr=(no) sname=(empty) file=(empty) "
+ "key=(empty) ipv6_reservations=(none)", hosts[0]->toText());
+}
-} // end of anonymous namespace
+} // namespace
return (false);
}
+ void update(HostPtr const&) {
+ }
+
std::string getType() const {
return ("one");
}
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
+
#include <dhcp/duid.h>
#include <dhcp/hwaddr.h>
#include <dhcpsrv/cfgmgr.h>
#include <dhcpsrv/host_data_source_factory.h>
#include <dhcpsrv/host_mgr.h>
#include <dhcpsrv/testutils/generic_host_data_source_unittest.h>
+#include <testutils/gtest_utils.h>
#include <gtest/gtest.h>
+
#include <vector>
using namespace isc;
using namespace isc::dhcp;
using namespace isc::dhcp::test;
using namespace isc::asiolink;
-
namespace {
// The tests in this file only address the in memory hosts.
}
// This test verifies that without a host data source an exception is thrown.
-TEST_F(HostMgrTest, addNoDataSource) {
+TEST_F(HostMgrTest, noDataSource) {
// Remove all configuration.
CfgMgr::instance().clear();
// Recreate HostMgr instance.
HostPtr host(new Host(hwaddrs_[0]->toText(false), "hw-address",
SubnetID(1), SUBNET_ID_UNUSED, IOAddress("192.0.2.5")));
- EXPECT_THROW(HostMgr::instance().add(host), NoHostDataSourceManager);
+ EXPECT_THROW_MSG(HostMgr::instance().add(host), NoHostDataSourceManager,
+ "Unable to add new host because there is no hosts-database configured.");
+ EXPECT_THROW_MSG(HostMgr::instance().update(host), NoHostDataSourceManager,
+ "Unable to update existing host because there is no hosts-database "
+ "configured.");
}
} // namespace
-// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
testMultipleHosts6();
}
+/// @brief Tests that hosts can be updated.
+TEST_F(MySqlHostDataSourceTest, update) {
+ testUpdate();
+}
+
+/// @brief Tests that hosts can be updated.
+TEST_F(MySqlHostDataSourceTest, updateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testUpdate();
+}
+
/// @brief Test fixture class for validating @c HostMgr using
/// MySQL as alternate host data source.
class MySQLHostMgrTest : public HostMgrTest {
-// Copyright (C) 2016-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2023 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
testMultipleHosts6();
}
+/// @brief Tests that hosts can be updated.
+TEST_F(PgSqlHostDataSourceTest, update) {
+ testUpdate();
+}
+
+/// @brief Tests that hosts can be updated.
+TEST_F(PgSqlHostDataSourceTest, updateMultiThreading) {
+ MultiThreadingTest mt(true);
+ testUpdate();
+}
/// @brief Test fixture class for validating @c HostMgr using
/// PostgreSQL as alternate host data source.
class PgSQLHostMgrTest : public HostMgrTest {
#include <dhcpsrv/testutils/host_data_source_utils.h>
#include <dhcpsrv/testutils/test_utils.h>
#include <database/testutils/schema.h>
+#include <testutils/gtest_utils.h>
#include <util/buffer.h>
-#include <boost/foreach.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
#include <gtest/gtest.h>
-#include <chrono>
-#include <cstring>
-#include <list>
#include <sstream>
#include <string>
-#include <typeinfo>
using namespace std;
using namespace isc::asiolink;
IPv6Resrv(IPv6Resrv::TYPE_NA, IOAddress("2001:db8:1::5"))));
}
+void
+GenericHostDataSourceTest::testUpdate() {
+ // Make sure the host data source is initialized.
+ ASSERT_TRUE(hdsptr_);
+
+ // Create a host with an IPv4 address reservation.
+ HostPtr const host(HostDataSourceUtils::initializeHost4("192.0.2.1", Host::IDENT_HWADDR));
+ SubnetID const v4_subnet(host->getIPv4SubnetID());
+ SubnetID const v6_subnet(host->getIPv6SubnetID());
+ string hwaddr(host->getHWAddress()->toText(false));
+ boost::replace_all(hwaddr, ":", "");
+ boost::to_upper(hwaddr);
+
+ // Updating a host that doesn't exist should throw.
+ EXPECT_THROW_MSG(hdsptr_->update(host), NoRowsAffected, "Host not updated (not found).");
+
+ // There should be no hosts.
+ ConstHostCollection hosts(hdsptr_->getAll4(v4_subnet));
+ EXPECT_EQ(0, hosts.size());
+
+ // Add the host.
+ EXPECT_NO_THROW(hdsptr_->add(host));
+
+ // The host should be there.
+ hosts = hdsptr_->getAll4(v4_subnet);
+ EXPECT_EQ(1, hosts.size());
+ EXPECT_EQ("hwaddr=" + hwaddr + " ipv4_subnet_id=" + to_string(v4_subnet) +
+ " ipv6_subnet_id=" + to_string(v6_subnet) +
+ " hostname=(empty) "
+ "ipv4_reservation=192.0.2.1 siaddr=(no) sname=(empty) file=(empty) key=(empty) "
+ "ipv6_reservations=(none)",
+ hosts[0]->toText());
+
+ // Update the host. Change nothing.
+ EXPECT_NO_THROW(hdsptr_->update(host));
+
+ // The same host should be in the data source.
+ hosts = hdsptr_->getAll4(v4_subnet);
+ EXPECT_EQ(1, hosts.size());
+ EXPECT_EQ("hwaddr=" + hwaddr + " ipv4_subnet_id=" + to_string(v4_subnet) +
+ " ipv6_subnet_id=" + to_string(v6_subnet) +
+ " hostname=(empty) "
+ "ipv4_reservation=192.0.2.1 siaddr=(no) sname=(empty) file=(empty) key=(empty) "
+ "ipv6_reservations=(none)",
+ hosts[0]->toText());
+
+ // Update the host with new hostname.
+ host->setHostname("foo.example.com");
+ EXPECT_NO_THROW(hdsptr_->update(host));
+
+ // The change should be reflected in the data source.
+ hosts = hdsptr_->getAll4(v4_subnet);
+ EXPECT_EQ(1, hosts.size());
+ EXPECT_EQ("hwaddr=" + hwaddr + " ipv4_subnet_id=" + to_string(v4_subnet) +
+ " ipv6_subnet_id=" + to_string(v6_subnet) +
+ " hostname=foo.example.com "
+ "ipv4_reservation=192.0.2.1 siaddr=(no) sname=(empty) file=(empty) key=(empty) "
+ "ipv6_reservations=(none)",
+ hosts[0]->toText());
+
+ // Remove hostname from host.
+ host->setHostname("");
+ EXPECT_NO_THROW(hdsptr_->update(host));
+
+ // The change should be reflected in the data source.
+ hosts = hdsptr_->getAll4(v4_subnet);
+ EXPECT_EQ(1, hosts.size());
+ EXPECT_EQ("hwaddr=" + hwaddr + " ipv4_subnet_id=" + to_string(v4_subnet) +
+ " ipv6_subnet_id=" + to_string(v6_subnet) +
+ " hostname=(empty) "
+ "ipv4_reservation=192.0.2.1 siaddr=(no) sname=(empty) file=(empty) key=(empty) "
+ "ipv6_reservations=(none)",
+ hosts[0]->toText());
+}
+
} // namespace test
} // namespace dhcp
} // namespace isc
-// Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2015-2023 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
/// Uses gtest macros to report failures.
void testMultipleHosts6();
+ /// @brief Tests that hosts can be updated.
+ ///
+ /// Uses gtest macros to report failures.
+ void testUpdate();
+
/// @brief Returns DUID with identical content as specified HW address
///
/// This method does not have any sense in real life and is only useful
return (false);
}
+void
+MemHostDataSource::update(HostPtr const& host) {
+ for (auto h = store_.begin(); h != store_.end(); ++h) {
+ if ((*h)->getHostId() == host->getHostId()) {
+ store_.erase(h);
+ break;
+ }
+ }
+ store_.push_back(host);
+}
+
size_t
MemHostDataSource::size() const {
return (store_.size());
const Host::IdentifierType& identifier_type,
const uint8_t* identifier_begin, const size_t identifier_len);
+ /// @brief Implements @ref BaseHostDataSource::update() for memory hosts.
+ void update(HostPtr const& host);
+
/// @brief Return backend type
///
/// Returns the type of the backend (e.g. "mysql", "memfile" etc.)