From: Marcin Siodelski Date: Wed, 10 Jul 2019 10:29:49 +0000 (+0200) Subject: [#717,!417] Added support to delete a subnet with ANY server selector. X-Git-Tag: Kea-1.6.0-beta2~71 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e5e5a0b0ca665c981c492a3e945d2b15c3759fa;p=thirdparty%2Fkea.git [#717,!417] Added support to delete a subnet with ANY server selector. --- diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index d12a8d125d..8e652bd40e 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -115,8 +115,10 @@ public: DELETE_GLOBAL_PARAMETER4, DELETE_ALL_GLOBAL_PARAMETERS4, DELETE_ALL_GLOBAL_PARAMETERS4_UNASSIGNED, - DELETE_SUBNET4_ID, - DELETE_SUBNET4_PREFIX, + DELETE_SUBNET4_ID_WITH_TAG, + DELETE_SUBNET4_ID_ANY, + DELETE_SUBNET4_PREFIX_WITH_TAG, + DELETE_SUBNET4_PREFIX_ANY, DELETE_ALL_SUBNETS4, DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME, DELETE_SUBNET4_SERVER, @@ -1003,11 +1005,27 @@ public: /// @return Number of deleted subnets. uint64_t deleteSubnet4(const ServerSelector& server_selector, const SubnetID& subnet_id) { - return (deleteTransactional(DELETE_SUBNET4_ID, server_selector, - "deleting a subnet", - "subnet deleted", - true, - static_cast(subnet_id))); + int index = (server_selector.amAny() ? + MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID_ANY : + MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID_WITH_TAG); + return (deleteTransactional(index, server_selector, + "deleting a subnet", "subnet deleted", + true, static_cast(subnet_id))); + } + + /// @brief Sends query to delete subnet by id. + /// + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be deleted. + /// @return Number of deleted subnets. + uint64_t deleteSubnet4(const ServerSelector& server_selector, + const std::string& subnet_prefix) { + int index = (server_selector.amAny() ? + MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX_ANY : + MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX_WITH_TAG); + return (deleteTransactional(index, server_selector, + "deleting a subnet", "subnet deleted", + true, subnet_prefix)); } /// @brief Deletes pools belonging to a subnet from the database. @@ -2422,24 +2440,34 @@ TaggedStatementArray tagged_statements = { { MYSQL_DELETE_GLOBAL_PARAMETER_UNASSIGNED(dhcp4) }, - // Delete subnet by id. - { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID, - MYSQL_DELETE_SUBNET(dhcp4, AND s.subnet_id = ?) + // Delete subnet by id with specifying server tag. + { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID_WITH_TAG, + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp4, AND s.subnet_id = ?) + }, + + // Delete subnet by id without specifying server tag. + { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_ID_ANY, + MYSQL_DELETE_SUBNET_ANY(dhcp4, WHERE s.subnet_id = ?) + }, + + // Delete subnet by prefix with specifying server tag. + { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX_WITH_TAG, + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp4, AND s.subnet_prefix = ?) }, - // Delete subnet by prefix. - { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX, - MYSQL_DELETE_SUBNET(dhcp4, AND s.subnet_prefix = ?) + // Delete subnet by prefix without specifying server tag. + { MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX_ANY, + MYSQL_DELETE_SUBNET_ANY(dhcp4, WHERE s.subnet_prefix = ?) }, // Delete all subnets. { MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4, - MYSQL_DELETE_SUBNET(dhcp4) + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp4) }, // Delete all subnets for a shared network. { MySqlConfigBackendDHCPv4Impl::DELETE_ALL_SUBNETS4_SHARED_NETWORK_NAME, - MYSQL_DELETE_SUBNET(dhcp4, AND s.shared_network_name = ?) + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp4, AND s.shared_network_name = ?) }, // Delete associations of a subnet with server. @@ -2862,9 +2890,7 @@ MySqlConfigBackendDHCPv4::deleteSubnet4(const ServerSelector& server_selector, const std::string& subnet_prefix) { LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_PREFIX_SUBNET4) .arg(subnet_prefix); - uint64_t result = impl_->deleteTransactional(MySqlConfigBackendDHCPv4Impl::DELETE_SUBNET4_PREFIX, - server_selector, "deleting a subnet by prefix", - "subnet deleted", true, subnet_prefix); + uint64_t result = impl_->deleteSubnet4(server_selector, subnet_prefix); LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_PREFIX_SUBNET4_RESULT) .arg(result); return (result); diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc index 53444d2e5d..276d0ba94e 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc @@ -120,8 +120,10 @@ public: DELETE_GLOBAL_PARAMETER6, DELETE_ALL_GLOBAL_PARAMETERS6, DELETE_ALL_GLOBAL_PARAMETERS6_UNASSIGNED, - DELETE_SUBNET6_ID, - DELETE_SUBNET6_PREFIX, + DELETE_SUBNET6_ID_WITH_TAG, + DELETE_SUBNET6_ID_ANY, + DELETE_SUBNET6_PREFIX_WITH_TAG, + DELETE_SUBNET6_PREFIX_ANY, DELETE_ALL_SUBNETS6, DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME, DELETE_SUBNET6_SERVER, @@ -1190,11 +1192,27 @@ public: /// @return Number of deleted subnets. uint64_t deleteSubnet6(const ServerSelector& server_selector, const SubnetID& subnet_id) { - return (deleteTransactional(DELETE_SUBNET6_ID, server_selector, - "deleting a subnet", - "subnet deleted", - true, - static_cast(subnet_id))); + int index = (server_selector.amAny() ? + MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_ANY : + MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_WITH_TAG); + return (deleteTransactional(index, server_selector, + "deleting a subnet", "subnet deleted", + true, static_cast(subnet_id))); + } + + /// @brief Sends query to delete subnet by id. + /// + /// @param server_selector Server selector. + /// @param subnet_prefix Prefix of the subnet to be deleted. + /// @return Number of deleted subnets. + uint64_t deleteSubnet6(const ServerSelector& server_selector, + const std::string& subnet_prefix) { + int index = (server_selector.amAny() ? + MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_ANY : + MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_WITH_TAG); + return (deleteTransactional(index, server_selector, + "deleting a subnet", "subnet deleted", + true, subnet_prefix)); } /// @brief Deletes pools belonging to a subnet from the database. @@ -2780,24 +2798,34 @@ TaggedStatementArray tagged_statements = { { MYSQL_DELETE_GLOBAL_PARAMETER(dhcp6) }, - // Delete subnet by id. - { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID, - MYSQL_DELETE_SUBNET(dhcp6, AND s.subnet_id = ?) + // Delete subnet by id with specifying server tag. + { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_WITH_TAG, + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp6, AND s.subnet_id = ?) + }, + + // Delete subnet by id without specifying server tag. + { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_ID_ANY, + MYSQL_DELETE_SUBNET_ANY(dhcp6, WHERE s.subnet_id = ?) + }, + + // Delete subnet by prefix with specifying server tag. + { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_WITH_TAG, + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp6, AND s.subnet_prefix = ?) }, - // Delete subnet by prefix. - { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX, - MYSQL_DELETE_SUBNET(dhcp6, AND s.subnet_prefix = ?) + // Delete subnet by prefix without specifying server tag. + { MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX_ANY, + MYSQL_DELETE_SUBNET_ANY(dhcp6, WHERE s.subnet_prefix = ?) }, // Delete all subnets. { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6, - MYSQL_DELETE_SUBNET(dhcp6) + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp6) }, // Delete all subnets for a shared network. { MySqlConfigBackendDHCPv6Impl::DELETE_ALL_SUBNETS6_SHARED_NETWORK_NAME, - MYSQL_DELETE_SUBNET(dhcp6, AND s.shared_network_name = ?) + MYSQL_DELETE_SUBNET_WITH_TAG(dhcp6, AND s.shared_network_name = ?) }, // Delete associations of a subnet with server. @@ -3241,9 +3269,7 @@ MySqlConfigBackendDHCPv6::deleteSubnet6(const ServerSelector& server_selector, const std::string& subnet_prefix) { LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_PREFIX_SUBNET6) .arg(subnet_prefix); - uint64_t result = impl_->deleteTransactional(MySqlConfigBackendDHCPv6Impl::DELETE_SUBNET6_PREFIX, - server_selector, "deleting a subnet by prefix", - "subnet deleted", true, subnet_prefix); + uint64_t result = impl_->deleteSubnet6(server_selector, subnet_prefix); LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_DELETE_BY_PREFIX_SUBNET6_RESULT) .arg(result); return (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 f00cb778bd..eb99233768 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h +++ b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h @@ -711,13 +711,21 @@ namespace { #endif #ifndef MYSQL_DELETE_SUBNET -#define MYSQL_DELETE_SUBNET(table_prefix, ...) \ +#define MYSQL_DELETE_SUBNET_COMMON(table_prefix, ...) \ "DELETE s FROM " #table_prefix "_subnet AS s " \ "INNER JOIN " #table_prefix "_subnet_server AS a " \ " ON s.subnet_id = a.subnet_id " \ "INNER JOIN " #table_prefix "_server AS srv" \ " ON a.server_id = srv.id " \ - "WHERE srv.tag = ? " #__VA_ARGS__ + #__VA_ARGS__ + +#define MYSQL_DELETE_SUBNET_WITH_TAG(table_prefix, ...) \ + MYSQL_DELETE_SUBNET_COMMON(table_prefix, WHERE srv.tag = ? __VA_ARGS__) + +#define MYSQL_DELETE_SUBNET_ANY(table_prefix, ...) \ + "DELETE s FROM " #table_prefix "_subnet AS s " \ + #__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 ac99fe277f..4d9d2a39a9 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 @@ -1428,6 +1428,114 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAllSubnets4WithServerTags) { EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server2"))); } +// Test that selected subnet can be deleted. +TEST_F(MySqlConfigBackendDHCPv4Test, deleteSubnet4) { + // Create two servers in the database. + EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[0])); + { + SCOPED_TRACE("CREATE audit entry for server"); + testNewAuditEntry("dhcp4_server", + AuditEntry::ModificationType::CREATE, + "server set"); + } + + EXPECT_NO_THROW(cbptr_->createUpdateServer4(test_servers_[2])); + { + SCOPED_TRACE("CREATE audit entry for server"); + testNewAuditEntry("dhcp4_server", + AuditEntry::ModificationType::CREATE, + "server set"); + } + + auto subnet1 = test_subnets_[0]; + auto subnet2 = test_subnets_[2]; + auto subnet3 = test_subnets_[3]; + + auto create_test_subnets = [&] () { + // Insert three subnets, one for all servers, one for server2 and one for two + // servers: server1 and server2. + EXPECT_NO_THROW( + cbptr_->createUpdateSubnet4(ServerSelector::ALL(), subnet1) + ); + EXPECT_NO_THROW( + cbptr_->createUpdateSubnet4(ServerSelector::ONE("server2"), subnet2) + ); + EXPECT_NO_THROW( + cbptr_->createUpdateSubnet4(ServerSelector::MULTIPLE({ "server1", "server2" }), + subnet3) + ); + }; + + create_test_subnets(); + + // Test that subnet is not deleted for a specified server selector. + auto test_no_delete = [this] (const std::string& test_case_name, + const ServerSelector& server_selector, + const Subnet4Ptr& subnet) { + SCOPED_TRACE(test_case_name); + uint64_t deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet4(server_selector, subnet->getID()) + ); + EXPECT_EQ(0, deleted_count); + + deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet4(server_selector, subnet->toText()) + ); + EXPECT_EQ(0, deleted_count); + }; + + { + SCOPED_TRACE("Test valid but non matching server selectors"); + test_no_delete("selector: one, actual: all", ServerSelector::ONE("server2"), + subnet1); + test_no_delete("selector: all, actual: one", ServerSelector::ALL(), + subnet2); + test_no_delete("selector: all, actual: multiple", ServerSelector::ALL(), + subnet3); + } + + // Test successful deletion of a subnet by ID. + auto test_delete_by_id = [this] (const std::string& test_case_name, + const ServerSelector& server_selector, + const Subnet4Ptr& subnet) { + SCOPED_TRACE(test_case_name); + uint64_t deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet4(server_selector, subnet->getID()) + ); + EXPECT_EQ(1, deleted_count); + + EXPECT_FALSE(cbptr_->getSubnet4(server_selector, subnet->getID())); + }; + + test_delete_by_id("all servers", ServerSelector::ALL(), subnet1); + test_delete_by_id("any server", ServerSelector::ANY(), subnet2); + test_delete_by_id("one server", ServerSelector::ONE("server1"), subnet3); + + // Re-create deleted subnets. + create_test_subnets(); + + // Test successful deletion of a subnet by prefix. + auto test_delete_by_prefix = [this] (const std::string& test_case_name, + const ServerSelector& server_selector, + const Subnet4Ptr& subnet) { + SCOPED_TRACE(test_case_name); + uint64_t deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet4(server_selector, subnet->toText()) + ); + EXPECT_EQ(1, deleted_count); + + EXPECT_FALSE(cbptr_->getSubnet4(server_selector, subnet->toText())); + }; + + test_delete_by_prefix("all servers", ServerSelector::ALL(), subnet1); + test_delete_by_prefix("any server", ServerSelector::ANY(), subnet2); + test_delete_by_prefix("one server", ServerSelector::ONE("server1"), subnet3); +} + // Test that subnets modified after given time can be fetched. TEST_F(MySqlConfigBackendDHCPv4Test, getModifiedSubnets4) { // Explicitly set timestamps of subnets. First subnet has a timestamp @@ -2094,7 +2202,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, deleteSharedNetwork4) { auto shared_network2 = test_networks_[2]; auto shared_network3 = test_networks_[3]; - // Insert two shared networks, one for all servers, and one for server2. + // Insert three shared networks, one for all servers, one for server2 and + // one for two servers: server1 and server2. EXPECT_NO_THROW( cbptr_->createUpdateSharedNetwork4(ServerSelector::ALL(), shared_network1) ); 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 0d2999a4c4..1de78367a3 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 @@ -1446,6 +1446,114 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getAllSubnets6WithServerTags) { EXPECT_FALSE(returned_subnet->hasServerTag(ServerTag("server2"))); } +// Test that selected subnet can be deleted. +TEST_F(MySqlConfigBackendDHCPv6Test, deleteSubnet6) { + // Create two servers in the database. + EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[0])); + { + SCOPED_TRACE("CREATE audit entry for server"); + testNewAuditEntry("dhcp6_server", + AuditEntry::ModificationType::CREATE, + "server set"); + } + + EXPECT_NO_THROW(cbptr_->createUpdateServer6(test_servers_[2])); + { + SCOPED_TRACE("CREATE audit entry for server"); + testNewAuditEntry("dhcp6_server", + AuditEntry::ModificationType::CREATE, + "server set"); + } + + auto subnet1 = test_subnets_[0]; + auto subnet2 = test_subnets_[2]; + auto subnet3 = test_subnets_[3]; + + auto create_test_subnets = [&] () { + // Insert three subnets, one for all servers, one for server2 and one for two + // servers: server1 and server2. + EXPECT_NO_THROW( + cbptr_->createUpdateSubnet6(ServerSelector::ALL(), subnet1) + ); + EXPECT_NO_THROW( + cbptr_->createUpdateSubnet6(ServerSelector::ONE("server2"), subnet2) + ); + EXPECT_NO_THROW( + cbptr_->createUpdateSubnet6(ServerSelector::MULTIPLE({ "server1", "server2" }), + subnet3) + ); + }; + + create_test_subnets(); + + // Test that subnet is not deleted for a specified server selector. + auto test_no_delete = [this] (const std::string& test_case_name, + const ServerSelector& server_selector, + const Subnet6Ptr& subnet) { + SCOPED_TRACE(test_case_name); + uint64_t deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->getID()) + ); + EXPECT_EQ(0, deleted_count); + + deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->toText()) + ); + EXPECT_EQ(0, deleted_count); + }; + + { + SCOPED_TRACE("Test valid but non matching server selectors"); + test_no_delete("selector: one, actual: all", ServerSelector::ONE("server2"), + subnet1); + test_no_delete("selector: all, actual: one", ServerSelector::ALL(), + subnet2); + test_no_delete("selector: all, actual: multiple", ServerSelector::ALL(), + subnet3); + } + + // Test successful deletion of a subnet by ID. + auto test_delete_by_id = [this] (const std::string& test_case_name, + const ServerSelector& server_selector, + const Subnet6Ptr& subnet) { + SCOPED_TRACE(test_case_name); + uint64_t deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->getID()) + ); + EXPECT_EQ(1, deleted_count); + + EXPECT_FALSE(cbptr_->getSubnet6(server_selector, subnet->getID())); + }; + + test_delete_by_id("all servers", ServerSelector::ALL(), subnet1); + test_delete_by_id("any server", ServerSelector::ANY(), subnet2); + test_delete_by_id("one server", ServerSelector::ONE("server1"), subnet3); + + // Re-create deleted subnets. + create_test_subnets(); + + // Test successful deletion of a subnet by prefix. + auto test_delete_by_prefix = [this] (const std::string& test_case_name, + const ServerSelector& server_selector, + const Subnet6Ptr& subnet) { + SCOPED_TRACE(test_case_name); + uint64_t deleted_count = 0; + EXPECT_NO_THROW( + deleted_count = cbptr_->deleteSubnet6(server_selector, subnet->toText()) + ); + EXPECT_EQ(1, deleted_count); + + EXPECT_FALSE(cbptr_->getSubnet6(server_selector, subnet->toText())); + }; + + test_delete_by_prefix("all servers", ServerSelector::ALL(), subnet1); + test_delete_by_prefix("any server", ServerSelector::ANY(), subnet2); + test_delete_by_prefix("one server", ServerSelector::ONE("server1"), subnet3); +} + // Test that subnets modified after given time can be fetched. TEST_F(MySqlConfigBackendDHCPv6Test, getModifiedSubnets6) { // Explicitly set timestamps of subnets. First subnet has a timestamp