Commit e88273f6 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[#680,!426] Deleted embedded DHCPv6 options when subnet is deleted.

parent f27f45ec
......@@ -2966,7 +2966,8 @@ MySqlConfigBackendDHCPv6Impl::MySqlConfigBackendDHCPv6Impl(const DatabaseConnect
}
MySqlConfigBackendDHCPv6::MySqlConfigBackendDHCPv6(const DatabaseConnection::ParameterMap& parameters)
: impl_(new MySqlConfigBackendDHCPv6Impl(parameters)) {
: base_impl_(new MySqlConfigBackendDHCPv6Impl(parameters)), impl_() {
impl_ = boost::dynamic_pointer_cast<MySqlConfigBackendDHCPv6Impl>(base_impl_);
}
Subnet6Ptr
......
......@@ -7,6 +7,7 @@
#ifndef MYSQL_CONFIG_BACKEND_DHCP6_H
#define MYSQL_CONFIG_BACKEND_DHCP6_H
#include <mysql_cb_impl.h>
#include <database/database_connection.h>
#include <dhcpsrv/config_backend_dhcp6.h>
#include <mysql_cb_log.h>
......@@ -571,7 +572,11 @@ 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 MySqlConfigBackendDHCPv6
/// class.
......
......@@ -18,11 +18,11 @@
#include <dhcp/option_string.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/testutils/generic_backend_unittest.h>
#include <dhcpsrv/testutils/mysql_generic_backend_unittest.h>
#include <mysql/testutils/mysql_schema.h>
#include <boost/shared_ptr.hpp>
#include <mysql.h>
#include <gtest/gtest.h>
#include <mysql.h>
#include <map>
#include <sstream>
......@@ -63,7 +63,7 @@ public:
/// server tags. We will have to expand existing tests when
/// the API is extended allowing for inserting servers to the
/// database.
class MySqlConfigBackendDHCPv4Test : public GenericBackendTest {
class MySqlConfigBackendDHCPv4Test : public MySqlGenericBackendTest {
public:
/// @brief Constructor.
......@@ -126,20 +126,7 @@ public:
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);
return (MySqlGenericBackendTest::countRows(conn, table));
}
/// @brief Creates several servers used in tests.
......@@ -1974,11 +1961,15 @@ TEST_F(MySqlConfigBackendDHCPv4Test, getSharedNetworkSubnets4) {
EXPECT_TRUE(isEquivalent(returned_list, test_list));
}
// Test that deleting a subnet triggers deletion of the options associated
// with the subnet and pools.
TEST_F(MySqlConfigBackendDHCPv4Test, subnetOptions) {
EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[0]));
EXPECT_EQ(2, countRows("dhcp4_pool"));
EXPECT_EQ(3, countRows("dhcp4_options"));
EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[1]));
EXPECT_EQ(2, countRows("dhcp4_pool"));
EXPECT_EQ(2, countRows("dhcp4_options"));
EXPECT_NO_THROW(cbptr_->deleteSubnet4(ServerSelector::ALL(), test_subnets_[1]->getID()));
......@@ -1988,6 +1979,7 @@ TEST_F(MySqlConfigBackendDHCPv4Test, subnetOptions) {
EXPECT_NO_THROW(cbptr_->createUpdateSubnet4(ServerSelector::ALL(), test_subnets_[0]));
EXPECT_EQ(3, countRows("dhcp4_options"));
EXPECT_EQ(2, countRows("dhcp4_pool"));
EXPECT_NO_THROW(cbptr_->deleteSubnet4(ServerSelector::ALL(), test_subnets_[0]->getID()));
EXPECT_EQ(0, countRows("dhcp4_subnet"));
......
......@@ -17,10 +17,11 @@
#include <dhcp/option_string.h>
#include <dhcpsrv/pool.h>
#include <dhcpsrv/subnet.h>
#include <dhcpsrv/testutils/generic_backend_unittest.h>
#include <dhcpsrv/testutils/mysql_generic_backend_unittest.h>
#include <mysql/testutils/mysql_schema.h>
#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include <mysql.h>
#include <map>
#include <sstream>
......@@ -33,6 +34,24 @@ using namespace isc::dhcp::test;
namespace {
/// @brief Test implementation of the MySQL configuration backend.
///
/// It exposes protected members of the @c MySqlConfigBackendDHCPv6.
class TestMySqlConfigBackendDHCPv6 : public MySqlConfigBackendDHCPv6 {
public:
/// @brief Constructor.
///
/// @param parameters A data structure relating keywords and values
/// concerned with the database.
explicit TestMySqlConfigBackendDHCPv6(const DatabaseConnection::ParameterMap& parameters)
: MySqlConfigBackendDHCPv6(parameters) {
}
using MySqlConfigBackendDHCPv6::base_impl_;
};
/// @brief Test fixture class for @c MySqlConfigBackendDHCPv6.
///
/// @todo The tests we're providing here only test cases when the
......@@ -43,7 +62,7 @@ namespace {
/// server tags. We will have to expand existing tests when
/// the API is extended allowing for inserting servers to the
/// database.
class MySqlConfigBackendDHCPv6Test : public GenericBackendTest {
class MySqlConfigBackendDHCPv6Test : public MySqlGenericBackendTest {
public:
/// @brief Constructor.
......@@ -58,7 +77,7 @@ public:
// Create MySQL connection and use it to start the backend.
DatabaseConnection::ParameterMap params =
DatabaseConnection::parse(validMySQLConnectionString());
cbptr_.reset(new MySqlConfigBackendDHCPv6(params));
cbptr_.reset(new TestMySqlConfigBackendDHCPv6(params));
} catch (...) {
std::cerr << "*** ERROR: unable to open database. The test\n"
......@@ -85,6 +104,30 @@ 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<TestMySqlConfigBackendDHCPv6>(cbptr_);
if (!p) {
ADD_FAILURE() << "cbptr_ does not cast to TestMySqlConfigBackendDHCPv6";
return (0);
}
// Reuse the existing connection of the backend.
auto impl = boost::dynamic_pointer_cast<MySqlConfigBackendImpl>(p->base_impl_);
auto& conn = impl->conn_;
return (MySqlGenericBackendTest::countRows(conn, table));
}
/// @brief Creates several servers used in tests.
void initTestServers() {
test_servers_.push_back(Server::create(ServerTag("server1"), "this is server 1"));
......@@ -1930,6 +1973,36 @@ TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetworkSubnets6) {
EXPECT_TRUE(isEquivalent(returned_list, test_list));
}
// Test that deleting a subnet triggers deletion of the options associated
// with the subnet and pools.
TEST_F(MySqlConfigBackendDHCPv6Test, subnetOptions) {
EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[0]));
EXPECT_EQ(2, countRows("dhcp6_pool"));
EXPECT_EQ(2, countRows("dhcp6_pd_pool"));
EXPECT_EQ(3, countRows("dhcp6_options"));
EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[1]));
EXPECT_EQ(2, countRows("dhcp6_pool"));
EXPECT_EQ(2, countRows("dhcp6_pd_pool"));
EXPECT_EQ(4, countRows("dhcp6_options"));
EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ALL(), test_subnets_[1]->getID()));
EXPECT_EQ(0, countRows("dhcp6_subnet"));
EXPECT_EQ(0, countRows("dhcp6_pool"));
EXPECT_EQ(0, countRows("dhcp6_pd_pool"));
EXPECT_EQ(0, countRows("dhcp6_options"));
EXPECT_NO_THROW(cbptr_->createUpdateSubnet6(ServerSelector::ALL(), test_subnets_[0]));
EXPECT_EQ(2, countRows("dhcp6_pool"));
EXPECT_EQ(3, countRows("dhcp6_options"));
EXPECT_NO_THROW(cbptr_->deleteSubnet6(ServerSelector::ALL(), test_subnets_[0]->getID()));
EXPECT_EQ(0, countRows("dhcp6_subnet"));
EXPECT_EQ(0, countRows("dhcp6_pool"));
EXPECT_EQ(0, countRows("dhcp6_pd_pool"));
EXPECT_EQ(0, countRows("dhcp6_options"));
}
// Test that shared network can be inserted, fetched, updated and then
// fetched again.
TEST_F(MySqlConfigBackendDHCPv6Test, getSharedNetwork6) {
......
......@@ -19,6 +19,7 @@ libdhcpsrvtest_la_SOURCES += memory_host_data_source.cc memory_host_data_source.
libdhcpsrvtest_la_SOURCES += generic_backend_unittest.cc generic_backend_unittest.h
libdhcpsrvtest_la_SOURCES += generic_host_data_source_unittest.cc generic_host_data_source_unittest.h
libdhcpsrvtest_la_SOURCES += lease_file_io.cc lease_file_io.h
libdhcpsrvtest_la_SOURCES += mysql_generic_backend_unittest.cc mysql_generic_backend_unittest.h
libdhcpsrvtest_la_SOURCES += test_config_backend.h
libdhcpsrvtest_la_SOURCES += test_config_backend_dhcp4.cc test_config_backend_dhcp4.h
libdhcpsrvtest_la_SOURCES += test_config_backend_dhcp6.cc test_config_backend_dhcp6.h
......
// Copyright (C) 2019 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#include <config.h>
#include <dhcpsrv/testutils/mysql_generic_backend_unittest.h>
using namespace isc::db;
namespace isc {
namespace dhcp {
namespace test {
MySqlGenericBackendTest::MySqlGenericBackendTest()
: GenericBackendTest() {
}
size_t
MySqlGenericBackendTest::countRows(MySqlConnection& conn, const std::string& table) const {
// 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);
}
} // end of namespace isc::dhcp::test
} // end of namespace isc::dhcp
} // end of namespace isc
// Copyright (C) 2018-2019 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
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef MYSQL_GENERIC_BACKEND_UNITTEST_H
#define MYSQL_GENERIC_BACKEND_UNITTEST_H
#include <dhcpsrv/testutils/generic_backend_unittest.h>
#include <mysql/mysql_connection.h>
namespace isc {
namespace dhcp {
namespace test {
/// @brief Generic test fixture class with utility functions for testing
/// MySQL database backends.
class MySqlGenericBackendTest : public GenericBackendTest {
public:
/// @brief Constructor.
MySqlGenericBackendTest();
/// @brief Counts rows in a selected table in MySQL database.
///
/// One of the common applications of this method is to check whether the
/// expected number of rows were deleted from the specified table. In
/// relational databases, a deletion of a raw in one table causes deletion of
/// rows in other tables, e.g. via cascaded delete or triggers. This method
/// can be used to verify that the deletion took place in the dependent
/// tables.
///
/// @param conn MySql connection to be used for the query.
/// @param table Table name.
/// @return Number of rows in the specified table.
size_t countRows(db::MySqlConnection& conn, const std::string& table) const;
};
}
}
}
#endif
......@@ -2404,6 +2404,51 @@ CREATE TRIGGER dhcp4_subnet_BDEL BEFORE DELETE ON dhcp4_subnet
END $$
DELIMITER ;
# Do not perform cascade deletion of the data in the dhcp6_pool and dhcp6_pd_pool
# because the cascaded deletion does not execute triggers associated with the table.
# Instead we are going to use triggers on the dhcp6_subnet table.
ALTER TABLE dhcp6_pool
DROP FOREIGN KEY fk_dhcp6_pool_subnet_id;
ALTER TABLE dhcp6_pd_pool
DROP FOREIGN KEY fk_dhcp6_pd_pool_subnet_id;
ALTER TABLE dhcp6_pool
ADD CONSTRAINT fk_dhcp6_pool_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp6_subnet (subnet_id)
ON DELETE NO ACTION ON UPDATE CASCADE;
ALTER TABLE dhcp6_pd_pool
ADD CONSTRAINT fk_dhcp6_pd_pool_subnet_id FOREIGN KEY (subnet_id)
REFERENCES dhcp6_subnet (subnet_id)
ON DELETE NO ACTION ON UPDATE CASCADE;
# Create trigger which removes pool specific options upon removal of
# the pool.
DELIMITER $$
CREATE TRIGGER dhcp6_pd_pool_BDEL BEFORE DELETE ON dhcp6_pd_pool FOR EACH ROW
BEGIN
DELETE FROM dhcp6_options WHERE scope_id = 6 AND pd_pool_id = OLD.id;
END
$$
DELIMITER ;
# Drop existing trigger on the dhcp6_subnet table.
DROP TRIGGER dhcp6_subnet_ADEL;
# Create new trigger which will delete pools associated with the subnet and
# the options associated with the subnet.
DELIMITER $$
CREATE TRIGGER dhcp6_subnet_BDEL BEFORE DELETE ON dhcp6_subnet
FOR EACH ROW
BEGIN
CALL createAuditEntryDHCP6('dhcp6_subnet', OLD.subnet_id, "delete");
DELETE FROM dhcp6_pool WHERE subnet_id = OLD.subnet_id;
DELETE FROM dhcp6_pd_pool WHERE subnet_id = OLD.subnet_id;
DELETE FROM dhcp6_options WHERE dhcp6_subnet_id = OLD.subnet_id;
END $$
DELIMITER ;
# Update the schema version number
UPDATE schema_version
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment