#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>
/// 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,
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,
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
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);
}
}
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
OptionDescriptorPtr desc_copy(new OptionDescriptor(*desc));
desc_copy->space_name_ = option_space;
createUpdateOption4(server_selector, shared_network->getName(),
- desc_copy);
+ desc_copy, false);
}
}
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()));
/// @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"
};
- 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)));
insertOption4(server_selector, in_bindings);
}
- transaction.commit();
+ if (transaction) {
+ transaction->commit();
+ }
}
/// @brief Sends query to insert or update DHCP option in a pool.
<< pool_end_address);
}
- createUpdateOption4(server_selector, pool_id, option);
+ createUpdateOption4(server_selector, pool_id, option, false);
}
/// @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"
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));
insertOption4(server_selector, in_bindings);
}
- transaction.commit();
+ if (transaction) {
+ transaction->commit();
+ }
}
/// @brief Sends query to insert or update DHCP option in a shared network.
/// @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"
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));
insertOption4(server_selector, in_bindings);
}
- transaction.commit();
+ if (transaction) {
+ transaction->commit();
+ }
}
/// @brief Sends query to retrieve single option definition by code and
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.
/// @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.
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
}
}
+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,
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.
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(),
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(),
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.
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);
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);
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(),
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
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(),
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(),
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(),
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");
+ }
}
REFERENCES dhcp4_audit_revision (id)
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
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 $$
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 $$
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
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 ;
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
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;
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;