]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#549] add tests for reservation-update
authorAndrei Pavel <andrei@isc.org>
Wed, 5 Apr 2023 11:39:28 +0000 (14:39 +0300)
committerAndrei Pavel <andrei@isc.org>
Wed, 19 Apr 2023 20:56:00 +0000 (23:56 +0300)
src/lib/dhcpsrv/tests/cfg_hosts_unittest.cc
src/lib/dhcpsrv/tests/host_cache_unittest.cc
src/lib/dhcpsrv/tests/host_mgr_unittest.cc
src/lib/dhcpsrv/tests/mysql_host_data_source_unittest.cc
src/lib/dhcpsrv/tests/pgsql_host_data_source_unittest.cc
src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.cc
src/lib/dhcpsrv/testutils/generic_host_data_source_unittest.h
src/lib/dhcpsrv/testutils/memory_host_data_source.cc
src/lib/dhcpsrv/testutils/memory_host_data_source.h

index a4cb460892197a26b11186adb42b1a45a88aba6c..b79790ec7ec9264432e5f403c2dee544c5a7077c 100644 (file)
@@ -5,6 +5,7 @@
 // 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>
 
@@ -1200,5 +1204,69 @@ TEST_F(CfgHostsTest, duplicatesSubnet6DUID) {
                                              "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
index c6ad92009845c7a3a9e7c12097a24e9ce09fdf7f..516c0f4e99b0d42cd9805c2653799fb712e919ac 100644 (file)
@@ -698,6 +698,9 @@ public:
         return (false);
     }
 
+    void update(HostPtr const&) {
+    }
+
     std::string getType() const {
         return ("one");
     }
index 5b19af0bb153bca73889f9af96f9c1d3fea4a9b3..a1b3647fd2d35044145d325cc8a5dbed1904a161 100644 (file)
@@ -5,6 +5,7 @@
 // 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;
@@ -21,7 +24,6 @@ using namespace isc::db;
 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.
@@ -152,7 +154,7 @@ TEST_F(HostMgrTest, get6ByPrefix) {
 }
 
 // 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.
@@ -160,7 +162,11 @@ TEST_F(HostMgrTest, addNoDataSource) {
 
     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
index 73f602a5873acd727afaa593112c42d4bf807ebd..ab4924a554041851edb31bf875dabfc7842afe33 100644 (file)
@@ -1,4 +1,4 @@
-// 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
@@ -1467,6 +1467,17 @@ TEST_F(MySqlHostDataSourceTest, testMultipleHosts6MultiThreading) {
     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 {
index 2c707f210584048af9481cc1ac8ebb211729308f..cedcfcd5baefe9ca28cd685377156387470bc0c2 100644 (file)
@@ -1,4 +1,4 @@
-// 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
@@ -1435,6 +1435,16 @@ TEST_F(PgSqlHostDataSourceTest, testMultipleHosts6MultiThreading) {
     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 {
index ed553ae651c3457eb9ca2abf5ecd8466d4faf92d..0c1fa3de320c54b422d8b896d2bb4888771797ba 100644 (file)
 #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;
@@ -3892,6 +3891,81 @@ HostMgrTest::testGetAll6BySubnetIP(BaseHostDataSource& data_source1,
                 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
index 0eb1ddfe0cafa6c504668041434e57ce2e66d0eb..c884776b83c9b848f80c9e02ff66dfbac1038d02 100644 (file)
@@ -1,4 +1,4 @@
-// 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
@@ -513,6 +513,11 @@ public:
     /// 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
index 3ea2b830c59672cc5a677497724b68639ff48b9d..89b6e2bf2616d62f0d78949d4659aa5265c7c218 100644 (file)
@@ -375,6 +375,17 @@ MemHostDataSource::del6(const SubnetID& subnet_id,
     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());
index 799ab08ea28e5938ca97d284834d9c5122043030..bf8f6a35e6524b4ab86591da53988e70c7e3fcfc 100644 (file)
@@ -257,6 +257,9 @@ public:
                       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.)