]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#717,!417] Added support for deleting unassigned subnets.
authorMarcin Siodelski <marcin@isc.org>
Wed, 10 Jul 2019 11:40:40 +0000 (13:40 +0200)
committerMarcin Siodelski <marcin@isc.org>
Fri, 12 Jul 2019 15:58:16 +0000 (11:58 -0400)
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc
src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc

index 8e652bd40e7ddf6392cea50a8646bbb1a065d080..f0eafbd764c5a02198c6d6b02f1108df9f27ed5c 100644 (file)
@@ -120,6 +120,7 @@ public:
         DELETE_SUBNET4_PREFIX_WITH_TAG,
         DELETE_SUBNET4_PREFIX_ANY,
         DELETE_ALL_SUBNETS4,
+        DELETE_ALL_SUBNETS4_UNASSIGNED,
         DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME,
         DELETE_SUBNET4_SERVER,
         DELETE_POOLS4_SUBNET_ID,
@@ -2465,6 +2466,11 @@ TaggedStatementArray tagged_statements = { {
       MYSQL_DELETE_SUBNET_WITH_TAG(dhcp4)
     },
 
+    // Delete all unassigned subnets.
+    { MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_UNASSIGNED,
+      MYSQL_DELETE_SUBNET_UNASSIGNED(dhcp4)
+    },
+
     // Delete all subnets for a shared network.
     { MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME,
       MYSQL_DELETE_SUBNET_WITH_TAG(dhcp4, AND s.shared_network_name = ?)
@@ -2910,8 +2916,11 @@ MySqlConfigBackendDHCPv4::deleteSubnet4(const ServerSelector& server_selector,
 uint64_t
 MySqlConfigBackendDHCPv4::deleteAllSubnets4(const ServerSelector& server_selector) {
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_ALL_SUBNETS4);
-    uint64_t result = impl_->deleteTransactional(MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4,
-                                                 server_selector, "deleting all subnets",
+
+    int index = (server_selector.amUnassigned() ?
+                 MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_UNASSIGNED :
+                 MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4);
+    uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all subnets",
                                                  "deleted all subnets", true);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_ALL_SUBNETS4_RESULT)
         .arg(result);
index 276d0ba94e9f5d6d1289c9ffd508decc9f33e17f..b11ea9dd967695951cd1129ac35e764ecbed51d4 100644 (file)
@@ -125,6 +125,7 @@ public:
         DELETE_SUBNET6_PREFIX_WITH_TAG,
         DELETE_SUBNET6_PREFIX_ANY,
         DELETE_ALL_SUBNETS6,
+        DELETE_ALL_SUBNETS6_UNASSIGNED,
         DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME,
         DELETE_SUBNET6_SERVER,
         DELETE_POOLS6_SUBNET_ID,
@@ -2823,6 +2824,11 @@ TaggedStatementArray tagged_statements = { {
       MYSQL_DELETE_SUBNET_WITH_TAG(dhcp6)
     },
 
+    // Delete all unassigned subnets.
+    { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_UNASSIGNED,
+      MYSQL_DELETE_SUBNET_UNASSIGNED(dhcp6)
+    },
+
     // Delete all subnets for a shared network.
     { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME,
       MYSQL_DELETE_SUBNET_WITH_TAG(dhcp6, AND s.shared_network_name = ?)
@@ -3289,8 +3295,11 @@ MySqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector,
 uint64_t
 MySqlConfigBackendDHCPv6::deleteAllSubnets6(const ServerSelector& server_selector) {
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_ALL_SUBNETS6);
-    uint64_t result = impl_->deleteTransactional(MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6,
-                                                 server_selector, "deleting all subnets",
+
+    int index = (server_selector.amUnassigned() ?
+                 MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_UNASSIGNED :
+                 MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6);
+    uint64_t result = impl_->deleteTransactional(index, server_selector, "deleting all subnets",
                                                  "deleted all subnets", true);
     LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_ALL_SUBNETS6_RESULT)
         .arg(result);
index eb99233768f7a3673086cac020c6856d4785afc1..bb823d539ee4303f21521101932a1ec211a07aea 100644 (file)
@@ -726,6 +726,12 @@ namespace {
     "DELETE s FROM " #table_prefix "_subnet AS s " \
     #__VA_ARGS__
 
+#define MYSQL_DELETE_SUBNET_UNASSIGNED(table_prefix, ...) \
+    "DELETE s FROM " #table_prefix "_subnet AS s " \
+    "LEFT JOIN " #table_prefix "_subnet_server AS a" \
+    "  ON s.subnet_id = a.subnet_id " \
+    "WHERE a.subnet_id IS NULL " #__VA_ARGS__
+
 #endif
 
 #ifndef MYSQL_DELETE_SUBNET_SERVER
index 4d9d2a39a9c18156d644932082be3466a4bba5d6..f1f64a1cc5bc97c41e6d78ec9eb2f3aab558faf2 100644 (file)
@@ -1536,6 +1536,104 @@ TEST_F(MySqlConfigBackendDHCPv4Test, deleteSubnet4) {
     test_delete_by_prefix("one server", ServerSelector::ONE("server1"), subnet3);
 }
 
+// Test that it is possible to retrieve and delete orphaned subnet.
+TEST_F(MySqlConfigBackendDHCPv4Test, unassignedSubnet4) {
+    // Create the server.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[0]));
+
+    // Create the shared networks and associate them with the server1.
+    auto subnet = test_subnets_[0];
+    auto subnet2 = test_subnets_[2];
+    EXPECT_NO_THROW(
+        cbptr_->createUpdateSubnet4(ServerSelector::ONE("server1"), subnet)
+    );
+    EXPECT_NO_THROW(
+        cbptr_->createUpdateSubnet4(ServerSelector::ONE("server1"), subnet2)
+    );
+
+    // Delete the server. The subnets should be preserved but are considered orphaned,
+    // i.e. do not belong to any server.
+    uint64_t deleted_count = 0;
+    EXPECT_NO_THROW(deleted_count = cbptr_->deleteServer4(ServerTag("server1")));
+    EXPECT_EQ(1, deleted_count);
+
+    // Trying to fetch the subnet by server tag should return no result.
+    Subnet4Ptr returned_subnet;
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server1"),
+                                                         subnet->getID()));
+    EXPECT_FALSE(returned_subnet);
+
+    // The same if we use other calls.
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet4(ServerSelector::ONE("server1"),
+                                                         subnet->toText()));
+    EXPECT_FALSE(returned_subnet);
+
+    Subnet4Collection returned_subnets;
+    EXPECT_NO_THROW(returned_subnets = cbptr_->getAllSubnets4(ServerSelector::ONE("server1")));
+    EXPECT_TRUE(returned_subnets.empty());
+
+    EXPECT_NO_THROW(
+        returned_subnets = cbptr_->getModifiedSubnets4(ServerSelector::ONE("server1"),
+                                                       timestamps_["two days ago"])
+    );
+    EXPECT_TRUE(returned_subnets.empty());
+
+    // We should get the subnet if we ask for unassigned.
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet4(ServerSelector::UNASSIGNED(),
+                                                         subnet->getID()));
+    ASSERT_TRUE(returned_subnet);
+
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet4(ServerSelector::UNASSIGNED(),
+                                                         subnet->toText()));
+    ASSERT_TRUE(returned_subnet);
+
+    // Also if we ask for all unassigned subnets it should be returned.
+    EXPECT_NO_THROW(returned_subnets = cbptr_->getAllSubnets4(ServerSelector::UNASSIGNED()));
+    ASSERT_EQ(2, returned_subnets.size());
+
+    // Same for modified subnets.
+    EXPECT_NO_THROW(
+        returned_subnets = cbptr_->getModifiedSubnets4(ServerSelector::UNASSIGNED(),
+                                                       timestamps_["two days ago"])
+    );
+    ASSERT_EQ(2, returned_subnets.size());
+
+    // If we ask for any subnet by subnet id, it should be returned too.
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet4(ServerSelector::ANY(),
+                                                         subnet->getID()));
+    ASSERT_TRUE(returned_subnet);
+
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet4(ServerSelector::ANY(),
+                                                         subnet->toText()));
+    ASSERT_TRUE(returned_subnet);
+
+    // Deleting the subnet with the mismatched server tag should not affect our
+    // subnet.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteSubnet4(ServerSelector::ONE("server1"),
+                                              subnet->getID())
+    );
+    EXPECT_EQ(0, deleted_count);
+
+    // Also, if we delete all subnets for server1.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteAllSubnets4(ServerSelector::ONE("server1"))
+    );
+    EXPECT_EQ(0, deleted_count);
+
+    // We can delete this subnet when we specify ANY and the matching id.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteSubnet4(ServerSelector::ANY(), subnet->getID())
+    );
+    EXPECT_EQ(1, deleted_count);
+
+    // We can delete all subnets using UNASSIGNED selector.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteAllSubnets4(ServerSelector::UNASSIGNED());
+    );
+    EXPECT_EQ(1, deleted_count);
+}
+
 // Test that subnets modified after given time can be fetched.
 TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedSubnets4) {
     // Explicitly set timestamps of subnets. First subnet has a timestamp
@@ -2383,7 +2481,7 @@ TEST_F(MySqlConfigBackendDHCPv4Test, unassignedSharedNetwork) {
     );
     EXPECT_EQ(1, deleted_count);
 
-    // We can delete all second networks using UNASSIGNED selector.
+    // We can delete all networks using UNASSIGNED selector.
     EXPECT_NO_THROW(
         deleted_count = cbptr_->deleteAllSharedNetworks4(ServerSelector::UNASSIGNED());
     );
index 1de78367a39c9aac078303b7d977ab0fc0c6160c..852de650b744bdda45fef84c7cdfad83007cc10a 100644 (file)
@@ -1554,6 +1554,104 @@ TEST_F(MySqlConfigBackendDHCPv6Test, deleteSubnet6) {
     test_delete_by_prefix("one server", ServerSelector::ONE("server1"), subnet3);
 }
 
+// Test that it is possible to retrieve and delete orphaned subnet.
+TEST_F(MySqlConfigBackendDHCPv6Test, unassignedSubnet6) {
+    // Create the server.
+    EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0]));
+
+    // Create the shared networks and associate them with the server1.
+    auto subnet = test_subnets_[0];
+    auto subnet2 = test_subnets_[2];
+    EXPECT_NO_THROW(
+        cbptr_->createUpdateSubnet6(ServerSelector::ONE("server1"), subnet)
+    );
+    EXPECT_NO_THROW(
+        cbptr_->createUpdateSubnet6(ServerSelector::ONE("server1"), subnet2)
+    );
+
+    // Delete the server. The subnets should be preserved but are considered orphaned,
+    // i.e. do not belong to any server.
+    uint64_t deleted_count = 0;
+    EXPECT_NO_THROW(deleted_count = cbptr_->deleteServer6(ServerTag("server1")));
+    EXPECT_EQ(1, deleted_count);
+
+    // Trying to fetch the subnet by server tag should return no result.
+    Subnet6Ptr returned_subnet;
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
+                                                         subnet->getID()));
+    EXPECT_FALSE(returned_subnet);
+
+    // The same if we use other calls.
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ONE("server1"),
+                                                         subnet->toText()));
+    EXPECT_FALSE(returned_subnet);
+
+    Subnet6Collection returned_subnets;
+    EXPECT_NO_THROW(returned_subnets = cbptr_->getAllSubnets6(ServerSelector::ONE("server1")));
+    EXPECT_TRUE(returned_subnets.empty());
+
+    EXPECT_NO_THROW(
+        returned_subnets = cbptr_->getModifiedSubnets6(ServerSelector::ONE("server1"),
+                                                       timestamps_["two days ago"])
+    );
+    EXPECT_TRUE(returned_subnets.empty());
+
+    // We should get the subnet if we ask for unassigned.
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::UNASSIGNED(),
+                                                         subnet->getID()));
+    ASSERT_TRUE(returned_subnet);
+
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::UNASSIGNED(),
+                                                         subnet->toText()));
+    ASSERT_TRUE(returned_subnet);
+
+    // Also if we ask for all unassigned subnets it should be returned.
+    EXPECT_NO_THROW(returned_subnets = cbptr_->getAllSubnets6(ServerSelector::UNASSIGNED()));
+    ASSERT_EQ(2, returned_subnets.size());
+
+    // Same for modified subnets.
+    EXPECT_NO_THROW(
+        returned_subnets = cbptr_->getModifiedSubnets6(ServerSelector::UNASSIGNED(),
+                                                       timestamps_["two days ago"])
+    );
+    ASSERT_EQ(2, returned_subnets.size());
+
+    // If we ask for any subnet by subnet id, it should be returned too.
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ANY(),
+                                                         subnet->getID()));
+    ASSERT_TRUE(returned_subnet);
+
+    EXPECT_NO_THROW(returned_subnet = cbptr_->getSubnet6(ServerSelector::ANY(),
+                                                         subnet->toText()));
+    ASSERT_TRUE(returned_subnet);
+
+    // Deleting the subnet with the mismatched server tag should not affect our
+    // subnet.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteSubnet6(ServerSelector::ONE("server1"),
+                                              subnet->getID())
+    );
+    EXPECT_EQ(0, deleted_count);
+
+    // Also, if we delete all subnets for server1.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteAllSubnets6(ServerSelector::ONE("server1"))
+    );
+    EXPECT_EQ(0, deleted_count);
+
+    // We can delete this subnet when we specify ANY and the matching id.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteSubnet6(ServerSelector::ANY(), subnet->getID())
+    );
+    EXPECT_EQ(1, deleted_count);
+
+    // We can delete all subnets using UNASSIGNED selector.
+    EXPECT_NO_THROW(
+        deleted_count = cbptr_->deleteAllSubnets6(ServerSelector::UNASSIGNED());
+    );
+    EXPECT_EQ(1, deleted_count);
+}
+
 // Test that subnets modified after given time can be fetched.
 TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSubnets6) {
     // Explicitly set timestamps of subnets. First subnet has a timestamp