summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc125
-rw-r--r--src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc11
-rw-r--r--src/hooks/dhcp/mysql_cb/mysql_cb_impl.h26
-rw-r--r--src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc85
-rw-r--r--src/share/database/scripts/mysql/dhcpdb_create.mysql133
-rw-r--r--src/share/database/scripts/mysql/dhcpdb_drop.mysql4
6 files changed, 340 insertions, 44 deletions
diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
index 69f65af635..864d20b44c 100644
--- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
+++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc
@@ -24,6 +24,7 @@
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/pointer_cast.hpp>
+#include <boost/scoped_ptr.hpp>
#include <mysql.h>
#include <mysqld_error.h>
#include <array>
@@ -51,7 +52,7 @@ public:
/// reading the database are less than those of statements modifying the
/// database.
enum StatementIndex {
- SET_AUDIT_LOG_MESSAGE,
+ INIT_AUDIT_REVISION,
GET_GLOBAL_PARAMETER4,
GET_ALL_GLOBAL_PARAMETERS4,
GET_MODIFIED_GLOBAL_PARAMETERS4,
@@ -215,9 +216,11 @@ public:
MySqlTransaction transaction(conn_);
- // Set log message to be used to create the audit revision.
- conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::SET_AUDIT_LOG_MESSAGE,
- { MySqlBinding::createString("this is a log message") });
+ // The last parameter indicates that the parameter is not bound to any
+ // other object (e.g. addition of a subnet), so an audit entry should
+ // be created for the addition of the parameter.
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", true);
// Try to update the existing row.
if (conn_.updateDeleteQuery(MySqlConfigBackendDHCPv4Impl::UPDATE_GLOBAL_PARAMETER4,
@@ -749,9 +752,14 @@ public:
MySqlTransaction transaction(conn_);
try {
- // Set log message to be used to create the audit revision.
- conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::SET_AUDIT_LOG_MESSAGE,
- { MySqlBinding::createString("this is a log message") });
+
+ // The change will involve multiple statements. The audit entry should
+ // be created for the parent object and should not be created for the
+ // DHCP options. The boolean value set to false indicates that the
+ // MySQL triggers should not create audit revision for the DHCP
+ // options associated with the subnet.
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", false);
// Try to insert subnet. If this duplicates primary key, i.e. this
// subnet already exists it will throw DuplicateEntry exception in
@@ -795,7 +803,8 @@ public:
for (auto desc = options->begin(); desc != options->end(); ++desc) {
OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
desc_copy->space_name_ = option_space;
- createUpdateOption4(server_selector, subnet->getID(), desc_copy);
+ createUpdateOption4(server_selector, subnet->getID(), desc_copy,
+ false);
}
}
@@ -1109,10 +1118,13 @@ public:
MySqlTransaction transaction(conn_);
try {
-
- // Set log message to be used to create the audit revision.
- conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::SET_AUDIT_LOG_MESSAGE,
- { MySqlBinding::createString("this is a log message") });
+ // The change will involve multiple statements. The audit entry should
+ // be created for the parent object and should not be created for the
+ // DHCP options. The boolean value set to false indicates that the
+ // MySQL triggers should not create audit revision for the DHCP
+ // options associated with the shared network.
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", false);
// Try to insert shared network. The shared network name must be unique,
// so if inserting fails with DuplicateEntry exception we'll need to
@@ -1150,7 +1162,7 @@ public:
OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
desc_copy->space_name_ = option_space;
createUpdateOption4(server_selector, shared_network->getName(),
- desc_copy);
+ desc_copy, false);
}
}
@@ -1218,6 +1230,13 @@ public:
OptionDescriptorPtr existing_option = getOption4(server_selector,
option->option_->getType(),
option->space_name_);
+
+ // The last parameter indicates that the option is not bound to any
+ // other object (e.g. addition of a subnet), so an audit entry should
+ // be created for the addition of the option.
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", true);
+
if (existing_option) {
in_bindings.push_back(MySqlBinding::createString(tag));
in_bindings.push_back(MySqlBinding::createInteger<uint8_t>(option->option_->getType()));
@@ -1238,9 +1257,12 @@ public:
/// @param server_selector Server selector.
/// @param subnet_id Identifier of the subnet the option belongs to.
/// @param option Pointer to the option descriptor encapsulating the option.
+ /// @param distinct_transaction Boolean value indicating whether setting
+ /// the option value should be enclosed in a separate transaction.
void createUpdateOption4(const ServerSelector& server_selector,
const SubnetID& subnet_id,
- const OptionDescriptorPtr& option) {
+ const OptionDescriptorPtr& option,
+ const bool distinct_transaction = false) {
if (server_selector.amUnassigned()) {
isc_throw(NotImplemented, "managing configuration for no particular server"
@@ -1266,11 +1288,21 @@ public:
};
- MySqlTransaction transaction(conn_);
+ boost::scoped_ptr<MySqlTransaction> transaction;
+ // Only start new transaction if specified to do so. This function may
+ // be called from within an existing transaction in which case we
+ // don't start the new one.
+ if (distinct_transaction) {
+ transaction.reset(new MySqlTransaction(conn_));
+ }
OptionDescriptorPtr existing_option = getOption4(server_selector, subnet_id,
option->option_->getType(),
option->space_name_);
+
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", distinct_transaction);
+
if (existing_option) {
in_bindings.push_back(MySqlBinding::createString(tag));
in_bindings.push_back(MySqlBinding::createInteger<uint32_t>(static_cast<uint32_t>(subnet_id)));
@@ -1283,7 +1315,9 @@ public:
insertOption4(server_selector, in_bindings);
}
- transaction.commit();
+ if (transaction) {
+ transaction->commit();
+ }
}
/// @brief Sends query to insert or update DHCP option in a pool.
@@ -1305,7 +1339,7 @@ public:
<< pool_end_address);
}
- createUpdateOption4(server_selector, pool_id, option);
+ createUpdateOption4(server_selector, pool_id, option, false);
}
@@ -1314,9 +1348,12 @@ public:
/// @param selector Server selector.
/// @param pool_id Identifier of the pool the option belongs to.
/// @param option Pointer to the option descriptor encapsulating the option.
+ /// @param distinct_transaction Boolean value indicating whether setting
+ /// the option value should be enclosed in a separate transaction.
void createUpdateOption4(const ServerSelector& server_selector,
const uint64_t pool_id,
- const OptionDescriptorPtr& option) {
+ const OptionDescriptorPtr& option,
+ const bool distinct_transaction = false) {
if (server_selector.amUnassigned()) {
isc_throw(NotImplemented, "managing configuration for no particular server"
@@ -1341,10 +1378,21 @@ public:
MySqlBinding::createTimestamp(option->getModificationTime())
};
- MySqlTransaction transaction(conn_);
+ boost::scoped_ptr<MySqlTransaction> transaction;
+ // Only start new transaction if specified to do so. This function may
+ // be called from within an existing transaction in which case we
+ // don't start the new one.
+ if (distinct_transaction) {
+ transaction.reset(new MySqlTransaction(conn_));
+ }
+
OptionDescriptorPtr existing_option = getOption4(server_selector, pool_id,
option->option_->getType(),
option->space_name_);
+
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", distinct_transaction);
+
if (existing_option) {
in_bindings.push_back(MySqlBinding::createString(tag));
in_bindings.push_back(MySqlBinding::createInteger<uint64_t>(pool_id));
@@ -1357,7 +1405,9 @@ public:
insertOption4(server_selector, in_bindings);
}
- transaction.commit();
+ if (transaction) {
+ transaction->commit();
+ }
}
/// @brief Sends query to insert or update DHCP option in a shared network.
@@ -1366,9 +1416,12 @@ public:
/// @param shared_network_name Name of the shared network the option
/// belongs to.
/// @param option Pointer to the option descriptor encapsulating the option.
+ /// @param distinct_transaction Boolean value indicating whether setting
+ /// the option value should be enclosed in a separate transaction.)
void createUpdateOption4(const ServerSelector& server_selector,
const std::string& shared_network_name,
- const OptionDescriptorPtr& option) {
+ const OptionDescriptorPtr& option,
+ const bool distinct_transaction = false) {
if (server_selector.amUnassigned()) {
isc_throw(NotImplemented, "managing configuration for no particular server"
@@ -1393,11 +1446,21 @@ public:
MySqlBinding::createTimestamp(option->getModificationTime())
};
- MySqlTransaction transaction(conn_);
+ boost::scoped_ptr<MySqlTransaction> transaction;
+ // Only start new transaction if specified to do so. This function may
+ // be called from within an existing transaction in which case we
+ // don't start the new one.
+ if (distinct_transaction) {
+ transaction.reset(new MySqlTransaction(conn_));
+ }
OptionDescriptorPtr existing_option = getOption4(server_selector, shared_network_name,
option->option_->getType(),
option->space_name_);
+
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", distinct_transaction);
+
if (existing_option) {
in_bindings.push_back(MySqlBinding::createString(tag));
in_bindings.push_back(MySqlBinding::createString(shared_network_name));
@@ -1410,7 +1473,9 @@ public:
insertOption4(server_selector, in_bindings);
}
- transaction.commit();
+ if (transaction) {
+ transaction->commit();
+ }
}
/// @brief Sends query to retrieve single option definition by code and
@@ -1712,10 +1777,8 @@ public:
option_def->getCode(),
option_def->getOptionSpaceName());
- // Set log message to be used to create the audit revision.
- conn_.insertQuery(MySqlConfigBackendDHCPv4Impl::SET_AUDIT_LOG_MESSAGE,
- { MySqlBinding::createString("this is a log message") });
-
+ initAuditRevision(MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "this is log message", true);
if (existing_definition) {
// Need to add three more bindings for WHERE clause.
@@ -1960,8 +2023,8 @@ TaggedStatementArray;
/// @brief Prepared MySQL statements used by the backend to insert and
/// retrieve data from the database.
TaggedStatementArray tagged_statements = { {
- { MySqlConfigBackendDHCPv4Impl::SET_AUDIT_LOG_MESSAGE,
- "SET @audit_log_message = ?"
+ { MySqlConfigBackendDHCPv4Impl::INIT_AUDIT_REVISION,
+ "CALL initAuditRevision(?, ?)"
},
// Select global parameter by name.
@@ -2521,14 +2584,14 @@ void
MySqlConfigBackendDHCPv4::createUpdateOption4(const db::ServerSelector& server_selector,
const std::string& shared_network_name,
const OptionDescriptorPtr& option) {
- impl_->createUpdateOption4(server_selector, shared_network_name, option);
+ impl_->createUpdateOption4(server_selector, shared_network_name, option, true);
}
void
MySqlConfigBackendDHCPv4::createUpdateOption4(const ServerSelector& server_selector,
const SubnetID& subnet_id,
const OptionDescriptorPtr& option) {
- impl_->createUpdateOption4(server_selector, subnet_id, option);
+ impl_->createUpdateOption4(server_selector, subnet_id, option, true);
}
void
diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc
index 0087412aab..89bb93d26d 100644
--- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc
+++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc
@@ -68,6 +68,17 @@ MySqlConfigBackendImpl::~MySqlConfigBackendImpl() {
}
void
+MySqlConfigBackendImpl::initAuditRevision(const int index,
+ const std::string& log_message,
+ const bool distinct_transaction) {
+ MySqlBindingCollection in_bindings = {
+ MySqlBinding::createString("this is a log message"),
+ MySqlBinding::createInteger<uint8_t>(static_cast<uint8_t>(distinct_transaction))
+ };
+ conn_.insertQuery(index, in_bindings);
+}
+
+void
MySqlConfigBackendImpl::getRecentAuditEntries(const int index,
const boost::posix_time::ptime& modification_time,
AuditEntryCollection& audit_entries) {
diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h
index 6b6f75ceee..f8908ed11e 100644
--- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h
+++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h
@@ -98,6 +98,32 @@ public:
return (s.str());
}
+ /// @brief Invokes the corresponding stored procedure in MySQL.
+ ///
+ /// The @c initAuditRevision stored procedure initializes several
+ /// session variables used when creating new audit revision in the
+ /// database. That includes setting a log message for the revision,
+ /// setting the boolean value indicating if the audit entries should
+ /// be created for DHCP options (that should only be the case when
+ /// the options are not added as part of the subnet, shared network
+ /// etc.). Finally, it resets the session variables used internally
+ /// by the database to corrdinate between the triggers.
+ ///
+ /// @param index query index.
+ /// @param log_message log message to be used for the audit revision.
+ /// @param distinct_transaction boolean value indicating if a single
+ /// change will be applied in the transaction (distinct transaction)
+ /// or a chain of transactions. The example of the former is when
+ /// an option is added to the existing subnet. The example of the
+ /// latter is when the subnet along with the options is added. This
+ /// consists of two changes (adding the subnet and options) as part
+ /// of the single transaction. The MySQL database needs to
+ /// distinguish between these two cases to bind audit revisions
+ /// to the appropriate objects.
+ void initAuditRevision(const int index,
+ const std::string& log_message,
+ const bool distinct_transaction);
+
/// @brief Sends query to the database to retrieve most recent audit entries.
///
/// @param index Index of the query to be used.
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 52ac6b848e..1177672d19 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
@@ -1129,6 +1129,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteOption4) {
ASSERT_TRUE(returned_opt_boot_file_name);
EXPECT_TRUE(returned_opt_boot_file_name->equals(*opt_boot_file_name));
+ {
+ SCOPED_TRACE("CREATE audit entry for an option");
+ testNewAuditEntry("dhcp4_options",
+ AuditEntry::ModificationType::CREATE,
+ "this is a log message");
+ }
+
// Modify option and update it in the database.
opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
cbptr_->createUpdateOption4(ServerSelector::ALL(),
@@ -1142,6 +1149,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteOption4) {
ASSERT_TRUE(returned_opt_boot_file_name);
EXPECT_TRUE(returned_opt_boot_file_name->equals(*opt_boot_file_name));
+ {
+ SCOPED_TRACE("UPDATE audit entry for an option");
+ testNewAuditEntry("dhcp4_options",
+ AuditEntry::ModificationType::UPDATE,
+ "this is a log message");
+ }
+
// Deleting an option with explicitly specified server tag should fail.
EXPECT_EQ(0, cbptr_->deleteOption4(ServerSelector::ONE("server1"),
opt_boot_file_name->option_->getType(),
@@ -1155,6 +1169,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteOption4) {
EXPECT_FALSE(cbptr_->getOption4(ServerSelector::ALL(),
opt_boot_file_name->option_->getType(),
opt_boot_file_name->space_name_));
+
+ {
+ SCOPED_TRACE("DELETE audit entry for an option");
+ testNewAuditEntry("dhcp4_options",
+ AuditEntry::ModificationType::DELETE,
+ "this is a log message");
+ }
}
// This test verifies that all global options can be retrieved.
@@ -1243,6 +1264,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSubnetOption4) {
subnet->getID());
ASSERT_TRUE(returned_subnet);
+ {
+ SCOPED_TRACE("CREATE audit entry for a new subnet");
+ testNewAuditEntry("dhcp4_subnet",
+ AuditEntry::ModificationType::CREATE,
+ "this is a log message");
+ }
+
OptionDescriptorPtr opt_boot_file_name = test_options_[0];
cbptr_->createUpdateOption4(ServerSelector::ALL(), subnet->getID(),
opt_boot_file_name);
@@ -1256,6 +1284,17 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSubnetOption4) {
ASSERT_TRUE(returned_opt_boot_file_name.option_);
EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+ {
+ SCOPED_TRACE("UPDATE audit entry for an added subnet option");
+ // Instead of adding an audit entry for an option we add an audit
+ // entry for the entire subnet so as the server refreshes the
+ // subnet with the new option. Note that the server doesn't
+ // have means to retrieve only the newly added option.
+ testNewAuditEntry("dhcp4_subnet",
+ AuditEntry::ModificationType::UPDATE,
+ "this is a log message");
+ }
+
opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
cbptr_->createUpdateOption4(ServerSelector::ALL(), subnet->getID(),
opt_boot_file_name);
@@ -1268,6 +1307,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSubnetOption4) {
ASSERT_TRUE(returned_opt_boot_file_name.option_);
EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+ {
+ SCOPED_TRACE("UPDATE audit entry for an updated subnet option");
+ testNewAuditEntry("dhcp4_subnet",
+ AuditEntry::ModificationType::UPDATE,
+ "this is a log message");
+ }
+
// Deleting an option with explicitly specified server tag should fail.
EXPECT_EQ(0, cbptr_->deleteOption4(ServerSelector::ONE("server1"),
subnet->getID(),
@@ -1284,6 +1330,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSubnetOption4) {
ASSERT_TRUE(returned_subnet);
EXPECT_FALSE(returned_subnet->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME).option_);
+
+ {
+ SCOPED_TRACE("UPDATE audit entry for a deleted subnet option");
+ testNewAuditEntry("dhcp4_subnet",
+ AuditEntry::ModificationType::UPDATE,
+ "this is a log message");
+ }
}
// This test verifies that option can be inserted, updated and deleted
@@ -1381,6 +1434,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSharedNetworkOption4) {
shared_network->getName());
ASSERT_TRUE(returned_network);
+ {
+ SCOPED_TRACE("CREATE audit entry for the new shared network");
+ testNewAuditEntry("dhcp4_shared_network",
+ AuditEntry::ModificationType::CREATE,
+ "this is a log message");
+ }
+
OptionDescriptorPtr opt_boot_file_name = test_options_[0];
cbptr_->createUpdateOption4(ServerSelector::ALL(),
shared_network->getName(),
@@ -1395,6 +1455,17 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSharedNetworkOption4) {
ASSERT_TRUE(returned_opt_boot_file_name.option_);
EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+ {
+ SCOPED_TRACE("UPDATE audit entry for the added shared network option");
+ // Instead of adding an audit entry for an option we add an audit
+ // entry for the entire shared network so as the server refreshes the
+ // shared network with the new option. Note that the server doesn't
+ // have means to retrieve only the newly added option.
+ testNewAuditEntry("dhcp4_shared_network",
+ AuditEntry::ModificationType::UPDATE,
+ "this is a log message");
+ }
+
opt_boot_file_name->persistent_ = !opt_boot_file_name->persistent_;
cbptr_->createUpdateOption4(ServerSelector::ALL(),
shared_network->getName(),
@@ -1408,6 +1479,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSharedNetworkOption4) {
ASSERT_TRUE(returned_opt_boot_file_name.option_);
EXPECT_TRUE(returned_opt_boot_file_name.equals(*opt_boot_file_name));
+ {
+ SCOPED_TRACE("UPDATE audit entry for the updated shared network option");
+ testNewAuditEntry("dhcp4_shared_network",
+ AuditEntry::ModificationType::UPDATE,
+ "this is a log message");
+ }
+
// Deleting an option with explicitly specified server tag should fail.
EXPECT_EQ(0, cbptr_->deleteOption4(ServerSelector::ONE("server1"),
shared_network->getName(),
@@ -1423,6 +1501,13 @@ TEST_F(MySqlConfigBackendDHCPv4Test, createUpdateDeleteSharedNetworkOption4) {
shared_network->getName());
ASSERT_TRUE(returned_network);
EXPECT_FALSE(returned_network->getCfgOption()->get(DHCP4_OPTION_SPACE, DHO_BOOT_FILE_NAME).option_);
+
+ {
+ SCOPED_TRACE("UPDATE audit entry for the deleted shared network option");
+ testNewAuditEntry("dhcp4_shared_network",
+ AuditEntry::ModificationType::UPDATE,
+ "this is a log message");
+ }
}
diff --git a/src/share/database/scripts/mysql/dhcpdb_create.mysql b/src/share/database/scripts/mysql/dhcpdb_create.mysql
index a54f3ec153..a9d50999b0 100644
--- a/src/share/database/scripts/mysql/dhcpdb_create.mysql
+++ b/src/share/database/scripts/mysql/dhcpdb_create.mysql
@@ -1366,6 +1366,21 @@ ALTER TABLE dhcp4_audit
ON DELETE NO ACTION ON UPDATE CASCADE;
-- -----------------------------------------------------
+-- Stored procedure which initializes the session
+-- variables for creation of the new audit revision.
+-- -----------------------------------------------------
+DROP PROCEDURE IF EXISTS initAuditRevision;
+DELIMITER $$
+CREATE PROCEDURE initAuditRevision(IN log_message TEXT,
+ IN distinct_transaction TINYINT(1))
+BEGIN
+ SET @audit_log_message = log_message;
+ SET @distinct_transaction = distinct_transaction;
+ SET @audit_revision_id = NULL;
+END $$
+DELIMITER ;
+
+-- -----------------------------------------------------
-- Stored procedure which creates a new entry in the
-- dhcp4_audit_revision table. This procedure should
-- be called from the triggers of the tables where
@@ -1377,9 +1392,12 @@ DROP PROCEDURE IF EXISTS createAuditRevisionDHCP4;
DELIMITER $$
CREATE PROCEDURE createAuditRevisionDHCP4()
BEGIN
+ DECLARE local_audit_log_message TEXT;
IF @audit_revision_id IS NULL THEN
+ SET local_audit_log_message = @audit_log_message;
+ SET @audit_log_message = NULL;
INSERT INTO dhcp4_audit_revision (modification_ts, log_message)
- VALUES (NOW(), @audit_log_message);
+ VALUES (NOW(), local_audit_log_message);
SET @audit_revision_id = LAST_INSERT_ID();
END IF;
END $$
@@ -1398,6 +1416,7 @@ CREATE PROCEDURE createAuditEntryDHCP4(IN object_type_val VARCHAR(256),
IN object_id_val BIGINT(20) UNSIGNED,
IN modification_type_val TINYINT(1))
BEGIN
+ CALL createAuditRevisionDHCP4();
INSERT INTO dhcp4_audit (object_type, object_id, modification_type, revision_id)
VALUES (object_type_val, object_id_val, modification_type_val, @audit_revision_id);
END $$
@@ -1414,7 +1433,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_global_parameter_AINS AFTER INSERT ON dhcp4_global_parameter
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_global_parameter', NEW.id, 0);
END $$
DELIMITER ;
@@ -1424,7 +1442,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_global_parameter_AUPD AFTER UPDATE ON dhcp4_global_parameter
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_global_parameter', NEW.id, 1);
END $$
DELIMITER ;
@@ -1434,7 +1451,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_global_parameter_ADEL AFTER DELETE ON dhcp4_global_parameter
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_global_parameter', OLD.id, 2);
END $$
DELIMITER ;
@@ -1444,7 +1460,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_subnet_AINS AFTER INSERT ON dhcp4_subnet
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_subnet', NEW.subnet_id, 0);
END $$
DELIMITER ;
@@ -1454,7 +1469,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_subnet_AUPD AFTER UPDATE ON dhcp4_subnet
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_subnet', NEW.subnet_id, 1);
END $$
DELIMITER ;
@@ -1464,7 +1478,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_subnet_ADEL AFTER DELETE ON dhcp4_subnet
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_subnet', OLD.subnet_id, 2);
END $$
DELIMITER ;
@@ -1474,7 +1487,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_shared_network_AINS AFTER INSERT ON dhcp4_shared_network
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_shared_network', NEW.id, 0);
END $$
DELIMITER ;
@@ -1484,7 +1496,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_shared_network_AUPD AFTER UPDATE ON dhcp4_shared_network
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_shared_network', NEW.id, 1);
END $$
DELIMITER ;
@@ -1494,7 +1505,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_shared_network_ADEL AFTER DELETE ON dhcp4_shared_network
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_shared_network', OLD.id, 2);
END $$
DELIMITER ;
@@ -1504,7 +1514,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_option_def_AINS AFTER INSERT ON dhcp4_option_def
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_option_def', NEW.id, 0);
END $$
DELIMITER ;
@@ -1514,7 +1523,6 @@ DELIMITER $$
CREATE TRIGGER dhcp4_option_def_AUPD AFTER UPDATE ON dhcp4_option_def
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_option_def', NEW.id, 1);
END $$
DELIMITER ;
@@ -1524,11 +1532,110 @@ DELIMITER $$
CREATE TRIGGER dhcp4_option_def_ADEL AFTER DELETE ON dhcp4_option_def
FOR EACH ROW
BEGIN
- CALL createAuditRevisionDHCP4();
CALL createAuditEntryDHCP4('dhcp4_option_def', OLD.id, 2);
END $$
DELIMITER ;
+-- -----------------------------------------------------
+-- Stored procedure which creates an audit entry for a
+-- DHCPv4 option. Depending on the scope of the option
+-- the audit entry can be created for various levels
+-- of configuration hierarchy. If this is a global
+-- option the audit entry is created for this option
+-- for CREATE, UPDATE or DELETE. If the option is being
+-- added for an owning option, e.g. for a subnet, the
+-- audit entry is created as an UPDATE to this object.
+-- From the Kea perspective such option addition will
+-- be seen as a subnet update and the server will fetch
+-- the whole subnet and merge it into its configuration.
+-- The audit entry is not created if it was already
+-- created as part of the current transaction.
+--
+-- The following parameters are passed to the procedure:
+-- - modification_type: CREATE, UPDATE or DELETE
+-- - scope_id: identifier of the option scope, e.g.
+-- global, subnet specific etc.
+-- - option_id: identifier of the option.
+-- - subnet_id: identifier of the subnet if the option
+-- belongs to the subnet.
+-- - host_id: identifier of the host if the option
+-- - belongs to the host.
+-- - network_name: shared network name if the option
+-- belongs to the shared network.
+-- -----------------------------------------------------
+DROP PROCEDURE IF EXISTS createOptionAuditDHCP4;
+DELIMITER $$
+CREATE PROCEDURE createOptionAuditDHCP4(IN modification_type TINYINT(1),
+ IN scope_id TINYINT(3) UNSIGNED,
+ IN option_id BIGINT(20) UNSIGNED,
+ IN subnet_id INT(10) UNSIGNED,
+ IN host_id INT(10) UNSIGNED,
+ IN network_name VARCHAR(128))
+BEGIN
+ # This variable will hold shared network id that we will retrieve
+ # by matching it name.
+ DECLARE snid VARCHAR(128);
+
+ # Distinct transaction flag is set when audit entry must be
+ # associated with the option. For example: adding a global
+ # option or adding an option to the existing subnet etc.
+ IF @distinct_transaction != 0 THEN
+ IF scope_id = 0 THEN
+ # If a global option is added or modified, create audit
+ # entry for the 'dhcp4_options' table.
+ CALL createAuditEntryDHCP4('dhcp4_options', option_id, modification_type);
+ ELSEIF scope_id = 1 THEN
+ # If subnet specific option is added or modified, create
+ # audit entry for the entire subnet, which indicates that
+ # it should be treated as the subnet update.
+ CALL createAuditEntryDHCP4('dhcp4_subnet', subnet_id, 1);
+ ELSEIF scope_id = 3 THEN
+ # If host specific option is added or modified, create
+ # audit entry for the host, which indicates that it
+ # should be treated as the host update.
+ CALL createAuditEntryDHCP4('hosts', host_id, 1);
+ ELSEIF scope_id = 4 THEN
+ # If shared network specific option is added or modified,
+ # created audit entry for the shared network which
+ # indicates that it should be treated as the shared
+ # network update.
+ SELECT id INTO snid FROM dhcp4_shared_network WHERE name = network_name LIMIT 1;
+ CALL createAuditEntryDHCP4('dhcp4_shared_network', snid, 1);
+ END IF;
+ END IF;
+END $$
+DELIMITER ;
+
+# Create dhcp4_options insert trigger
+DELIMITER $$
+CREATE TRIGGER dhcp4_options_AINS AFTER INSERT ON dhcp4_options
+ FOR EACH ROW
+ BEGIN
+ CALL createOptionAuditDHCP4(0, NEW.scope_id, NEW.option_id, NEW.dhcp4_subnet_id,
+ NEW.host_id, NEW.shared_network_name);
+ END $$
+DELIMITER ;
+
+# Create dhcp4_options update trigger
+DELIMITER $$
+CREATE TRIGGER dhcp4_options_AUPD AFTER UPDATE ON dhcp4_options
+ FOR EACH ROW
+ BEGIN
+ CALL createOptionAuditDHCP4(1, NEW.scope_id, NEW.option_id, NEW.dhcp4_subnet_id,
+ NEW.host_id, NEW.shared_network_name);
+ END $$
+DELIMITER ;
+
+# Create dhcp4_options delete trigger
+DELIMITER $$
+CREATE TRIGGER dhcp4_options_ADEL AFTER DELETE ON dhcp4_options
+ FOR EACH ROW
+ BEGIN
+ CALL createOptionAuditDHCP4(2, OLD.scope_id, OLD.option_id, OLD.dhcp4_subnet_id,
+ OLD.host_id, OLD.shared_network_name);
+ END $$
+DELIMITER ;
+
# Update the schema version number
UPDATE schema_version
diff --git a/src/share/database/scripts/mysql/dhcpdb_drop.mysql b/src/share/database/scripts/mysql/dhcpdb_drop.mysql
index 0a183d6573..2a41667d9c 100644
--- a/src/share/database/scripts/mysql/dhcpdb_drop.mysql
+++ b/src/share/database/scripts/mysql/dhcpdb_drop.mysql
@@ -59,6 +59,7 @@ DROP TABLE IF EXISTS dhcp6_shared_network_server;
DROP TABLE IF EXISTS dhcp6_subnet;
DROP TABLE IF EXISTS dhcp6_subnet_server;
DROP TABLE IF EXISTS modification;
+DROP PROCEDURE IF EXISTS initAuditRevision;
DROP PROCEDURE IF EXISTS createAuditRevisionDHCP4;
DROP PROCEDURE IF EXISTS createAuditEntryDHCP4;
DROP TRIGGER IF EXISTS dhcp4_global_parameter_AINS;
@@ -73,3 +74,6 @@ DROP TRIGGER IF EXISTS dhcp4_shared_network_ADEL;
DROP TRIGGER IF EXISTS dhcp4_option_def_AINS;
DROP TRIGGER IF EXISTS dhcp4_option_def_AUPD;
DROP TRIGGER IF EXISTS dhcp4_option_def_ADEL;
+DROP TRIGGER IF EXISTS dhcp4_options_AINS;
+DROP TRIGGER IF EXISTS dhcp4_options_AUPD;
+DROP TRIGGER IF EXISTS dhcp4_options_ADEL;