diff options
-rw-r--r-- | src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc | 125 | ||||
-rw-r--r-- | src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc | 11 | ||||
-rw-r--r-- | src/hooks/dhcp/mysql_cb/mysql_cb_impl.h | 26 | ||||
-rw-r--r-- | src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp4_unittest.cc | 85 | ||||
-rw-r--r-- | src/share/database/scripts/mysql/dhcpdb_create.mysql | 133 | ||||
-rw-r--r-- | src/share/database/scripts/mysql/dhcpdb_drop.mysql | 4 |
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; |