diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index 5d467b259..a71974a1c 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -132,7 +132,7 @@ void IcingaDB::ConfigStaticInitialize() void IcingaDB::UpdateAllConfigObjects() { m_Rcon->Sync(); - m_Rcon->FireAndForgetQuery({"XADD", "icinga:schema", "MAXLEN", "1", "*", "version", "1"}, Prio::Heartbeat); + m_Rcon->FireAndForgetQuery({"XADD", "icinga:schema", "MAXLEN", "1", "*", "version", "2"}, Prio::Heartbeat); Log(LogInformation, "IcingaDB") << "Starting initial config/status dump"; double startTime = Utility::GetTime(); @@ -1536,6 +1536,9 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul if (!checkable) return; + if (!cr) + return; + Host::Ptr host; Service::Ptr service; @@ -1550,9 +1553,15 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul hard_state = service ? Convert::ToLong(service->GetLastHardState()) : Convert::ToLong(host->GetLastHardState()); } + auto eventTime (cr->GetExecutionEnd()); + auto eventTs (TimestampToMilliseconds(eventTime)); + + Array::Ptr rawId = new Array(Prepend(GetEnvironment(), GetObjectIdentifiersWithoutEnv(object))); + rawId->Add(eventTs); + std::vector xAdd ({ "XADD", "icinga:history:stream:state", "*", - "id", Utility::NewUniqueID(), + "id", HashValue(rawId), "environment_id", m_EnvironmentId, "host_id", GetObjectIdentifier(host), "state_type", Convert::ToString(type), @@ -1562,8 +1571,8 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul "previous_soft_state", Convert::ToString(GetPreviousState(checkable, service, StateTypeSoft)), "previous_hard_state", Convert::ToString(GetPreviousState(checkable, service, StateTypeHard)), "max_check_attempts", Convert::ToString(checkable->GetMaxCheckAttempts()), - "event_time", Convert::ToString(TimestampToMilliseconds(cr ? cr->GetExecutionEnd() : Utility::GetTime())), - "event_id", Utility::NewUniqueID(), + "event_time", Convert::ToString(eventTs), + "event_id", CalcEventID("state_change", object, eventTime), "event_type", "state_change" }); @@ -1609,7 +1618,7 @@ void IcingaDB::SendStateChange(const ConfigObject::Ptr& object, const CheckResul void IcingaDB::SendSentNotification( const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, - NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text + NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text, double sendTime ) { if (!m_Rcon || !m_Rcon->IsConnected()) @@ -1625,7 +1634,13 @@ void IcingaDB::SendSentNotification( } auto usersAmount (users.size()); - auto notificationHistoryId = Utility::NewUniqueID(); + auto sendTs (TimestampToMilliseconds(sendTime)); + + Array::Ptr rawId = new Array(Prepend(GetEnvironment(), GetObjectIdentifiersWithoutEnv(notification))); + rawId->Add(GetNotificationTypeByEnum(type)); + rawId->Add(sendTs); + + auto notificationHistoryId (HashValue(rawId)); std::vector xAdd ({ "XADD", "icinga:history:stream:notification", "*", @@ -1639,8 +1654,8 @@ void IcingaDB::SendSentNotification( "author", Utility::ValidateUTF8(author), "text", Utility::ValidateUTF8(finalText), "users_notified", Convert::ToString(usersAmount), - "send_time", Convert::ToString(TimestampToMilliseconds(Utility::GetTime())), - "event_id", Utility::NewUniqueID(), + "send_time", Convert::ToString(sendTs), + "event_id", CalcEventID("notification", notification, sendTime, type), "event_type", "notification" }); @@ -1701,7 +1716,7 @@ void IcingaDB::SendStartedDowntime(const Downtime::Ptr& downtime) "scheduled_end_time", Convert::ToString(TimestampToMilliseconds(downtime->GetEndTime())), "has_been_cancelled", Convert::ToString((unsigned short)downtime->GetWasCancelled()), "trigger_time", Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime())), - "event_id", Utility::NewUniqueID(), + "event_id", CalcEventID("downtime_start", downtime), "event_type", "downtime_start" }); @@ -1788,7 +1803,7 @@ void IcingaDB::SendRemovedDowntime(const Downtime::Ptr& downtime) "has_been_cancelled", Convert::ToString((unsigned short)downtime->GetWasCancelled()), "trigger_time", Convert::ToString(TimestampToMilliseconds(downtime->GetTriggerTime())), "cancel_time", Convert::ToString(TimestampToMilliseconds(Utility::GetTime())), - "event_id", Utility::NewUniqueID(), + "event_id", CalcEventID("downtime_end", downtime), "event_type", "downtime_end" }); @@ -1865,7 +1880,7 @@ void IcingaDB::SendAddedComment(const Comment::Ptr& comment) "entry_type", Convert::ToString(comment->GetEntryType()), "is_persistent", Convert::ToString((unsigned short)comment->GetPersistent()), "is_sticky", Convert::ToString((unsigned short)(comment->GetEntryType() == CommentAcknowledgement && comment->GetCheckable()->GetAcknowledgement() == AcknowledgementSticky)), - "event_id", Utility::NewUniqueID(), + "event_id", CalcEventID("comment_add", comment), "event_type", "comment_add" }); @@ -1922,7 +1937,7 @@ void IcingaDB::SendRemovedComment(const Comment::Ptr& comment) "entry_type", Convert::ToString(comment->GetEntryType()), "is_persistent", Convert::ToString((unsigned short)comment->GetPersistent()), "is_sticky", Convert::ToString((unsigned short)(comment->GetEntryType() == CommentAcknowledgement && comment->GetCheckable()->GetAcknowledgement() == AcknowledgementSticky)), - "event_id", Utility::NewUniqueID(), + "event_id", CalcEventID("comment_remove", comment), "event_type", "comment_remove" }); @@ -1983,8 +1998,7 @@ void IcingaDB::SendFlappingChange(const Checkable::Ptr& checkable, double change "environment_id", m_EnvironmentId, "host_id", GetObjectIdentifier(host), "flapping_threshold_low", Convert::ToString(checkable->GetFlappingThresholdLow()), - "flapping_threshold_high", Convert::ToString(checkable->GetFlappingThresholdHigh()), - "event_id", Utility::NewUniqueID() + "flapping_threshold_high", Convert::ToString(checkable->GetFlappingThresholdHigh()) }); if (service) { @@ -2026,6 +2040,8 @@ void IcingaDB::SendFlappingChange(const Checkable::Ptr& checkable, double change xAdd.emplace_back("start_time"); xAdd.emplace_back(Convert::ToString(startTime)); + xAdd.emplace_back("event_id"); + xAdd.emplace_back(CalcEventID(checkable->IsFlapping() ? "flapping_start" : "flapping_end", checkable, startTime)); xAdd.emplace_back("id"); xAdd.emplace_back(HashValue(new Array({GetEnvironment(), checkable->GetName(), startTime}))); @@ -2070,7 +2086,6 @@ void IcingaDB::SendAcknowledgementSet(const Checkable::Ptr& checkable, const Str std::vector xAdd ({ "XADD", "icinga:history:stream:acknowledgement", "*", - "event_id", Utility::NewUniqueID(), "environment_id", m_EnvironmentId, "host_id", GetObjectIdentifier(host), "event_type", "ack_set", @@ -2106,6 +2121,8 @@ void IcingaDB::SendAcknowledgementSet(const Checkable::Ptr& checkable, const Str xAdd.emplace_back("set_time"); xAdd.emplace_back(Convert::ToString(setTime)); + xAdd.emplace_back("event_id"); + xAdd.emplace_back(CalcEventID("ack_set", checkable, setTime)); xAdd.emplace_back("id"); xAdd.emplace_back(HashValue(new Array({GetEnvironment(), checkable->GetName(), setTime}))); @@ -2123,7 +2140,6 @@ void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const std::vector xAdd ({ "XADD", "icinga:history:stream:acknowledgement", "*", - "event_id", Utility::NewUniqueID(), "environment_id", m_EnvironmentId, "host_id", GetObjectIdentifier(host), "clear_time", Convert::ToString(TimestampToMilliseconds(changeTime)), @@ -2151,6 +2167,8 @@ void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const xAdd.emplace_back("set_time"); xAdd.emplace_back(Convert::ToString(setTime)); + xAdd.emplace_back("event_id"); + xAdd.emplace_back(CalcEventID("ack_clear", checkable, setTime)); xAdd.emplace_back("id"); xAdd.emplace_back(HashValue(new Array({GetEnvironment(), checkable->GetName(), setTime}))); @@ -2389,10 +2407,11 @@ void IcingaDB::NotificationSentToAllUsersHandler( ) { auto rws (ConfigType::GetObjectsByType()); + auto sendTime (notification->GetLastNotification()); if (!rws.empty()) { for (auto& rw : rws) { - rw->SendSentNotification(notification, checkable, users, type, cr, author, text); + rw->SendSentNotification(notification, checkable, users, type, cr, author, text, sendTime); } } } diff --git a/lib/icingadb/icingadb-utility.cpp b/lib/icingadb/icingadb-utility.cpp index 8ca9e298e..3b1713cde 100644 --- a/lib/icingadb/icingadb-utility.cpp +++ b/lib/icingadb/icingadb-utility.cpp @@ -80,6 +80,28 @@ String IcingaDB::GetObjectIdentifier(const ConfigObject::Ptr& object) return HashValue(new Array(Prepend(GetEnvironment(), GetObjectIdentifiersWithoutEnv(object)))); } +/** + * Calculates a deterministic history event ID like SHA1(env, eventType, x...[, nt][, eventTime]) + * + * Where SHA1(env, x...) = GetObjectIdentifier(object) + */ +String IcingaDB::CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime, NotificationType nt) +{ + Array::Ptr rawId = new Array(GetObjectIdentifiersWithoutEnv(object)); + rawId->Insert(0, GetEnvironment()); + rawId->Insert(1, eventType); + + if (nt) { + rawId->Add(GetNotificationTypeByEnum(nt)); + } + + if (eventTime) { + rawId->Add(TimestampToMilliseconds(eventTime)); + } + + return HashValue(std::move(rawId)); +} + static const std::set metadataWhitelist ({"package", "source_location", "templates"}); /** @@ -149,6 +171,32 @@ Dictionary::Ptr IcingaDB::SerializeVars(const CustomVarObject::Ptr& object) return res; } +const char* IcingaDB::GetNotificationTypeByEnum(NotificationType type) +{ + switch (type) { + case NotificationDowntimeStart: + return "downtime_start"; + case NotificationDowntimeEnd: + return "downtime_end"; + case NotificationDowntimeRemoved: + return "downtime_removed"; + case NotificationCustom: + return "custom"; + case NotificationAcknowledgement: + return "acknowledgement"; + case NotificationProblem: + return "problem"; + case NotificationRecovery: + return "recovery"; + case NotificationFlappingStart: + return "flapping_start"; + case NotificationFlappingEnd: + return "flapping_end"; + } + + VERIFY(!"Invalid notification type."); +} + static const std::set propertiesBlacklistEmpty; String IcingaDB::HashValue(const Value& value) diff --git a/lib/icingadb/icingadb.hpp b/lib/icingadb/icingadb.hpp index df49fd17d..eef4267bf 100644 --- a/lib/icingadb/icingadb.hpp +++ b/lib/icingadb/icingadb.hpp @@ -81,7 +81,7 @@ private: void SendSentNotification( const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, - NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text + NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text, double sendTime ); void SendStartedDowntime(const Downtime::Ptr& downtime); @@ -107,8 +107,10 @@ private: static ArrayData GetObjectIdentifiersWithoutEnv(const ConfigObject::Ptr& object); static String GetObjectIdentifier(const ConfigObject::Ptr& object); + static String CalcEventID(const char* eventType, const ConfigObject::Ptr& object, double eventTime = 0, NotificationType nt = NotificationType(0)); static String GetEnvironment(); static Dictionary::Ptr SerializeVars(const CustomVarObject::Ptr& object); + static const char* GetNotificationTypeByEnum(NotificationType type); static String HashValue(const Value& value); static String HashValue(const Value& value, const std::set& propertiesBlacklist, bool propertiesWhitelist = false);