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);
-// 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
/// @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.
///
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);
-// 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
/// @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.
///
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 = {
MySqlBinding::createInteger<uint64_t>(), // object_id
MySqlBinding::createInteger<uint8_t>(), // modification_type
MySqlBinding::createTimestamp(), // modification_time
+ MySqlBinding::createInteger<uint64_t>(), // revision_id
MySqlBinding::createString(AUDIT_ENTRY_LOG_MESSAGE_BUF_LENGTH) // log_message
};
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<uint64_t>(modification_id)
};
// Execute select.
out_bindings[2]->getInteger<uint64_t>(),
mod_type,
out_bindings[4]->getTimestamp(),
- out_bindings[5]->getStringOrDefault(""));
+ out_bindings[5]->getInteger<uint64_t>(),
+ out_bindings[6]->getStringOrDefault(""));
audit_entries.insert(audit_entry);
});
}
-// 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
/// @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.
-// 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 <cstddef>
#include <log/message_types.h>
"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",
-// 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
-# Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC")
+# Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC")
$NAMESPACE isc::cb
% 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
" 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
std::string logExistingAuditEntries(const std::string& server_tag) {
std::ostringstream s;
- auto& mod_time_idx = audit_entries_[server_tag].get<AuditEntryModificationTimeTag>();
+ auto& mod_time_idx = audit_entries_[server_tag].get<AuditEntryModificationTimeIdTag>();
for (auto audit_entry_it = mod_time_idx.begin();
audit_entry_it != mod_time_idx.end();
<< audit_entry->getObjectId() << ", "
<< static_cast<int>(audit_entry->getModificationType()) << ", "
<< audit_entry->getModificationTime() << ", "
+ << audit_entry->getEntryId() << ", "
<< audit_entry->getLogMessage()
<< std::endl;
}
// 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<AuditEntryModificationTimeTag>();
+ auto& mod_time_idx = audit_entries_[tag].get<AuditEntryModificationTimeIdTag>();
// Iterate over specified number of entries starting from the most recent
// one and check they have correct values.
std::string logExistingAuditEntries(const std::string& server_tag) {
std::ostringstream s;
- auto& mod_time_idx = audit_entries_[server_tag].get<AuditEntryModificationTimeTag>();
+ auto& mod_time_idx = audit_entries_[server_tag].get<AuditEntryModificationTimeIdTag>();
for (auto audit_entry_it = mod_time_idx.begin();
audit_entry_it != mod_time_idx.end();
<< audit_entry->getObjectId() << ", "
<< static_cast<int>(audit_entry->getModificationType()) << ", "
<< audit_entry->getModificationTime() << ", "
+ << audit_entry->getEntryId() << ", "
<< audit_entry->getLogMessage()
<< std::endl;
}
// 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<AuditEntryModificationTimeTag>();
+ auto& mod_time_idx = audit_entries_[tag].get<AuditEntryModificationTimeIdTag>();
// Iterate over specified number of entries starting from the most recent
// one and check they have correct values.
-// 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
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();
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();
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<AuditEntry>(object_type, object_id,
modification_type,
- modification_time,
+ modification_time, id,
log_message));
}
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<AuditEntry>(object_type, object_id,
- modification_type,
+ modification_type, id,
log_message));
}
-// 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
/// 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
/// - 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
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.
/// @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.
/// @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.
///
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.
/// @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.
///
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.
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.
/// @brief Modification time.
boost::posix_time::ptime modification_time_;
+ /// @brief Entry id.
+ uint64_t id_;
+
/// @brief Log message.
std::string log_message_;
};
struct AuditEntryObjectTypeTag { };
/// @brief Tag used to access index by modification time.
-struct AuditEntryModificationTimeTag { };
+struct AuditEntryModificationTimeIdTag { };
/// @brief Multi index container holding @c AuditEntry instances.
///
>
>,
- // 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<AuditEntryModificationTimeTag>,
- boost::multi_index::const_mem_fun<
+ boost::multi_index::tag<AuditEntryModificationTimeIdTag>,
+ 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
+ >
>
>
>
-// 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
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());
}
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());
}
}
{
SCOPED_TRACE("empty object type");
EXPECT_THROW(AuditEntry("", 10, AuditEntry::ModificationType::DELETE,
- fixedTime(), "deleted subnet 10"),
+ fixedTime(), 123, "deleted subnet 10"),
BadValue);
}
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);
}
}
/// 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
// Checks that entries can be found by modification time.
TEST_F(AuditEntryCollectionTest, getByModificationTime) {
- const auto& mod_time_idx = audit_entries_.get<AuditEntryModificationTimeTag>();
+ const auto& mod_time_idx = audit_entries_.get<AuditEntryModificationTimeIdTag>();
// Search for objects later than fixed time - 10s.
auto lb = mod_time_idx.lower_bound(diffTime(-10));
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<AuditEntryModificationTimeIdTag>();
+
+ // 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()));
+}
+
}
-// 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
/// @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.
///
-// 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
/// @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.
///
-// 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
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<AuditEntryCollection, const boost::posix_time::ptime&>
(&ConfigBackendDHCPv4::getRecentAuditEntries, backend_selector,
- server_selector, audit_entries, modification_time);
+ server_selector, audit_entries, modification_time, modification_id);
return (audit_entries);
}
-// 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
/// @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.
///
-// 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
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<AuditEntryCollection, const boost::posix_time::ptime&>
(&ConfigBackendDHCPv6::getRecentAuditEntries, backend_selector,
- server_selector, audit_entries, modification_time);
+ server_selector, audit_entries, modification_time, modification_id);
return (audit_entries);
}
-// 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
/// @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.
///
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);
}
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);
}
-// 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
AuditEntryCollection
TestConfigBackendDHCPv4::getRecentAuditEntries(const db::ServerSelector&,
- const boost::posix_time::ptime&) const {
+ const boost::posix_time::ptime&,
+ const uint64_t&) const {
return (AuditEntryCollection());
}
-// 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
/// @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.
///
-// 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
AuditEntryCollection
TestConfigBackendDHCPv6::getRecentAuditEntries(const db::ServerSelector&,
- const boost::posix_time::ptime&) const {
+ const boost::posix_time::ptime&,
+ const uint64_t&) const {
return (AuditEntryCollection());
}
-// 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
/// @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.
///
-// 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
/// - 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
/// @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.
/// @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.
// 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.
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;
}
}
/// 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()) {
// Get the audit entries sorted by modification time and pick the
// latest entry.
- const auto& index = audit_entries.get<db::AuditEntryModificationTimeTag>();
- last_audit_entry_time_ = ((*index.rbegin())->getModificationTime());
+ const auto& index = audit_entries.get<db::AuditEntryModificationTimeIdTag>();
+ 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
-// 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
///
/// @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<AuditEntryModificationTimeTag>();
+ const auto& index = audit_entries_.get<AuditEntryModificationTimeIdTag>();
// 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()) {
/// @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
/// @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<void, const std::string&, uint64_t,
- const boost::posix_time::ptime&>
+ 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.
/// @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<AuditEntryCollection, const boost::posix_time::ptime&>
(&CBControlBackend::getRecentAuditEntries, backend_selector,
- server_selector, audit_entries, modification_time);
+ server_selector, audit_entries, modification_time,
+ modification_id);
return (audit_entries);
}
};
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.
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.
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
// 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"));
// 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"));
}
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.
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));
// 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
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));
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());
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));
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
// 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));
ServerSelector::ALL(),
"sql_table_1",
3456,
- timestamps_["yesterday"]);
+ timestamps_["yesterday"],
+ 4567);
ASSERT_EQ(0, cb_ctl_.getMergesNum());
ServerSelector::ALL(),
"sql_table_1",
3456,
- timestamps_["today"]);
+ timestamps_["today"],
+ 4567);
// Configure the CBControl to always throw simulating the failure
// during configuration merge.
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);
// 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());
}
}