From: Marcin Siodelski Date: Wed, 10 Jul 2019 11:40:40 +0000 (+0200) Subject: [#717,!417] Added support for deleting unassigned subnets. X-Git-Tag: Kea-1.6.0-beta2~70 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ce12414d2d87898db8a674d8b754281630e0d327;p=thirdparty%2Fkea.git [#717,!417] Added support for deleting unassigned subnets. --- diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index 8e652bd40e..f0eafbd764 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -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); diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc index 276d0ba94e..b11ea9dd96 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc @@ -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); diff --git a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h index eb99233768..bb823d539e 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h +++ b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h @@ -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 diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc index 4d9d2a39a9..f1f64a1cc5 100644 --- a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc @@ -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()); ); diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc index 1de78367a3..852de650b7 100644 --- a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc @@ -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