From: Francis Dupont Date: Wed, 17 Jun 2020 07:26:00 +0000 (+0200) Subject: [#1247] Checkpoint: fixed for audit entries X-Git-Tag: Kea-1.7.10~110 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=44e0cf8d2c3d49f99fdcd5218d5d71558b0a389b;p=thirdparty%2Fkea.git [#1247] Checkpoint: fixed for audit entries --- diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc index e519775e3f..436770195b 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.cc @@ -3002,12 +3002,15 @@ MySqlConfigBackendDHCPv4::getModifiedGlobalParameters4(const db::ServerSelector& AuditEntryCollection MySqlConfigBackendDHCPv4::getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const { + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const { LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4) - .arg(util::ptimeToText(modification_time)); + .arg(util::ptimeToText(modification_time)) + .arg(modification_id); AuditEntryCollection audit_entries; impl_->getRecentAuditEntries(MySqlConfigBackendDHCPv4Impl::GET_AUDIT_ENTRIES4_TIME, - server_selector, modification_time, audit_entries); + server_selector, modification_time, + modification_id, audit_entries); LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4_RESULT) .arg(audit_entries.size()); return (audit_entries); diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.h b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.h index 710a5d7503..f5fd30b40b 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.h +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp4.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 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 @@ -230,10 +230,13 @@ public: /// @param selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const; /// @brief Retrieves all servers. /// diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc index 03764f9f98..d66bf10725 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.cc @@ -3476,12 +3476,15 @@ MySqlConfigBackendDHCPv6::getModifiedGlobalParameters6(const db::ServerSelector& AuditEntryCollection MySqlConfigBackendDHCPv6::getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const { + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const { LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6) - .arg(util::ptimeToText(modification_time)); + .arg(util::ptimeToText(modification_time)) + .arg(modification_id); AuditEntryCollection audit_entries; impl_->getRecentAuditEntries(MySqlConfigBackendDHCPv6Impl::GET_AUDIT_ENTRIES6_TIME, - server_selector, modification_time, audit_entries); + server_selector, modification_time, + modification_id, audit_entries); LOG_DEBUG(mysql_cb_logger, DBGLVL_TRACE_BASIC, MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6_RESULT) .arg(audit_entries.size()); return (audit_entries); diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h index 1c97647f61..3f35e838c7 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_dhcp6.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -230,10 +230,13 @@ public: /// @param selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const; /// @brief Retrieves all servers. /// diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc index 39c43a9b38..f3e6510942 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.cc @@ -192,6 +192,7 @@ void MySqlConfigBackendImpl::getRecentAuditEntries(const int index, const db::ServerSelector& server_selector, const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id, AuditEntryCollection& audit_entries) { // Create the output bindings for receiving the data. MySqlBindingCollection out_bindings = { @@ -200,6 +201,7 @@ MySqlConfigBackendImpl::getRecentAuditEntries(const int index, MySqlBinding::createInteger(), // object_id MySqlBinding::createInteger(), // modification_type MySqlBinding::createTimestamp(), // modification_time + MySqlBinding::createInteger(), // revision_id MySqlBinding::createString(AUDIT_ENTRY_LOG_MESSAGE_BUF_LENGTH) // log_message }; @@ -207,10 +209,12 @@ MySqlConfigBackendImpl::getRecentAuditEntries(const int index, for (auto tag : tags) { - // There is only one input binding, modification time. + // There are only a few input bindings MySqlBindingCollection in_bindings = { MySqlBinding::createString(tag.get()), - MySqlBinding::createTimestamp(modification_time) + MySqlBinding::createTimestamp(modification_time), + MySqlBinding::createTimestamp(modification_time), + MySqlBinding::createInteger(modification_id) }; // Execute select. @@ -227,7 +231,8 @@ MySqlConfigBackendImpl::getRecentAuditEntries(const int index, out_bindings[2]->getInteger(), mod_type, out_bindings[4]->getTimestamp(), - out_bindings[5]->getStringOrDefault("")); + out_bindings[5]->getInteger(), + out_bindings[6]->getStringOrDefault("")); audit_entries.insert(audit_entry); }); } diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h index d2e3c6f987..9c3639d77c 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_impl.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 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 @@ -239,11 +239,14 @@ public: /// @param server_selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @param [out] audit_entries Reference to the container where fetched audit /// entries will be inserted. void getRecentAuditEntries(const int index, const db::ServerSelector& server_selector, const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id, db::AuditEntryCollection& audit_entries); /// @brief Sends query to delete rows from a table. diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_messages.cc b/src/hooks/dhcp/mysql_cb/mysql_cb_messages.cc index 6b3b9e280a..b7511942ae 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_messages.cc +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_messages.cc @@ -1,4 +1,4 @@ -// File created from ../../../../src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes on Tue Jun 25 2019 16:12 +// File created from ../../../../src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes on Wed Jun 17 2020 02:55 #include #include @@ -317,9 +317,9 @@ const char* values[] = { "MYSQL_CB_GET_OPTION_DEF6", "retrieving option definition code: %1 space: %2", "MYSQL_CB_GET_PORT4", "get port", "MYSQL_CB_GET_PORT6", "get port", - "MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4", "retrieving audit entries from: %1", + "MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4", "retrieving audit entries from: %1 %2", "MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4_RESULT", "retrieving: %1 elements", - "MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6", "retrieving audit entries from: %1", + "MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6", "retrieving audit entries from: %1 %2", "MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6_RESULT", "retrieving: %1 elements", "MYSQL_CB_GET_SERVER4", "retrieving DHCPv4 server: %1", "MYSQL_CB_GET_SERVER6", "retrieving DHCPv6 server: %1", diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_messages.h b/src/hooks/dhcp/mysql_cb/mysql_cb_messages.h index f8a143f66e..723cc2a98d 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_messages.h +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_messages.h @@ -1,4 +1,4 @@ -// File created from ../../../../src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes on Tue Jun 25 2019 16:12 +// File created from ../../../../src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes on Wed Jun 17 2020 02:55 #ifndef MYSQL_CB_MESSAGES_H #define MYSQL_CB_MESSAGES_H diff --git a/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes b/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes index e76df2864a..95d504e259 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes +++ b/src/hooks/dhcp/mysql_cb/mysql_cb_messages.mes @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +# Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") $NAMESPACE isc::cb @@ -429,14 +429,14 @@ Debug message issued when triggered an action to retrieve port % MYSQL_CB_GET_PORT6 get port Debug message issued when triggered an action to retrieve port -% MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4 retrieving audit entries from: %1 -Debug message issued when triggered an action to retrieve audit entries from specified time +% MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4 retrieving audit entries from: %1 %2 +Debug message issued when triggered an action to retrieve audit entries from specified time and id. % MYSQL_CB_GET_RECENT_AUDIT_ENTRIES4_RESULT retrieving: %1 elements Debug message indicating the result of an action to retrieve audit entries from specified time -% MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6 retrieving audit entries from: %1 -Debug message issued when triggered an action to retrieve audit entries from specified time +% MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6 retrieving audit entries from: %1 %2 +Debug message issued when triggered an action to retrieve audit entries from specified time and id % MYSQL_CB_GET_RECENT_AUDIT_ENTRIES6_RESULT retrieving: %1 elements Debug message indicating the result of an action to retrieve audit entries from specified time diff --git a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h index 33b72352ee..bf56ca3eb6 100644 --- a/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h +++ b/src/hooks/dhcp/mysql_cb/mysql_query_macros_dhcp.h @@ -617,13 +617,16 @@ namespace { " a.object_id," \ " a.modification_type," \ " r.modification_ts," \ + " r.id, " \ " r.log_message " \ "FROM " #table_prefix "_audit AS a " \ "INNER JOIN " #table_prefix "_audit_revision AS r " \ " ON a.revision_id = r.id " \ "INNER JOIN " #table_prefix "_server AS s" \ " ON r.server_id = s.id " \ - "WHERE (s.tag = ? OR s.id = 1) AND (r.modification_ts > ?) " \ + "WHERE (s.tag = ? OR s.id = 1) AND" \ + " ((r.modification_ts > ?) OR " \ + " ((r.modification_ts = ?) AND (r.id > ?))) " \ "ORDER BY r.modification_ts, r.id" #endif 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 9b7c29f563..0a09237b9a 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 @@ -432,7 +432,7 @@ public: std::string logExistingAuditEntries(const std::string& server_tag) { std::ostringstream s; - auto& mod_time_idx = audit_entries_[server_tag].get(); + auto& mod_time_idx = audit_entries_[server_tag].get(); for (auto audit_entry_it = mod_time_idx.begin(); audit_entry_it != mod_time_idx.end(); @@ -442,6 +442,7 @@ public: << audit_entry->getObjectId() << ", " << static_cast(audit_entry->getModificationType()) << ", " << audit_entry->getModificationTime() << ", " + << audit_entry->getEntryId() << ", " << audit_entry->getLogMessage() << std::endl; } @@ -492,11 +493,11 @@ public: // Audit entries for different server tags are stored in separate // containers. audit_entries_[tag] = cbptr_->getRecentAuditEntries(server_selector, - timestamps_["two days ago"]); + timestamps_["two days ago"], 0); ASSERT_EQ(audit_entries_size_save + new_entries_num, audit_entries_[tag].size()) << logExistingAuditEntries(tag); - auto& mod_time_idx = audit_entries_[tag].get(); + auto& mod_time_idx = audit_entries_[tag].get(); // Iterate over specified number of entries starting from the most recent // one and check they have correct values. diff --git a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc index 6a5cf02815..9a7a0e8ae3 100644 --- a/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc +++ b/src/hooks/dhcp/mysql_cb/tests/mysql_cb_dhcp6_unittest.cc @@ -480,7 +480,7 @@ public: std::string logExistingAuditEntries(const std::string& server_tag) { std::ostringstream s; - auto& mod_time_idx = audit_entries_[server_tag].get(); + auto& mod_time_idx = audit_entries_[server_tag].get(); for (auto audit_entry_it = mod_time_idx.begin(); audit_entry_it != mod_time_idx.end(); @@ -490,6 +490,7 @@ public: << audit_entry->getObjectId() << ", " << static_cast(audit_entry->getModificationType()) << ", " << audit_entry->getModificationTime() << ", " + << audit_entry->getEntryId() << ", " << audit_entry->getLogMessage() << std::endl; } @@ -539,11 +540,11 @@ public: // Audit entries for different server tags are stored in separate // containers. audit_entries_[tag] = cbptr_->getRecentAuditEntries(server_selector, - timestamps_["two days ago"]); + timestamps_["two days ago"], 0); ASSERT_EQ(audit_entries_size_save + new_entries_num, audit_entries_[tag].size()) << logExistingAuditEntries(tag); - auto& mod_time_idx = audit_entries_[tag].get(); + auto& mod_time_idx = audit_entries_[tag].get(); // Iterate over specified number of entries starting from the most recent // one and check they have correct values. diff --git a/src/lib/database/audit_entry.cc b/src/lib/database/audit_entry.cc index b44c15ab62..08d5de13e1 100644 --- a/src/lib/database/audit_entry.cc +++ b/src/lib/database/audit_entry.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -15,11 +15,13 @@ AuditEntry::AuditEntry(const std::string& object_type, const uint64_t object_id, const ModificationType& modification_type, const boost::posix_time::ptime& modification_time, + const uint64_t id, const std::string& log_message) : object_type_(object_type), object_id_(object_id), modification_type_(modification_type), modification_time_(modification_time), + id_(id), log_message_(log_message) { // Check if the provided values are sane. validate(); @@ -28,11 +30,13 @@ AuditEntry::AuditEntry(const std::string& object_type, AuditEntry::AuditEntry(const std::string& object_type, const uint64_t object_id, const ModificationType& modification_type, + const uint64_t id, const std::string& log_message) : object_type_(object_type), object_id_(object_id), modification_type_(modification_type), modification_time_(boost::posix_time::microsec_clock::local_time()), + id_(id), log_message_(log_message) { // Check if the provided values are sane. validate(); @@ -43,10 +47,11 @@ AuditEntry::create(const std::string& object_type, const uint64_t object_id, const ModificationType& modification_type, const boost::posix_time::ptime& modification_time, + const uint64_t id, const std::string& log_message) { return (boost::make_shared(object_type, object_id, modification_type, - modification_time, + modification_time, id, log_message)); } @@ -54,9 +59,10 @@ AuditEntryPtr AuditEntry::create(const std::string& object_type, const uint64_t object_id, const ModificationType& modification_type, + const uint64_t id, const std::string& log_message) { return (boost::make_shared(object_type, object_id, - modification_type, + modification_type, id, log_message)); } diff --git a/src/lib/database/audit_entry.h b/src/lib/database/audit_entry.h index 465705fcf6..f115fb1982 100644 --- a/src/lib/database/audit_entry.h +++ b/src/lib/database/audit_entry.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -39,6 +39,8 @@ typedef boost::shared_ptr AuditEntryPtr; /// the most recent modifications in the database it will query for all /// entries with later modification time than stored. That way the /// server queries only for the audit entries it hasn't fetched yet. +/// In the case two (or more) successive audit entries have the same +/// modification time the strictly increasing id is used. /// /// When the modification type of the entry is set to /// @c AuditEntry::ModificationType::DELETE, the corresponding @@ -51,6 +53,7 @@ typedef boost::shared_ptr AuditEntryPtr; /// - object_id: 123 /// - modification_type: 3 (DELETE) /// - modification_time: "2019-01-15 15:45:23" +/// - id: 234 /// - log_message: "DHCPv4 subnet 123 deleted" /// /// The subnet is instantly removed from the dhcp4_subnet table. When @@ -72,18 +75,20 @@ public: DELETE = 2 }; - /// @brief Constructor using explicit modification time. + /// @brief Constructor using explicit modification time and id. /// /// @param object_type name of the table where data was modified. /// @param object_id identifier of the modified record in this table. /// @param modification_type type of the modification, e.g. DELETE. /// @param modification_time time of modification for that record. + /// @param id identifier of the entry itself. /// @param log_message optional log message associated with the /// modification. AuditEntry(const std::string& object_type, const uint64_t object_id, const ModificationType& modification_type, const boost::posix_time::ptime& modification_time, + const uint64_t id, const std::string& log_message); /// @brief Constructor using default modification time. @@ -91,11 +96,13 @@ public: /// @param object_type name of the table where data was modified. /// @param object_id identifier of the modified record in this table. /// @param modification_type type of the modification, e.g. DELETE. + /// @param id identifier of the entry itself. /// @param log_message optional log message associated with the /// modification. AuditEntry(const std::string& object_type, const uint64_t object_id, const ModificationType& modification_type, + const uint64_t id, const std::string& log_message); /// @brief Factory function creating an instance of @c AuditEntry. @@ -109,6 +116,7 @@ public: /// @param object_id identifier of the modified record in this table. /// @param modification_type type of the modification, e.g. DELETE. /// @param modification_time time of modification for that record. + /// @param id identifier of the entry itself. /// @param log_message optional log message associated with the /// modification. /// @@ -117,6 +125,7 @@ public: const uint64_t object_id, const ModificationType& modification_type, const boost::posix_time::ptime& modification_time, + const uint64_t id, const std::string& log_message); /// @brief Factory function creating an instance of @c AuditEntry. @@ -129,6 +138,7 @@ public: /// @param object_type name of the table where data was modified. /// @param object_id identifier of the modified record in this table. /// @param modification_type type of the modification, e.g. DELETE. + /// @param id identifier of the entry itself. /// @param log_message optional log message associated with the /// modification. /// @@ -136,6 +146,7 @@ public: static AuditEntryPtr create(const std::string& object_type, const uint64_t object_id, const ModificationType& modification_type, + const uint64_t id, const std::string& log_message); /// @brief Returns object type. @@ -166,6 +177,13 @@ public: return (modification_time_); } + /// @brief Returns entry id. + /// + /// @return Identifier of the entry. + uint64_t getEntryId() const { + return (id_); + } + /// @brief Returns log message. /// /// @return Optional log message corresponding to the changes. @@ -193,6 +211,9 @@ private: /// @brief Modification time. boost::posix_time::ptime modification_time_; + /// @brief Entry id. + uint64_t id_; + /// @brief Log message. std::string log_message_; }; @@ -201,7 +222,7 @@ private: struct AuditEntryObjectTypeTag { }; /// @brief Tag used to access index by modification time. -struct AuditEntryModificationTimeTag { }; +struct AuditEntryModificationTimeIdTag { }; /// @brief Multi index container holding @c AuditEntry instances. /// @@ -229,13 +250,21 @@ typedef boost::multi_index_container< > >, - // Second index allows for accessing by the modification time. + // Second index allows for accessing by the modification time and id. boost::multi_index::ordered_non_unique< - boost::multi_index::tag, - boost::multi_index::const_mem_fun< + boost::multi_index::tag, + boost::multi_index::composite_key< AuditEntry, - boost::posix_time::ptime, - &AuditEntry::getModificationTime + boost::multi_index::const_mem_fun< + AuditEntry, + boost::posix_time::ptime, + &AuditEntry::getModificationTime + >, + boost::multi_index::const_mem_fun< + AuditEntry, + uint64_t, + &AuditEntry::getEntryId + > > > > diff --git a/src/lib/database/tests/audit_entry_unittest.cc b/src/lib/database/tests/audit_entry_unittest.cc index a669949ab9..fbfdc1d2ae 100644 --- a/src/lib/database/tests/audit_entry_unittest.cc +++ b/src/lib/database/tests/audit_entry_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -91,11 +91,12 @@ TEST_F(AuditEntryTest, create) { ASSERT_NO_THROW(audit_entry = AuditEntry::create ("dhcp4_subnet", 10, AuditEntry::ModificationType::DELETE, - fixedTime(), "deleted subnet 10")); + fixedTime(), 123, "deleted subnet 10")); EXPECT_EQ("dhcp4_subnet", audit_entry->getObjectType()); EXPECT_EQ(10, audit_entry->getObjectId()); EXPECT_EQ(AuditEntry::ModificationType::DELETE, audit_entry->getModificationType()); EXPECT_EQ(fixedTime(), audit_entry->getModificationTime()); + EXPECT_EQ(123, audit_entry->getEntryId()); EXPECT_EQ("deleted subnet 10", audit_entry->getLogMessage()); } @@ -104,11 +105,12 @@ TEST_F(AuditEntryTest, create) { ASSERT_NO_THROW(audit_entry = AuditEntry::create ("dhcp4_option", 123, AuditEntry::ModificationType::CREATE, - "")); + 234, "")); EXPECT_EQ("dhcp4_option", audit_entry->getObjectType()); EXPECT_EQ(123, audit_entry->getObjectId()); EXPECT_EQ(AuditEntry::ModificationType::CREATE, audit_entry->getModificationType()); EXPECT_TRUE(almostEqualTime(audit_entry->getModificationTime())); + EXPECT_EQ(234, audit_entry->getEntryId()); EXPECT_TRUE(audit_entry->getLogMessage().empty()); } } @@ -118,7 +120,7 @@ TEST_F(AuditEntryTest, createFailures) { { SCOPED_TRACE("empty object type"); EXPECT_THROW(AuditEntry("", 10, AuditEntry::ModificationType::DELETE, - fixedTime(), "deleted subnet 10"), + fixedTime(), 123, "deleted subnet 10"), BadValue); } @@ -126,7 +128,8 @@ TEST_F(AuditEntryTest, createFailures) { SCOPED_TRACE("not a date time"); EXPECT_THROW(AuditEntry("dhcp4_subnet", 10, AuditEntry::ModificationType::DELETE, - boost::posix_time::ptime(), "deleted subnet 10"), + boost::posix_time::ptime(), 123, + "deleted subnet 10"), BadValue); } } @@ -171,17 +174,17 @@ public: /// the tests. void createTestAuditEntries() { create("dhcp4_subnet", 10, AuditEntry::ModificationType::CREATE, - diffTime(-5), "added subnet 10"); + diffTime(-5), 100, "added subnet 10"); create("dhcp4_shared_network", 1, AuditEntry::ModificationType::CREATE, - diffTime(-5), "added shared network 1"); + diffTime(-5), 110, "added shared network 1"); create("dhcp4_shared_network", 120, AuditEntry::ModificationType::UPDATE, - diffTime(-8), "updated shared network 120"); + diffTime(-8), 90, "updated shared network 120"); create("dhcp4_subnet", 120, AuditEntry::ModificationType::DELETE, - diffTime(8), "deleted subnet 120"); + diffTime(8), 130, "deleted subnet 120"); create("dhcp4_subnet", 1000, AuditEntry::ModificationType::CREATE, - diffTime(4), "created subnet 1000"); + diffTime(4), 120, "created subnet 1000"); create("dhcp4_option", 15, AuditEntry::ModificationType::UPDATE, - diffTime(16), "updated option 15"); + diffTime(16), 140, "updated option 15"); } /// @brief Checks if the returned results range contains an @c AuditEntry @@ -237,7 +240,7 @@ TEST_F(AuditEntryCollectionTest, getByObjectType) { // Checks that entries can be found by modification time. TEST_F(AuditEntryCollectionTest, getByModificationTime) { - const auto& mod_time_idx = audit_entries_.get(); + const auto& mod_time_idx = audit_entries_.get(); // Search for objects later than fixed time - 10s. auto lb = mod_time_idx.lower_bound(diffTime(-10)); @@ -282,4 +285,18 @@ TEST_F(AuditEntryCollectionTest, getByModificationTime) { ASSERT_EQ(0, std::distance(lb, mod_time_idx.end())); } +// Checks that entries can be found by modification time and id. +TEST_F(AuditEntryCollectionTest, getByModificationTimeAndId) { + const auto& mod_time_idx = audit_entries_.get(); + + // Search for objects later than added added subnet 10. + auto mod = boost::make_tuple(diffTime(-5), 100 + 1); + auto lb = mod_time_idx.lower_bound(mod); + ASSERT_EQ(4, std::distance(lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 120, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_subnet", 1000, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_shared_network", 1, lb, mod_time_idx.end())); + EXPECT_TRUE(includes("dhcp4_option", 15, lb, mod_time_idx.end())); +} + } diff --git a/src/lib/dhcpsrv/config_backend_dhcp4.h b/src/lib/dhcpsrv/config_backend_dhcp4.h index c958274587..4f8ee608e9 100644 --- a/src/lib/dhcpsrv/config_backend_dhcp4.h +++ b/src/lib/dhcpsrv/config_backend_dhcp4.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 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 @@ -301,10 +301,13 @@ public: /// @param server_selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const = 0; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const = 0; /// @brief Retrieves all servers. /// diff --git a/src/lib/dhcpsrv/config_backend_dhcp6.h b/src/lib/dhcpsrv/config_backend_dhcp6.h index 5f8ec3d1e9..b11056e55e 100644 --- a/src/lib/dhcpsrv/config_backend_dhcp6.h +++ b/src/lib/dhcpsrv/config_backend_dhcp6.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -302,10 +302,13 @@ public: /// @param server_selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const = 0; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const = 0; /// @brief Retrieves all servers. /// diff --git a/src/lib/dhcpsrv/config_backend_pool_dhcp4.cc b/src/lib/dhcpsrv/config_backend_pool_dhcp4.cc index cf09452020..cc2fb2b315 100644 --- a/src/lib/dhcpsrv/config_backend_pool_dhcp4.cc +++ b/src/lib/dhcpsrv/config_backend_pool_dhcp4.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 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 @@ -204,11 +204,12 @@ AuditEntryCollection ConfigBackendPoolDHCPv4:: getRecentAuditEntries(const db::BackendSelector& backend_selector, const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const { + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const { AuditEntryCollection audit_entries; getMultiplePropertiesConst (&ConfigBackendDHCPv4::getRecentAuditEntries, backend_selector, - server_selector, audit_entries, modification_time); + server_selector, audit_entries, modification_time, modification_id); return (audit_entries); } diff --git a/src/lib/dhcpsrv/config_backend_pool_dhcp4.h b/src/lib/dhcpsrv/config_backend_pool_dhcp4.h index 56497be43d..8e6ba8b725 100644 --- a/src/lib/dhcpsrv/config_backend_pool_dhcp4.h +++ b/src/lib/dhcpsrv/config_backend_pool_dhcp4.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 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 @@ -227,11 +227,14 @@ public: /// @param server_selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::BackendSelector& backend_selector, const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const; /// @brief Retrieves all servers from the particular backend. /// diff --git a/src/lib/dhcpsrv/config_backend_pool_dhcp6.cc b/src/lib/dhcpsrv/config_backend_pool_dhcp6.cc index d36afbd1c7..0539bc8697 100644 --- a/src/lib/dhcpsrv/config_backend_pool_dhcp6.cc +++ b/src/lib/dhcpsrv/config_backend_pool_dhcp6.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -204,11 +204,12 @@ AuditEntryCollection ConfigBackendPoolDHCPv6:: getRecentAuditEntries(const db::BackendSelector& backend_selector, const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const { + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const { AuditEntryCollection audit_entries; getMultiplePropertiesConst (&ConfigBackendDHCPv6::getRecentAuditEntries, backend_selector, - server_selector, audit_entries, modification_time); + server_selector, audit_entries, modification_time, modification_id); return (audit_entries); } diff --git a/src/lib/dhcpsrv/config_backend_pool_dhcp6.h b/src/lib/dhcpsrv/config_backend_pool_dhcp6.h index c79bb44abd..2765acb9a2 100644 --- a/src/lib/dhcpsrv/config_backend_pool_dhcp6.h +++ b/src/lib/dhcpsrv/config_backend_pool_dhcp6.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -226,11 +226,14 @@ public: /// @param server_selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::BackendSelector& backend_selector, const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const; /// @brief Retrieves all servers from the particular backend. /// diff --git a/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc b/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc index fac44f8163..bd94ce325c 100644 --- a/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc +++ b/src/lib/dhcpsrv/tests/cb_ctl_dhcp_unittest.cc @@ -70,7 +70,7 @@ public: void addCreateAuditEntry(const std::string& object_type) { AuditEntryPtr entry(new AuditEntry(object_type, 1234, AuditEntry::ModificationType::CREATE, - "some log message")); + 2345, "some log message")); audit_entries_.insert(entry); } @@ -86,7 +86,7 @@ public: const uint64_t object_id) { AuditEntryPtr entry(new AuditEntry(object_type, object_id, AuditEntry::ModificationType::DELETE, - "some log message")); + 1234, "some log message")); audit_entries_.insert(entry); } diff --git a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.cc b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.cc index edf2ea6f2f..0cbe3dd38c 100644 --- a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.cc +++ b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 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 @@ -511,7 +511,8 @@ TestConfigBackendDHCPv4::getModifiedGlobalParameters4(const db::ServerSelector& AuditEntryCollection TestConfigBackendDHCPv4::getRecentAuditEntries(const db::ServerSelector&, - const boost::posix_time::ptime&) const { + const boost::posix_time::ptime&, + const uint64_t&) const { return (AuditEntryCollection()); } diff --git a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.h b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.h index 20cde9869f..fcda52b148 100644 --- a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.h +++ b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp4.h @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 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 @@ -220,10 +220,13 @@ public: /// @param server_selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const; /// @brief Retrieves all servers. /// diff --git a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.cc b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.cc index 69b10d56f5..3cdf001334 100644 --- a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.cc +++ b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -510,7 +510,8 @@ TestConfigBackendDHCPv6::getModifiedGlobalParameters6(const db::ServerSelector& AuditEntryCollection TestConfigBackendDHCPv6::getRecentAuditEntries(const db::ServerSelector&, - const boost::posix_time::ptime&) const { + const boost::posix_time::ptime&, + const uint64_t&) const { return (AuditEntryCollection()); } diff --git a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.h b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.h index e598df977a..9a1d25a106 100644 --- a/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.h +++ b/src/lib/dhcpsrv/testutils/test_config_backend_dhcp6.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -220,10 +220,13 @@ public: /// @param server_selector Server selector. /// @param modification_time Timestamp being a lower limit for the returned /// result set, i.e. entries later than specified time are returned. + /// @param modification_id Identifier being a lower limit for the returned + /// result set, used when two (or more) entries have modification_time. /// @return Collection of audit entries. virtual db::AuditEntryCollection getRecentAuditEntries(const db::ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const; + const boost::posix_time::ptime& modification_time, + const uint64_t& modification_id) const; /// @brief Retrieves all servers. /// diff --git a/src/lib/process/cb_ctl_base.h b/src/lib/process/cb_ctl_base.h index e068efa158..37daaaea9a 100644 --- a/src/lib/process/cb_ctl_base.h +++ b/src/lib/process/cb_ctl_base.h @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -62,8 +62,8 @@ namespace process { /// - use the "config-control" specification to connect to the specified /// databases via the configuration backends, /// - fetch the audit trail to detect configuration updates, -/// - store the timestamp of the most recent audit entry fetched from the -/// database, so as next time it can fetch only the later updates. +/// - store the timestamp and id of the most recent audit entry fetched +/// from the database, so as next time it can fetch only the later updates. /// /// The server specific part to be implemented in derived classes must /// correctly interpret the audit entries and make appropriate API calls @@ -92,9 +92,11 @@ public: /// @brief Constructor. /// - /// Sets the time of the last fetched audit entry to Jan 1st, 1970. + /// Sets the time of the last fetched audit entry to Jan 1st, 1970, + /// with id 0. CBControlBase() - : last_audit_entry_time_(getInitialAuditEntryTime()) { + : last_audit_entry_time_(getInitialAuditEntryTime()), + last_audit_entry_id_(0) { } /// @brief Virtual destructor. @@ -107,10 +109,11 @@ public: /// @brief Resets the state of this object. /// /// Disconnects the configuration backends resets the recorded last - /// audit entry time. + /// audit entry time and id. void reset() { databaseConfigDisconnect(); last_audit_entry_time_ = getInitialAuditEntryTime(); + last_audit_entry_id_ = 0; } /// @brief (Re)connects to the specified configuration backends. @@ -200,13 +203,16 @@ public: // Save the timestamp indicating last audit entry time. auto lb_modification_time = last_audit_entry_time_; + // Save the identifier indicating last audit entry id. + auto lb_modification_id = last_audit_entry_id_; audit_entries = getMgr().getPool()->getRecentAuditEntries(backend_selector, server_selector, - lb_modification_time); + lb_modification_time, + lb_modification_id); // Store the last audit entry time. It should be set to the most recent // audit entry fetched. If returned audit is empty we don't update. - updateLastAuditEntryTime(audit_entries); + updateLastAuditEntryTimeId(audit_entries); // If this is full reconfiguration we don't need the audit entries anymore. // Let's remove them and proceed as if they don't exist. @@ -223,11 +229,12 @@ public: databaseConfigApply(backend_selector, server_selector, lb_modification_time, audit_entries); } catch (...) { - // Revert last audit entry time so as we can retry from the - // last successful attempt. + // Revert last audit entry time and id so as we can retry + // from the last successful attempt. /// @todo Consider reverting to the initial value to reload /// the entire configuration if the update failed. last_audit_entry_time_ = lb_modification_time; + last_audit_entry_id_ = lb_modification_id; throw; } } @@ -326,7 +333,7 @@ protected: /// returns without updating the timestamp. /// /// @param audit_entries reference to the collection of the fetched audit entries. - void updateLastAuditEntryTime(const db::AuditEntryCollection& audit_entries) { + void updateLastAuditEntryTimeId(const db::AuditEntryCollection& audit_entries) { // Do nothing if there are no audit entries. It is the case if // there were no updates to the configuration. if (audit_entries.empty()) { @@ -335,12 +342,21 @@ protected: // Get the audit entries sorted by modification time and pick the // latest entry. - const auto& index = audit_entries.get(); - last_audit_entry_time_ = ((*index.rbegin())->getModificationTime()); + const auto& index = audit_entries.get(); + last_audit_entry_time_ = (*index.rbegin())->getModificationTime(); + last_audit_entry_id_ = (*index.rbegin())->getEntryId(); } - /// @brief Stores the most recent audit entry. + /// @brief Stores the most recent audit entry timestamp. boost::posix_time::ptime last_audit_entry_time_; + + /// @brief Stores the most recent audit entry identifier. + /// + /// The identifier is used when two (or more) audit entries have + /// the same timestamp. It is not used by itself because timestamps + /// are more user friendly. Unfortunately old versions of MySQL do not + /// support millisecond timestamps. + uint64_t last_audit_entry_id_; }; } // end of namespace isc::process diff --git a/src/lib/process/tests/cb_ctl_base_unittests.cc b/src/lib/process/tests/cb_ctl_base_unittests.cc index eb27040e55..1d53fc9ce4 100644 --- a/src/lib/process/tests/cb_ctl_base_unittests.cc +++ b/src/lib/process/tests/cb_ctl_base_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2019-2020 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 @@ -39,19 +39,23 @@ public: /// /// @param modification_time The lower bound time for which audit /// entries should be returned. + /// @param modification_id The lower bound id for which audit + /// entries should be returned. /// /// @return Collection of audit entries later than specified time. virtual db::AuditEntryCollection getRecentAuditEntries(const db::ServerSelector&, - const boost::posix_time::ptime& modification_time) const { + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) const { db::AuditEntryCollection filtered_entries; // Use the index which orders the audit entries by timestamps. - const auto& index = audit_entries_.get(); + const auto& index = audit_entries_.get(); // Locate the first audit entry after the last one having the - // specified modification time. - auto first_entry = index.upper_bound(modification_time); + // specified modification time and id. + auto modification = boost::make_tuple(modification_time, modification_id); + auto first_entry = index.upper_bound(modification); // If there are any entries found return them. if (first_entry != index.end()) { @@ -66,15 +70,18 @@ public: /// @param object_type Object type to be stored in the audit entry. /// @param object_id Object id to be stored in the audit entry. /// @param modification_time Audit entry modification time to be set. + /// @param modification_id Audit entry modification id to be set. void addAuditEntry(const ServerSelector&, const std::string& object_type, const uint64_t object_id, - const boost::posix_time::ptime& modification_time) { + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) { // Create new audit entry from the specified parameters. AuditEntryPtr audit_entry(new AuditEntry(object_type, object_id, AuditEntry::ModificationType::CREATE, modification_time, + modification_id, "added audit entry")); // The audit entries are held in the static variable so as they @@ -143,15 +150,17 @@ public: /// @param object_type Object type to be stored in the audit entry. /// @param object_id Object id to be stored in the audit entry. /// @param modification_time Audit entry modification time to be set. + /// @param modification_id Audit entry modification id to be set. void addAuditEntry(const BackendSelector& backend_selector, const ServerSelector& server_selector, const std::string& object_type, const uint64_t object_id, - const boost::posix_time::ptime& modification_time) { + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) { createUpdateDeleteProperty + const boost::posix_time::ptime&, uint64_t> (&CBControlBackend::addAuditEntry, backend_selector, server_selector, - object_type, object_id, modification_time); + object_type, object_id, modification_time, modification_id); } /// @brief Retrieves the audit entries later than specified time. @@ -160,16 +169,20 @@ public: /// @param server_selector Server selector. /// @param modification_time The lower bound time for which audit /// entries should be returned. + /// @param modification_id The lower bound id for which audit + /// entries should be returned. /// /// @return Collection of audit entries later than specified time. virtual db::AuditEntryCollection getRecentAuditEntries(const BackendSelector& backend_selector, const ServerSelector& server_selector, - const boost::posix_time::ptime& modification_time) const { + const boost::posix_time::ptime& modification_time, + const uint64_t modification_id) const { AuditEntryCollection audit_entries; getMultiplePropertiesConst (&CBControlBackend::getRecentAuditEntries, backend_selector, - server_selector, audit_entries, modification_time); + server_selector, audit_entries, modification_time, + modification_id); return (audit_entries); } }; @@ -286,6 +299,11 @@ public: return (last_audit_entry_time_); } + /// @brief Returns the recorded id of last audit entry. + uint64_t getLastAuditEntryId() const { + return (last_audit_entry_id_); + } + /// @brief Overwrites the last audit entry time. /// /// @param last_audit_entry_time New time to be set. @@ -293,6 +311,13 @@ public: last_audit_entry_time_ = last_audit_entry_time; } + /// @brief Overwrites the last audit entry id. + /// + /// @param last_audit_entry_id New id to be set. + void setLastAuditEntryId(const uint64_t& last_audit_entry_id) { + last_audit_entry_id_ = last_audit_entry_id; + } + /// @brief Enables the @c databaseConfigApply function to throw. /// /// This is useful to test scenarios when configuration merge fails. @@ -400,6 +425,7 @@ TEST_F(CBControlBaseTest, reset) { cb_ctl_.setLastAuditEntryTime(timestamps_["tomorrow"]); cb_ctl_.reset(); EXPECT_EQ(cb_ctl_.getInitialAuditEntryTime(), cb_ctl_.getLastAuditEntryTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditEntryId()); } // This test verifies that it is correctly determined whether the @@ -418,7 +444,7 @@ TEST_F(CBControlBaseTest, fetchConfigElement) { // no reason to fetch the data from the database. AuditEntryPtr audit_entry(new AuditEntry("dhcp4_subnet", 1234 , AuditEntry::ModificationType::DELETE, - "added audit entry")); + 2345, "added audit entry")); audit_entries.insert(audit_entry); EXPECT_FALSE(cb_ctl_.fetchConfigElement(audit_entries, "my_object_type")); @@ -426,14 +452,14 @@ TEST_F(CBControlBaseTest, fetchConfigElement) { // This time we should get 'true'. audit_entry.reset(new AuditEntry("my_object_type", 5678, AuditEntry::ModificationType::CREATE, - "added audit entry")); + 6789, "added audit entry")); audit_entries.insert(audit_entry); EXPECT_TRUE(cb_ctl_.fetchConfigElement(audit_entries, "my_object_type")); // Also we should get 'true' for the UPDATE case. audit_entry.reset(new AuditEntry("another_object_type", 5678, AuditEntry::ModificationType::UPDATE, - "added audit entry")); + 6790, "added audit entry")); audit_entries.insert(audit_entry); EXPECT_TRUE(cb_ctl_.fetchConfigElement(audit_entries, "another_object_type")); } @@ -462,13 +488,15 @@ TEST_F(CBControlBaseTest, fetchAll) { ServerSelector::ALL(), "sql_table_2", 1234, - timestamps_["yesterday"]); + timestamps_["yesterday"], + 2345); cb_ctl_.getMgr().getPool()->addAuditEntry(BackendSelector::UNSPEC(), ServerSelector::ALL(), "sql_table_1", 3456, - timestamps_["today"]); + timestamps_["today"], + 4567); // Disconnect from the database in order to check that the // databaseConfigFetch reconnects. @@ -480,6 +508,7 @@ TEST_F(CBControlBaseTest, fetchAll) { ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); EXPECT_EQ(cb_ctl_.getInitialAuditEntryTime(), cb_ctl_.getLastAuditEntryTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditEntryId()); // Connect to the database and fetch the configuration. ASSERT_NO_THROW(cb_ctl_.databaseConfigFetch(config_base)); @@ -495,6 +524,7 @@ TEST_F(CBControlBaseTest, fetchAll) { // audit entry, so as the server will only later fetch config // updates after this timestamp. EXPECT_EQ(timestamps_["today"], cb_ctl_.getLastAuditEntryTime()); + EXPECT_EQ(4567, cb_ctl_.getLastAuditEntryId()); } // This test verifies that the configuration can be fetched for a @@ -510,6 +540,7 @@ TEST_F(CBControlBaseTest, fetchFromServer) { ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); EXPECT_EQ(cb_ctl_.getInitialAuditEntryTime(), cb_ctl_.getLastAuditEntryTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditEntryId()); ASSERT_NO_THROW(cb_ctl_.databaseConfigFetch(config_base)); @@ -540,7 +571,8 @@ TEST_F(CBControlBaseTest, fetchUpdates) { ServerSelector::ALL(), "sql_table_1", 3456, - timestamps_["today"]); + timestamps_["today"], + 4567); // Verify that various indicators are set to their initial values. ASSERT_EQ(0, cb_ctl_.getMergesNum()); @@ -548,6 +580,7 @@ TEST_F(CBControlBaseTest, fetchUpdates) { ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); EXPECT_EQ(cb_ctl_.getInitialAuditEntryTime(), cb_ctl_.getLastAuditEntryTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditEntryId()); ASSERT_NO_THROW(cb_ctl_.databaseConfigFetch(config_base, CBControl::FetchMode::FETCH_UPDATE)); @@ -560,6 +593,7 @@ TEST_F(CBControlBaseTest, fetchUpdates) { EXPECT_EQ(ServerSelector::Type::ALL, cb_ctl_.getServerSelector().getType()); // The last audit entry time should be set to the latest audit entry. EXPECT_EQ(timestamps_["today"], cb_ctl_.getLastAuditEntryTime()); + EXPECT_EQ(4567, cb_ctl_.getLastAuditEntryId()); } // Check that the databaseConfigApply function is not called when there @@ -571,6 +605,7 @@ TEST_F(CBControlBaseTest, fetchNoUpdates) { // entry we are going to add. That means that there will be // no new audit entries to fetch. cb_ctl_.setLastAuditEntryTime(timestamps_["yesterday"]); + cb_ctl_.setLastAuditEntryId(4567); ASSERT_TRUE(cb_ctl_.databaseConfigConnect(config_base)); @@ -578,7 +613,8 @@ TEST_F(CBControlBaseTest, fetchNoUpdates) { ServerSelector::ALL(), "sql_table_1", 3456, - timestamps_["yesterday"]); + timestamps_["yesterday"], + 4567); ASSERT_EQ(0, cb_ctl_.getMergesNum()); @@ -603,7 +639,8 @@ TEST_F(CBControlBaseTest, fetchFailure) { ServerSelector::ALL(), "sql_table_1", 3456, - timestamps_["today"]); + timestamps_["today"], + 4567); // Configure the CBControl to always throw simulating the failure // during configuration merge. @@ -615,6 +652,7 @@ TEST_F(CBControlBaseTest, fetchFailure) { ASSERT_EQ(ServerSelector::Type::UNASSIGNED, cb_ctl_.getServerSelector().getType()); ASSERT_EQ(-1, cb_ctl_.getAuditEntriesNum()); EXPECT_EQ(cb_ctl_.getInitialAuditEntryTime(), cb_ctl_.getLastAuditEntryTime()); + EXPECT_EQ(0, cb_ctl_.getLastAuditEntryId()); ASSERT_THROW(cb_ctl_.databaseConfigFetch(config_base, CBControl::FetchMode::FETCH_UPDATE), isc::Unexpected); @@ -628,7 +666,7 @@ TEST_F(CBControlBaseTest, fetchFailure) { // The last audit entry time should not be modified because there was a merge // error. EXPECT_EQ(cb_ctl_.getInitialAuditEntryTime(), cb_ctl_.getLastAuditEntryTime()); - + EXPECT_EQ(0, cb_ctl_.getLastAuditEntryId()); } }