]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#680,!426] Delete embedded options when subnet gets deleted in mysql_cb.
authorMarcin Siodelski <marcin@isc.org>
Fri, 19 Jul 2019 09:12:25 +0000 (11:12 +0200)
committerMarcin Siodelski <marcin@isc.org>
Thu, 25 Jul 2019 07:58:11 +0000 (03:58 -0400)
configure.ac
src/bin/admin/tests/mysql_tests.sh.in
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.h
src/hooks/dhcp/mysql_cb/mysql_cb_impl.h
src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc
src/lib/mysql/mysql_constants.h
src/share/database/scripts/mysql/.gitignore
src/share/database/scripts/mysql/Makefile.am
src/share/database/scripts/mysql/dhcpdb_create.mysql
src/share/database/scripts/mysql/upgrade_8.1_to_8.2.sh.in [new file with mode: 0644]

index 56c061cdef0d0907225e9676f01633ae537004c4..d6e0a14fa679c1a0bc613647badb3da148cdad5d 100644 (file)
@@ -1722,6 +1722,7 @@ AC_CONFIG_FILES([Makefile
                  src/share/database/scripts/mysql/upgrade_6.0_to_7.0.sh
                  src/share/database/scripts/mysql/upgrade_7.0_to_8.0.sh
                  src/share/database/scripts/mysql/upgrade_8.0_to_8.1.sh
+                 src/share/database/scripts/mysql/upgrade_8.1_to_8.2.sh
                  src/share/database/scripts/mysql/wipe_data.sh
                  src/share/database/scripts/pgsql/Makefile
                  src/share/database/scripts/pgsql/upgrade_1.0_to_2.0.sh
index 4adbfb8c98f20cf31cce4481e470813b2f22441a..6cf8264538765e6f961a2050b0335f088cce8fbc 100644 (file)
@@ -258,7 +258,7 @@ mysql_upgrade_test() {
 
     assert_str_eq "1.0" ${version} "Expected kea-admin to return %s, returned value was %s"
 
-    # Ok, we have a 1.0 database. Let's upgrade it to 8.1
+    # Ok, we have a 1.0 database. Let's upgrade it to 8.2
     ${keaadmin} db-upgrade mysql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir
     ERRCODE=$?
 
@@ -689,7 +689,7 @@ EOF
     qry="SELECT COUNT(*) FROM parameter_data_type";
     run_statement "parameter_data_type count" "$qry" 4;
 
-    # Schema upgrade from 8.0 to 8.1
+    # Schema upgrade from 8.0 to 8.2
 
     # New lifetime bounds.
 
@@ -709,9 +709,9 @@ EOF
     qry="select subnet_prefix, client_class, interface, modification_ts, preferred_lifetime, min_preferred_lifetime, max_preferred_lifetime, rapid_commit, rebind_timer, relay, renew_timer, require_client_classes, reservation_mode, shared_network_name, subnet_id, user_context, valid_lifetime, min_valid_lifetime, max_valid_lifetime, calculate_tee_times, t1_percent, t2_percent from dhcp6_subnet"
     run_statement "dhcp6_subnet" "$qry"
 
-    # Verify upgraded schema reports version 8.1
+    # Verify upgraded schema reports version 8.2
     version=$(${keaadmin} db-version mysql -u $db_user -p $db_password -n $db_name -d $db_scripts_dir)
-    assert_str_eq "8.1" ${version} "Expected kea-admin to return %s, returned value was %s"
+    assert_str_eq "8.2" ${version} "Expected kea-admin to return %s, returned value was %s"
 
     # Let's wipe the whole database
     mysql_wipe
index b9e3b19462d8cba1e47e4de6a54267ce4d72c95e..e9f3bb6206225508d5c366d05775e7951ecb6aa7 100644 (file)
@@ -2597,7 +2597,8 @@ MySqlConfigBackendDHCPv4Impl::MySqlConfigBackendDHCPv4Impl(const DatabaseConnect
 }
 
 MySqlConfigBackendDHCPv4::MySqlConfigBackendDHCPv4(const DatabaseConnection::ParameterMap& parameters)
-    : impl_(new MySqlConfigBackendDHCPv4Impl(parameters)) {
+    : base_impl_(new MySqlConfigBackendDHCPv4Impl(parameters)), impl_() {
+    impl_ = boost::dynamic_pointer_cast<MySqlConfigBackendDHCPv4Impl>(base_impl_);
 }
 
 Subnet4Ptr
index dfb034d02db13ae1a9718ad31033ac72895fff01..710a5d750316a92d9c365a7e7d5ff806e1c5ac84 100644 (file)
@@ -7,6 +7,7 @@
 #ifndef MYSQL_CONFIG_BACKEND_DHCP4_H
 #define MYSQL_CONFIG_BACKEND_DHCP4_H
 
+#include <mysql_cb_impl.h>
 #include <database/database_connection.h>
 #include <dhcpsrv/config_backend_dhcp4.h>
 #include <mysql_cb_log.h>
@@ -538,12 +539,15 @@ public:
     /// This should be called by the hook lib unload() function.
     static void unregisterBackendType();
 
-private:
+protected:
+
+    /// @brief Pointer to the base implementation of the backend shared by
+    /// DHCPv4 and DHCPv6 servers.
+    boost::shared_ptr<MySqlConfigBackendImpl> base_impl_;
 
     /// @brief Pointer to the implementation of the @c MySqlConfigBackendDHCPv4
     /// class.
     boost::shared_ptr<MySqlConfigBackendDHCPv4Impl> impl_;
-
 };
 
 /// @brief Pointer to the @c MySqlConfigBackendDHCPv4 class.
index 31edee4ee2e921d9e17b3f5d3613857fd43dea4d..461c7670a22fe8b0bec773c3d2f5cfec0868a66a 100644 (file)
@@ -110,7 +110,7 @@ public:
     explicit MySqlConfigBackendImpl(const db::DatabaseConnection::ParameterMap& parameters);
 
     /// @brief Destructor.
-    ~MySqlConfigBackendImpl();
+    virtual ~MySqlConfigBackendImpl();
 
     /// @brief Creates MySQL binding from a @c Triplet.
     ///
index 51cca00f02f7d35d95f22bbb636f5bf1e19f4678..531252174e8bbafade39fef0f918676cdc010e19 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <config.h>
 #include <mysql_cb_dhcp4.h>
+#include <mysql_cb_impl.h>
 #include <database/db_exceptions.h>
 #include <database/server.h>
 #include <database/testutils/schema.h>
@@ -20,6 +21,7 @@
 #include <dhcpsrv/testutils/generic_backend_unittest.h>
 #include <mysql/testutils/mysql_schema.h>
 #include <boost/shared_ptr.hpp>
+#include <mysql.h>
 #include <gtest/gtest.h>
 #include <map>
 #include <sstream>
@@ -33,6 +35,24 @@ using namespace isc::dhcp::test;
 
 namespace {
 
+/// @brief Test implementation of the MySQL configuration backend.
+///
+/// It exposes protected members of the @c MySqlConfigBackendDHCPv4.
+class TestMySqlConfigBackendDHCPv4 : public MySqlConfigBackendDHCPv4 {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param parameters A data structure relating keywords and values
+    /// concerned with the database.
+    explicit TestMySqlConfigBackendDHCPv4(const DatabaseConnection::ParameterMap& parameters)
+        : MySqlConfigBackendDHCPv4(parameters) {
+    }
+
+    using MySqlConfigBackendDHCPv4::base_impl_;
+
+};
+
 /// @brief Test fixture class for @c MySqlConfigBackendDHCPv4.
 ///
 /// @todo The tests we're providing here only test cases when the
@@ -58,7 +78,7 @@ public:
             // Create MySQL connection and use it to start the backend.
             DatabaseConnection::ParameterMap params =
                 DatabaseConnection::parse(validMySQLConnectionString());
-            cbptr_.reset(new MySqlConfigBackendDHCPv4(params));
+            cbptr_.reset(new TestMySqlConfigBackendDHCPv4(params));
 
         } catch (...) {
             std::cerr << "*** ERROR: unable to open database. The test\n"
@@ -85,6 +105,43 @@ public:
         destroyMySQLSchema();
     }
 
+    /// @brief Counts rows in a selected table in MySQL database.
+    ///
+    /// This method can be used to verify that some configuration elements were
+    /// deleted from a selected table as a result of cascade delete or a trigger.
+    /// For example, deleting a subnet should trigger deletion of its address
+    /// pools and options. By counting the rows on each table we can determine
+    /// whether the deletion took place on all tables for which it was expected.
+    ///
+    /// @param table Table name.
+    /// @return Number of rows in the specified table.
+    size_t countRows(const std::string& table) const {
+        auto p = boost::dynamic_pointer_cast<TestMySqlConfigBackendDHCPv4>(cbptr_);
+        if (!p) {
+            ADD_FAILURE() << "cbptr_ does not cast to TestMySqlConfigBackendDHCPv4";
+            return (0);
+        }
+
+        // Reuse the existing connection of the backend.
+        auto impl = boost::dynamic_pointer_cast<MySqlConfigBackendImpl>(p->base_impl_);
+        auto& conn = impl->conn_;
+
+        // Execute a simple select query on all rows.
+        std::string query = "SELECT * FROM " + table;
+        auto status = mysql_query(conn.mysql_, query.c_str());
+        if (status != 0) {
+            ADD_FAILURE() << "Query failed: " << mysql_error(conn.mysql_);
+            return (0);
+        }
+
+        // Get the number of rows returned and free the result.
+        MYSQL_RES * res = mysql_store_result(conn.mysql_);
+        unsigned numrows = static_cast<unsigned>(mysql_num_rows(res));
+        mysql_free_result(res);
+
+        return (numrows);
+    }
+
     /// @brief Creates several servers used in tests.
     void initTestServers() {
         test_servers_.push_back(Server::create(ServerTag("server1"), "this is server 1"));
@@ -614,6 +671,8 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getAndDeleteAllServers) {
     // The number of deleted server should be equal to the number of
     // inserted servers. The logical 'all' server should be excluded.
     EXPECT_EQ(test_servers_.size() - 1, deleted_servers);
+
+    EXPECT_EQ(1, countRows("dhcp4_server"));
 }
 
 // This test verifies that the global parameter can be added, updated and
@@ -1915,6 +1974,27 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetworkSubnets4) {
     EXPECT_TRUE(isEquivalent(returned_list, test_list));
 }
 
+TEST_F(MySqlConfigBackendDHCPv4Test, subnetOptions) {
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[0]));
+    EXPECT_EQ(3, countRows("dhcp4_options"));
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[1]));
+    EXPECT_EQ(2, countRows("dhcp4_options"));
+
+    EXPECT_NO_THROW(cbptr_->deleteSubnet4(ServerSelector::ALL(), test_subnets_[1]->getID()));
+    EXPECT_EQ(0, countRows("dhcp4_subnet"));
+    EXPECT_EQ(0, countRows("dhcp4_pool"));
+    EXPECT_EQ(0, countRows("dhcp4_options"));
+
+    EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[0]));
+    EXPECT_EQ(3, countRows("dhcp4_options"));
+
+    EXPECT_NO_THROW(cbptr_->deleteSubnet4(ServerSelector::ALL(), test_subnets_[0]->getID()));
+    EXPECT_EQ(0, countRows("dhcp4_subnet"));
+    EXPECT_EQ(0, countRows("dhcp4_pool"));
+    EXPECT_EQ(0, countRows("dhcp4_options"));
+}
+
 // Test that shared network can be inserted, fetched, updated and then
 // fetched again.
 TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetwork4) {
index 348c7c5213f75f4172d68da0fd620971911a79cc..d83a8116c9ee0eab63dc3a48fde4f9031e43456e 100644 (file)
@@ -52,7 +52,7 @@ const int MLM_MYSQL_FETCH_FAILURE = 0;
 /// @name Current database schema version values.
 //@{
 const uint32_t MYSQL_SCHEMA_VERSION_MAJOR = 8;
-const uint32_t MYSQL_SCHEMA_VERSION_MINOR = 1;
+const uint32_t MYSQL_SCHEMA_VERSION_MINOR = 2;
 
 //@}
 
index 524e3c83651750b3557cd969d42c4604423ce7f1..f3ba934d661084bf844721a88141c313f3ce8895 100644 (file)
@@ -9,4 +9,5 @@
 /upgrade_6.0_to_7.0.sh
 /upgrade_7.0_to_8.0.sh
 /upgrade_8.0_to_8.1.sh
+/upgrade_8.1_to_8.2.sh
 /wipe_data.sh
index 1f8584206992c63a2b633c612d7ebad2a246226a..6370f95af90a50540be134368fc002b4880c2a11 100644 (file)
@@ -14,6 +14,7 @@ sqlscripts_DATA += upgrade_5.2_to_6.0.sh
 sqlscripts_DATA += upgrade_6.0_to_7.0.sh
 sqlscripts_DATA += upgrade_7.0_to_8.0.sh
 sqlscripts_DATA += upgrade_8.0_to_8.1.sh
+sqlscripts_DATA += upgrade_8.1_to_8.2.sh
 sqlscripts_DATA += wipe_data.sh
 
 DISTCLEANFILES = upgrade_1.0_to_2.0.sh
@@ -26,7 +27,7 @@ DISTCLEANFILES += upgrade_5.1_to_5.2.sh
 DISTCLEANFILES += upgrade_5.2_to_6.0.sh
 DISTCLEANFILES += upgrade_6.0_to_7.0.sh
 DISTCLEANFILES += upgrade_7.0_to_8.0.sh
-DISTCLEANFILES += upgrade_8.0_to_8.1.sh
+DISTCLEANFILES += upgrade_8.1_to_8.2.sh
 DISTCLEANFILES += wipe_data.sh
 
 EXTRA_DIST = ${sqlscripts_DATA}
index f5ce7e781797dfb73a1d645a989c7ca581ea3b6d..253f2f45c704739d35eadc22c40667c93839871c 100644 (file)
@@ -2377,6 +2377,40 @@ SET version = '8', minor = '1';
 
 # This line concludes database upgrade to version 8.1.
 
+# Do not perform cascade deletion of the data in the dhcp4_pool because
+# the cascade deletion does not execute triggers associated with the table.
+# Instead we are going to use triggers on the dhcp4_subnet table.
+ALTER TABLE dhcp4_pool
+    DROP FOREIGN KEY fk_dhcp4_pool_subnet_id;
+
+ALTER TABLE dhcp4_pool
+    ADD CONSTRAINT fk_dhcp4_pool_subnet_id FOREIGN KEY (subnet_id)
+    REFERENCES dhcp4_subnet (subnet_id)
+    ON DELETE NO ACTION ON UPDATE CASCADE;
+
+
+# Drop existing trigger on the dhcp4_subnet table.
+DROP TRIGGER dhcp4_subnet_ADEL;
+
+# Create new trigger which will delete pools associated with the subnet and
+# the options associated with the subnet.
+DELIMITER $$
+CREATE TRIGGER dhcp4_subnet_BDEL BEFORE DELETE ON dhcp4_subnet
+    FOR EACH ROW
+    BEGIN
+        CALL createAuditEntryDHCP4('dhcp4_subnet', OLD.subnet_id, "delete");
+        DELETE FROM dhcp4_pool WHERE subnet_id = OLD.subnet_id;
+        DELETE FROM dhcp4_options WHERE dhcp4_subnet_id = OLD.subnet_id;
+    END $$
+DELIMITER ;
+
+
+# Update the schema version number
+UPDATE schema_version
+SET version = '8', minor = '2';
+
+# This line concludes database upgrade to version 8.2.
+
 
 # Notes:
 #
diff --git a/src/share/database/scripts/mysql/upgrade_8.1_to_8.2.sh.in b/src/share/database/scripts/mysql/upgrade_8.1_to_8.2.sh.in
new file mode 100644 (file)
index 0000000..a8dbcde
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+prefix=@prefix@
+# Include utilities. Use installed version if available and
+# use build version if it isn't.
+if [ -e @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh ]; then
+    . @datarootdir@/@PACKAGE_NAME@/scripts/admin-utils.sh
+else
+    . @abs_top_builddir@/src/bin/admin/admin-utils.sh
+fi
+
+VERSION=`mysql_version "$@"`
+
+if [ "$VERSION" != "8.1" ]; then
+    printf "This script upgrades 8.1 to 8.2. Reported version is $VERSION. Skipping upgrade.\n"
+    exit 0
+fi
+
+mysql "$@" <<EOF
+
+# Do not perform cascade deletion of the data in the dhcp4_pool because
+# the cascade deletion does not execute triggers associated with the table.
+# Instead we are going to use triggers on the dhcp4_subnet table.
+ALTER TABLE dhcp4_pool
+    DROP FOREIGN KEY fk_dhcp4_pool_subnet_id;
+
+ALTER TABLE dhcp4_pool
+    ADD CONSTRAINT fk_dhcp4_pool_subnet_id FOREIGN KEY (subnet_id)
+    REFERENCES dhcp4_subnet (subnet_id)
+    ON DELETE NO ACTION ON UPDATE CASCADE;
+
+
+# Drop existing trigger on the dhcp4_subnet table.
+DROP TRIGGER dhcp4_subnet_ADEL;
+
+# Create new trigger which will delete pools associated with the subnet and
+# the options associated with the subnet.
+DELIMITER $$
+CREATE TRIGGER dhcp4_subnet_BDEL BEFORE DELETE ON dhcp4_subnet
+    FOR EACH ROW
+    BEGIN
+        CALL createAuditEntryDHCP4('dhcp4_subnet', OLD.subnet_id, "delete");
+        DELETE FROM dhcp4_pool WHERE subnet_id = OLD.subnet_id;
+        DELETE FROM dhcp4_options WHERE dhcp4_subnet_id = OLD.subnet_id;
+    END $$
+DELIMITER ;
+
+
+# Update the schema version number
+UPDATE schema_version
+SET version = '8', minor = '2';
+
+# This line concludes database upgrade to version 8.2.
+
+EOF
+
+RESULT=$?
+
+exit $?