diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index c4a3edb6a..7d4f1f5c2 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -244,8 +244,10 @@ Dictionary::Ptr ApiActions::RemoveAcknowledgement(const ConfigObject::Ptr& objec "Cannot remove acknowledgement for non-existent checkable object " + object->GetName() + "."); - checkable->ClearAcknowledgement(); - checkable->RemoveCommentsByType(CommentAcknowledgement, HttpUtility::GetLastParameter(params, "author")); + String removedBy (HttpUtility::GetLastParameter(params, "author")); + + checkable->ClearAcknowledgement(removedBy); + checkable->RemoveCommentsByType(CommentAcknowledgement, removedBy); return ApiActions::CreateResult(200, "Successfully removed acknowledgement for object '" + checkable->GetName() + "'."); } diff --git a/lib/icinga/apievents.cpp b/lib/icinga/apievents.cpp index 73eef3e48..1bec0e35a 100644 --- a/lib/icinga/apievents.cpp +++ b/lib/icinga/apievents.cpp @@ -221,7 +221,7 @@ void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, inboxes.Push(std::move(result)); } -void ApiEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin) +void ApiEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("AcknowledgementCleared"); auto inboxes (EventsRouter::GetInstance().GetInboxes(EventType::AcknowledgementCleared)); diff --git a/lib/icinga/apievents.hpp b/lib/icinga/apievents.hpp index e4dcc7835..cc9f1b1a5 100644 --- a/lib/icinga/apievents.hpp +++ b/lib/icinga/apievents.hpp @@ -30,7 +30,7 @@ public: static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin); - static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin); + static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy, const MessageOrigin::Ptr& origin); static void CommentAddedHandler(const Comment::Ptr& comment); static void CommentRemovedHandler(const Comment::Ptr& comment); diff --git a/lib/icinga/checkable-check.cpp b/lib/icinga/checkable-check.cpp index 42d563ea6..49aeaf223 100644 --- a/lib/icinga/checkable-check.cpp +++ b/lib/icinga/checkable-check.cpp @@ -280,7 +280,7 @@ void Checkable::ProcessCheckResult(const CheckResult::Ptr& cr, const MessageOrig /* remove acknowledgements */ if (GetAcknowledgement() == AcknowledgementNormal || (GetAcknowledgement() == AcknowledgementSticky && IsStateOK(new_state))) { - ClearAcknowledgement(); + ClearAcknowledgement(""); } /* reschedule direct parents */ diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index 36a5a6c79..cf2d7c9d2 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -16,7 +16,7 @@ REGISTER_TYPE_WITH_PROTOTYPE(Checkable, Checkable::GetPrototype()); INITIALIZE_ONCE(&Checkable::StaticInitialize); boost::signals2::signal Checkable::OnAcknowledgementSet; -boost::signals2::signal Checkable::OnAcknowledgementCleared; +boost::signals2::signal Checkable::OnAcknowledgementCleared; static Timer::Ptr l_CheckablesFireSuppressedNotifications; @@ -110,7 +110,7 @@ AcknowledgementType Checkable::GetAcknowledgement() if (expiry != 0 && expiry < Utility::GetTime()) { avalue = AcknowledgementNone; - ClearAcknowledgement(); + ClearAcknowledgement(""); } } @@ -136,7 +136,7 @@ void Checkable::AcknowledgeProblem(const String& author, const String& comment, OnAcknowledgementSet(this, author, comment, type, notify, persistent, expiry, origin); } -void Checkable::ClearAcknowledgement(const MessageOrigin::Ptr& origin) +void Checkable::ClearAcknowledgement(const String& removedBy, const MessageOrigin::Ptr& origin) { SetAcknowledgementRaw(AcknowledgementNone); SetAcknowledgementExpiry(0); @@ -144,7 +144,7 @@ void Checkable::ClearAcknowledgement(const MessageOrigin::Ptr& origin) Log(LogInformation, "Checkable") << "Acknowledgement cleared for checkable '" << GetName() << "'."; - OnAcknowledgementCleared(this, origin); + OnAcknowledgementCleared(this, removedBy, origin); } Endpoint::Ptr Checkable::GetCommandEndpoint() const diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index a5d34725b..b9130f87a 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -69,7 +69,7 @@ public: AcknowledgementType GetAcknowledgement(); void AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, bool notify = true, bool persistent = false, double expiry = 0, const MessageOrigin::Ptr& origin = nullptr); - void ClearAcknowledgement(const MessageOrigin::Ptr& origin = nullptr); + void ClearAcknowledgement(const String& removedBy, const MessageOrigin::Ptr& origin = nullptr); int GetSeverity() const override; bool GetProblem() const override; @@ -113,7 +113,7 @@ public: const String&, const MessageOrigin::Ptr&)> OnNotificationSentToAllUsers; static boost::signals2::signal OnAcknowledgementSet; - static boost::signals2::signal OnAcknowledgementCleared; + static boost::signals2::signal OnAcknowledgementCleared; static boost::signals2::signal OnNextCheckUpdated; static boost::signals2::signal OnEventCommandExecuted; diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 313adb1eb..5d8f236ef 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -542,7 +542,7 @@ Value ClusterEvents::AcknowledgementSetAPIHandler(const MessageOrigin::Ptr& orig return Empty; } -void ClusterEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin) +void ClusterEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy, const MessageOrigin::Ptr& origin) { ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -557,6 +557,7 @@ void ClusterEvents::AcknowledgementClearedHandler(const Checkable::Ptr& checkabl params->Set("host", host->GetName()); if (service) params->Set("service", service->GetShortName()); + params->Set("author", removedBy); Dictionary::Ptr message = new Dictionary(); message->Set("jsonrpc", "2.0"); @@ -598,7 +599,7 @@ Value ClusterEvents::AcknowledgementClearedAPIHandler(const MessageOrigin::Ptr& return Empty; } - checkable->ClearAcknowledgement(origin); + checkable->ClearAcknowledgement(params->Get("author"), origin); return Empty; } diff --git a/lib/icinga/clusterevents.hpp b/lib/icinga/clusterevents.hpp index 8dc6f48b9..d814ebd49 100644 --- a/lib/icinga/clusterevents.hpp +++ b/lib/icinga/clusterevents.hpp @@ -42,7 +42,7 @@ public: bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin); static Value AcknowledgementSetAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); - static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin); + static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy, const MessageOrigin::Ptr& origin); static Value AcknowledgementClearedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static Value ExecuteCommandAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index 9f088ae67..a0720a0be 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -639,7 +639,7 @@ void ExternalCommandProcessor::RemoveSvcAcknowledgement(double, const std::vecto { ObjectLock olock(service); - service->ClearAcknowledgement(); + service->ClearAcknowledgement(""); } service->RemoveCommentsByType(CommentAcknowledgement); @@ -703,7 +703,7 @@ void ExternalCommandProcessor::RemoveHostAcknowledgement(double, const std::vect { ObjectLock olock(host); - host->ClearAcknowledgement(); + host->ClearAcknowledgement(""); } host->RemoveCommentsByType(CommentAcknowledgement); } diff --git a/lib/icingadb/icingadb-objects.cpp b/lib/icingadb/icingadb-objects.cpp index 5c7e26517..f972546a1 100644 --- a/lib/icingadb/icingadb-objects.cpp +++ b/lib/icingadb/icingadb-objects.cpp @@ -60,8 +60,15 @@ void IcingaDB::ConfigStaticInitialize() IcingaDB::StateChangeHandler(checkable, cr, type); }); + Checkable::OnAcknowledgementSet.connect([](const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool, bool persistent, double expiry, const MessageOrigin::Ptr&) { + AcknowledgementSetHandler(checkable, author, comment, type, persistent, expiry); + }); + Checkable::OnAcknowledgementCleared.connect([](const Checkable::Ptr& checkable, const String& removedBy, const MessageOrigin::Ptr&) { + AcknowledgementClearedHandler(checkable, removedBy); + }); + /* triggered when acknowledged host/service goes back to ok and when the acknowledgement gets deleted */ - Checkable::OnAcknowledgementCleared.connect([](const Checkable::Ptr& checkable, const MessageOrigin::Ptr&) { + Checkable::OnAcknowledgementCleared.connect([](const Checkable::Ptr& checkable, const String&, const MessageOrigin::Ptr&) { IcingaDB::StateChangeHandler(checkable); }); @@ -1662,6 +1669,92 @@ void IcingaDB::SendNextUpdate(const Checkable::Ptr& checkable) ); } +void IcingaDB::SendAcknowledgementSet(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool persistent, double expiry) +{ + if (!m_Rcon || !m_Rcon->IsConnected()) + return; + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + std::vector xAdd ({ + "XADD", "icinga:history:stream:acknowledgement", "*", + "id", Utility::NewUniqueID(), + "environment_id", SHA1(GetEnvironment()), + "host_id", GetObjectIdentifier(host), + "event_time", Convert::ToString(TimestampToMilliseconds(Utility::GetTime())), + "event_type", "ack_set", + "author", author, + "comment", comment, + "is_sticky", Convert::ToString((unsigned short)(type == AcknowledgementSticky)), + "is_persistent", Convert::ToString((unsigned short)persistent) + }); + + if (service) { + xAdd.emplace_back("object_type"); + xAdd.emplace_back("service"); + xAdd.emplace_back("service_id"); + xAdd.emplace_back(GetObjectIdentifier(checkable)); + } else { + xAdd.emplace_back("object_type"); + xAdd.emplace_back("host"); + } + + auto endpoint (Endpoint::GetLocalEndpoint()); + + if (endpoint) { + xAdd.emplace_back("endpoint_id"); + xAdd.emplace_back(GetObjectIdentifier(endpoint)); + } + + if (expiry > 0) { + xAdd.emplace_back("expire_time"); + xAdd.emplace_back(Convert::ToString(TimestampToMilliseconds(expiry))); + } + + m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History); +} + +void IcingaDB::SendAcknowledgementCleared(const Checkable::Ptr& checkable, const String& removedBy) +{ + if (!m_Rcon || !m_Rcon->IsConnected()) + return; + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + std::vector xAdd ({ + "XADD", "icinga:history:stream:acknowledgement", "*", + "id", Utility::NewUniqueID(), + "environment_id", SHA1(GetEnvironment()), + "host_id", GetObjectIdentifier(host), + "event_time", Convert::ToString(TimestampToMilliseconds(Utility::GetTime())), + "event_type", "ack_clear", + "author", removedBy + }); + + if (service) { + xAdd.emplace_back("object_type"); + xAdd.emplace_back("service"); + xAdd.emplace_back("service_id"); + xAdd.emplace_back(GetObjectIdentifier(checkable)); + } else { + xAdd.emplace_back("object_type"); + xAdd.emplace_back("host"); + } + + auto endpoint (Endpoint::GetLocalEndpoint()); + + if (endpoint) { + xAdd.emplace_back("endpoint_id"); + xAdd.emplace_back(GetObjectIdentifier(endpoint)); + } + + m_Rcon->FireAndForgetQuery(std::move(xAdd), Prio::History); +} + Dictionary::Ptr IcingaDB::SerializeState(const Checkable::Ptr& checkable) { Dictionary::Ptr attrs = new Dictionary(); @@ -1907,3 +2000,35 @@ void IcingaDB::NewCheckResultHandler(const Checkable::Ptr& checkable) rw->m_WorkQueue.Enqueue([rw, checkable]() { rw->SendNextUpdate(checkable); }); } } + +struct AuthorComment +{ + String Author; + String Comment; +}; + +void IcingaDB::AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool persistent, double expiry) +{ + auto rws (ConfigType::GetObjectsByType()); + + if (!rws.empty()) { + auto ac (Shared::Make(AuthorComment{author, comment})); + + for (auto& rw : rws) { + rw->m_WorkQueue.Enqueue([rw, checkable, ac, type, persistent, expiry]() { rw->SendAcknowledgementSet(checkable, ac->Author, ac->Comment, type, persistent, expiry); }); + } + } +} + +void IcingaDB::AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy) +{ + auto rws (ConfigType::GetObjectsByType()); + + if (!rws.empty()) { + auto rb (Shared::Make(removedBy)); + + for (auto& rw : rws) { + rw->m_WorkQueue.Enqueue([rw, checkable, rb]() { rw->SendAcknowledgementCleared(checkable, *rb); }); + } + } +} diff --git a/lib/icingadb/icingadb.hpp b/lib/icingadb/icingadb.hpp index a1ae85c80..2ac8ab6bf 100644 --- a/lib/icingadb/icingadb.hpp +++ b/lib/icingadb/icingadb.hpp @@ -67,6 +67,8 @@ private: void SendRemovedComment(const Comment::Ptr& comment); void SendFlappingChanged(const Checkable::Ptr& checkable, const Value& value); void SendNextUpdate(const Checkable::Ptr& checkable); + void SendAcknowledgementSet(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool persistent, double expiry); + void SendAcknowledgementCleared(const Checkable::Ptr& checkable, const String& removedBy); std::vector UpdateObjectAttrs(const ConfigObject::Ptr& object, int fieldType, const String& typeNameOverride); Dictionary::Ptr SerializeState(const Checkable::Ptr& checkable); @@ -109,6 +111,8 @@ private: static void CommentRemovedHandler(const Comment::Ptr& comment); static void FlappingChangedHandler(const Checkable::Ptr& checkable, const Value& value); static void NewCheckResultHandler(const Checkable::Ptr& checkable); + static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, bool persistent, double expiry); + static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const String& removedBy); void AssertOnWorkQueue();