From 273ca6a2cca1d11f34e13a497cc1cfce61346185 Mon Sep 17 00:00:00 2001 From: Rune Darrud Date: Wed, 25 Jan 2017 21:21:22 +0100 Subject: [PATCH] Fix persistent comments for Acknowledgements fixes #4818 Signed-off-by: Michael Friedrich --- doc/12-icinga2-api.md | 15 ++++++++------- doc/9-object-types.md | 1 + lib/db_ido/dbevents.cpp | 2 +- lib/icinga/apiactions.cpp | 7 +++++-- lib/icinga/apievents.cpp | 3 ++- lib/icinga/apievents.hpp | 2 +- lib/icinga/checkable-comment.cpp | 4 ++++ lib/icinga/checkable.cpp | 6 +++--- lib/icinga/checkable.hpp | 4 ++-- lib/icinga/clusterevents.cpp | 4 ++-- lib/icinga/clusterevents.hpp | 2 +- lib/icinga/comment.cpp | 12 +++++++++--- lib/icinga/comment.hpp | 2 +- lib/icinga/comment.ti | 1 + lib/icinga/externalcommandprocessor.cpp | 24 ++++++++++++++---------- 15 files changed, 55 insertions(+), 34 deletions(-) diff --git a/doc/12-icinga2-api.md b/doc/12-icinga2-api.md index 0c92e3af2..594c8da56 100644 --- a/doc/12-icinga2-api.md +++ b/doc/12-icinga2-api.md @@ -905,13 +905,14 @@ are disabled. Send a `POST` request to the URL endpoint `/v1/actions/acknowledge-problem`. - Parameter | Type | Description - ----------|-----------|-------------- - author | string | **Required.** Name of the author, may be empty. - comment | string | **Required.** Comment text, may be empty. - expiry | timestamp | **Optional.** Whether the acknowledgement will be removed at the timestamp. - sticky | boolean | **Optional.** Whether the acknowledgement will be set until the service or host fully recovers. Defaults to `false`. - notify | boolean | **Optional.** Whether a notification of the `Acknowledgement` type will be sent. Defaults to `false`. + Parameter | Type | Description + ---------------------|-----------|-------------- + author | string | **Required.** Name of the author, may be empty. + comment | string | **Required.** Comment text, may be empty. + expiry | timestamp | **Optional.** Whether the acknowledgement will be removed at the timestamp. + sticky | boolean | **Optional.** Whether the acknowledgement will be set until the service or host fully recovers. Defaults to `false`. + notify | boolean | **Optional.** Whether a notification of the `Acknowledgement` type will be sent. Defaults to `false`. + persistent | boolean | **Optional.** When the comment is of type `Acknowledgement` and this is set to `true`, the comment will remain after the acknowledgement recovers or expires. Defaults to `false`. In addition to these parameters a [filter](12-icinga2-api.md#icinga2-api-filters) must be provided. The valid types for this action are `Host` and `Service`. diff --git a/doc/9-object-types.md b/doc/9-object-types.md index 0dda7f9c1..d7b53b444 100644 --- a/doc/9-object-types.md +++ b/doc/9-object-types.md @@ -252,6 +252,7 @@ Configuration Attributes: entry_time | **Optional.** The unix timestamp when this comment was added. entry_type | **Optional.** The comment type (`User` = 1, `Downtime` = 2, `Flapping` = 3, `Acknowledgement` = 4). expire_time | **Optional.** The comment's expire time as unix timestamp. + persistent | **Optional.** Only evaluated for `entry_type` Acknowledgement. `true` does not remove the comment when the acknowledgement is removed. ## CompatLogger diff --git a/lib/db_ido/dbevents.cpp b/lib/db_ido/dbevents.cpp index 0a6a0d578..d5e39d52d 100644 --- a/lib/db_ido/dbevents.cpp +++ b/lib/db_ido/dbevents.cpp @@ -354,7 +354,7 @@ void DbEvents::AddCommentInternal(std::vector& queries, const Comment:: fields1->Set("comment_time", DbValue::FromTimestamp(entry_time)); /* same as entry_time */ fields1->Set("author_name", comment->GetAuthor()); fields1->Set("comment_data", comment->GetText()); - fields1->Set("is_persistent", 1); + fields1->Set("is_persistent", comment->GetPersistent() ? 1 : 0); fields1->Set("comment_source", 1); /* external */ fields1->Set("expires", (comment->GetExpireTime() > 0) ? 1 : 0); fields1->Set("expiration_time", DbValue::FromTimestamp(comment->GetExpireTime())); diff --git a/lib/icinga/apiactions.cpp b/lib/icinga/apiactions.cpp index d4211357b..dacd8fc71 100644 --- a/lib/icinga/apiactions.cpp +++ b/lib/icinga/apiactions.cpp @@ -194,12 +194,15 @@ Dictionary::Ptr ApiActions::AcknowledgeProblem(const ConfigObject::Ptr& object, AcknowledgementType sticky = AcknowledgementNormal; bool notify = false; + bool persistent = false; double timestamp = 0.0; if (params->Contains("sticky") && HttpUtility::GetLastParameter(params, "sticky")) sticky = AcknowledgementSticky; if (params->Contains("notify")) notify = HttpUtility::GetLastParameter(params, "notify"); + if (params->Contains("persistent")) + persistent = HttpUtility::GetLastParameter(params, "persistent"); if (params->Contains("expiry")) timestamp = HttpUtility::GetLastParameter(params, "expiry"); else @@ -218,7 +221,7 @@ Dictionary::Ptr ApiActions::AcknowledgeProblem(const ConfigObject::Ptr& object, } Comment::AddComment(checkable, CommentAcknowledgement, HttpUtility::GetLastParameter(params, "author"), - HttpUtility::GetLastParameter(params, "comment"), timestamp); + HttpUtility::GetLastParameter(params, "comment"), persistent, timestamp); checkable->AcknowledgeProblem(HttpUtility::GetLastParameter(params, "author"), HttpUtility::GetLastParameter(params, "comment"), sticky, notify, timestamp); @@ -254,7 +257,7 @@ Dictionary::Ptr ApiActions::AddComment(const ConfigObject::Ptr& object, String commentName = Comment::AddComment(checkable, CommentUser, HttpUtility::GetLastParameter(params, "author"), - HttpUtility::GetLastParameter(params, "comment"), 0); + HttpUtility::GetLastParameter(params, "comment"), false, 0); Comment::Ptr comment = Comment::GetByName(commentName); diff --git a/lib/icinga/apievents.cpp b/lib/icinga/apievents.cpp index cd69bfdd7..0fb38e190 100644 --- a/lib/icinga/apievents.cpp +++ b/lib/icinga/apievents.cpp @@ -178,7 +178,7 @@ void ApiEvents::FlappingChangedHandler(const Checkable::Ptr& checkable, const Me void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, - bool notify, double expiry, const MessageOrigin::Ptr& origin) + bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("AcknowledgementSet"); @@ -206,6 +206,7 @@ void ApiEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, result->Set("comment", comment); result->Set("acknowledgement_type", type); result->Set("notify", notify); + result->Set("persistent", persistent); result->Set("expiry", expiry); for (const EventQueue::Ptr& queue : queues) { diff --git a/lib/icinga/apievents.hpp b/lib/icinga/apievents.hpp index 2ca07b4ee..a71615b41 100644 --- a/lib/icinga/apievents.hpp +++ b/lib/icinga/apievents.hpp @@ -46,7 +46,7 @@ public: static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, - bool notify, double expiry, const MessageOrigin::Ptr& origin); + bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin); static void AcknowledgementClearedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin); static void CommentAddedHandler(const Comment::Ptr& comment); diff --git a/lib/icinga/checkable-comment.cpp b/lib/icinga/checkable-comment.cpp index e91321b29..97a0fcff4 100644 --- a/lib/icinga/checkable-comment.cpp +++ b/lib/icinga/checkable-comment.cpp @@ -38,6 +38,10 @@ void Checkable::RemoveAllComments(void) void Checkable::RemoveCommentsByType(int type) { for (const Comment::Ptr& comment : GetComments()) { + /* Do not remove persistent comments from an acknowledgement */ + if (comment->GetEntryType() == CommentAcknowledgement && comment->GetPersistent()) + continue; + if (comment->GetEntryType() == type) Comment::RemoveComment(comment->GetName()); } diff --git a/lib/icinga/checkable.cpp b/lib/icinga/checkable.cpp index d34204a5a..100b8ed2b 100644 --- a/lib/icinga/checkable.cpp +++ b/lib/icinga/checkable.cpp @@ -30,7 +30,7 @@ using namespace icinga; REGISTER_TYPE_WITH_PROTOTYPE(Checkable, Checkable::GetPrototype()); INITIALIZE_ONCE(&Checkable::StaticInitialize); -boost::signals2::signal Checkable::OnAcknowledgementSet; +boost::signals2::signal Checkable::OnAcknowledgementSet; boost::signals2::signal Checkable::OnAcknowledgementCleared; void Checkable::StaticInitialize(void) @@ -122,7 +122,7 @@ bool Checkable::IsAcknowledged(void) const return const_cast(this)->GetAcknowledgement() != AcknowledgementNone; } -void Checkable::AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, bool notify, double expiry, const MessageOrigin::Ptr& origin) +void Checkable::AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin) { SetAcknowledgementRaw(type); SetAcknowledgementExpiry(expiry); @@ -130,7 +130,7 @@ void Checkable::AcknowledgeProblem(const String& author, const String& comment, if (notify && !IsPaused()) OnNotificationsRequested(this, NotificationAcknowledgement, GetLastCheckResult(), author, comment, MessageOrigin::Ptr()); - OnAcknowledgementSet(this, author, comment, type, notify, expiry, origin); + OnAcknowledgementSet(this, author, comment, type, notify, persistent, expiry, origin); } void Checkable::ClearAcknowledgement(const MessageOrigin::Ptr& origin) diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index c24a421af..03fd5ae14 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -98,7 +98,7 @@ public: AcknowledgementType GetAcknowledgement(void); - void AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, bool notify = true, double expiry = 0, const MessageOrigin::Ptr& origin = MessageOrigin::Ptr()); + void AcknowledgeProblem(const String& author, const String& comment, AcknowledgementType type, bool notify = true, bool persistent = false, double expiry = 0, const MessageOrigin::Ptr& origin = MessageOrigin::Ptr()); void ClearAcknowledgement(const MessageOrigin::Ptr& origin = MessageOrigin::Ptr()); virtual int GetSeverity(void) const override; @@ -139,7 +139,7 @@ public: const NotificationType&, const CheckResult::Ptr&, const String&, const String&, const MessageOrigin::Ptr&)> OnNotificationSentToAllUsers; static boost::signals2::signal OnAcknowledgementSet; + bool, bool, double, const MessageOrigin::Ptr&)> OnAcknowledgementSet; 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 aa5ce9b43..11151467a 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -445,7 +445,7 @@ Value ClusterEvents::ForceNextNotificationChangedAPIHandler(const MessageOrigin: void ClusterEvents::AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, - bool notify, double expiry, const MessageOrigin::Ptr& origin) + bool notify, bool persistent, double expiry, const MessageOrigin::Ptr& origin) { ApiListener::Ptr listener = ApiListener::GetInstance(); @@ -510,7 +510,7 @@ Value ClusterEvents::AcknowledgementSetAPIHandler(const MessageOrigin::Ptr& orig checkable->AcknowledgeProblem(params->Get("author"), params->Get("comment"), static_cast(static_cast(params->Get("acktype"))), - params->Get("notify"), params->Get("expiry"), origin); + params->Get("notify"), params->Get("persistent"), params->Get("expiry"), origin); return Empty; } diff --git a/lib/icinga/clusterevents.hpp b/lib/icinga/clusterevents.hpp index 6078af5d6..702f145ef 100644 --- a/lib/icinga/clusterevents.hpp +++ b/lib/icinga/clusterevents.hpp @@ -53,7 +53,7 @@ public: static Value ForceNextNotificationChangedAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); static void AcknowledgementSetHandler(const Checkable::Ptr& checkable, const String& author, const String& comment, AcknowledgementType type, - bool notify, double expiry, const MessageOrigin::Ptr& origin); + 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); diff --git a/lib/icinga/comment.cpp b/lib/icinga/comment.cpp index 138a9863b..fc267039c 100644 --- a/lib/icinga/comment.cpp +++ b/lib/icinga/comment.cpp @@ -150,7 +150,7 @@ int Comment::GetNextCommentID(void) } String Comment::AddComment(const Checkable::Ptr& checkable, CommentType entryType, const String& author, - const String& text, double expireTime, const String& id, const MessageOrigin::Ptr& origin) + const String& text, bool persistent, double expireTime, const String& id, const MessageOrigin::Ptr& origin) { String fullName; @@ -163,6 +163,7 @@ String Comment::AddComment(const Checkable::Ptr& checkable, CommentType entryTyp attrs->Set("author", author); attrs->Set("text", text); + attrs->Set("persistent", persistent); attrs->Set("expire_time", expireTime); attrs->Set("entry_type", entryType); attrs->Set("entry_time", Utility::GetTime()); @@ -247,8 +248,13 @@ void Comment::CommentsExpireTimerHandler(void) } for (const Comment::Ptr& comment : comments) { - /* Only remove comment which are activated after daemon start. */ - if (comment->IsActive() && comment->IsExpired()) + /* Only remove comments which are activated after daemon start. */ + if (comment->IsActive() && comment->IsExpired()) { + /* Do not remove persistent comments from an acknowledgement */ + if (comment->GetEntryType() == CommentAcknowledgement && comment->GetPersistent()) + continue; + RemoveComment(comment->GetName()); + } } } diff --git a/lib/icinga/comment.hpp b/lib/icinga/comment.hpp index 078f64e30..67669ba08 100644 --- a/lib/icinga/comment.hpp +++ b/lib/icinga/comment.hpp @@ -49,7 +49,7 @@ public: static int GetNextCommentID(void); static String AddComment(const intrusive_ptr& checkable, CommentType entryType, - const String& author, const String& text, double expireTime, + const String& author, const String& text, bool persistent, double expireTime, const String& id = String(), const MessageOrigin::Ptr& origin = MessageOrigin::Ptr()); static void RemoveComment(const String& id, const MessageOrigin::Ptr& origin = MessageOrigin::Ptr()); diff --git a/lib/icinga/comment.ti b/lib/icinga/comment.ti index 0187b49fe..aaf3e3986 100644 --- a/lib/icinga/comment.ti +++ b/lib/icinga/comment.ti @@ -87,6 +87,7 @@ class Comment : ConfigObject < CommentNameComposer }; [config, required] String author; [config, required] String text; + [config] bool persistent; [config] Timestamp expire_time; [state] int legacy_id; }; diff --git a/lib/icinga/externalcommandprocessor.cpp b/lib/icinga/externalcommandprocessor.cpp index 75e21bf91..a7d97ad01 100644 --- a/lib/icinga/externalcommandprocessor.cpp +++ b/lib/icinga/externalcommandprocessor.cpp @@ -621,6 +621,7 @@ void ExternalCommandProcessor::AcknowledgeSvcProblem(double, const std::vector 0 ? true : false); + bool persistent = (Convert::ToLong(arguments[4]) > 0 ? true : false); Service::Ptr service = Service::GetByNamePair(arguments[0], arguments[1]); @@ -633,14 +634,15 @@ void ExternalCommandProcessor::AcknowledgeSvcProblem(double, const std::vectorGetName() << "'" << (notify ? "" : ". Disabled notification"); - Comment::AddComment(service, CommentAcknowledgement, arguments[5], arguments[6], 0); - service->AcknowledgeProblem(arguments[5], arguments[6], sticky ? AcknowledgementSticky : AcknowledgementNormal, notify); + Comment::AddComment(service, CommentAcknowledgement, arguments[5], arguments[6], persistent, 0); + service->AcknowledgeProblem(arguments[5], arguments[6], sticky ? AcknowledgementSticky : AcknowledgementNormal, persistent, notify); } void ExternalCommandProcessor::AcknowledgeSvcProblemExpire(double, const std::vector& arguments) { bool sticky = (Convert::ToLong(arguments[2]) == 2 ? true : false); bool notify = (Convert::ToLong(arguments[3]) > 0 ? true : false); + bool persistent = (Convert::ToLong(arguments[4]) > 0 ? true : false); double timestamp = Convert::ToDouble(arguments[5]); Service::Ptr service = Service::GetByNamePair(arguments[0], arguments[1]); @@ -654,8 +656,8 @@ void ExternalCommandProcessor::AcknowledgeSvcProblemExpire(double, const std::ve Log(LogNotice, "ExternalCommandProcessor") << "Setting timed acknowledgement for service '" << service->GetName() << "'" << (notify ? "" : ". Disabled notification"); - Comment::AddComment(service, CommentAcknowledgement, arguments[6], arguments[7], timestamp); - service->AcknowledgeProblem(arguments[6], arguments[7], sticky ? AcknowledgementSticky : AcknowledgementNormal, notify, timestamp); + Comment::AddComment(service, CommentAcknowledgement, arguments[6], arguments[7], persistent, timestamp); + service->AcknowledgeProblem(arguments[6], arguments[7], sticky ? AcknowledgementSticky : AcknowledgementNormal, notify, persistent, timestamp); } void ExternalCommandProcessor::RemoveSvcAcknowledgement(double, const std::vector& arguments) @@ -680,6 +682,7 @@ void ExternalCommandProcessor::AcknowledgeHostProblem(double, const std::vector< { bool sticky = (Convert::ToLong(arguments[1]) == 2 ? true : false); bool notify = (Convert::ToLong(arguments[2]) > 0 ? true : false); + bool persistent = (Convert::ToLong(arguments[3]) > 0 ? true : false); Host::Ptr host = Host::GetByName(arguments[0]); @@ -692,14 +695,15 @@ void ExternalCommandProcessor::AcknowledgeHostProblem(double, const std::vector< if (host->GetState() == HostUp) BOOST_THROW_EXCEPTION(std::invalid_argument("The host '" + arguments[0] + "' is OK.")); - Comment::AddComment(host, CommentAcknowledgement, arguments[4], arguments[5], 0); - host->AcknowledgeProblem(arguments[4], arguments[5], sticky ? AcknowledgementSticky : AcknowledgementNormal, notify); + Comment::AddComment(host, CommentAcknowledgement, arguments[4], arguments[5], persistent, 0); + host->AcknowledgeProblem(arguments[4], arguments[5], sticky ? AcknowledgementSticky : AcknowledgementNormal, persistent, notify); } void ExternalCommandProcessor::AcknowledgeHostProblemExpire(double, const std::vector& arguments) { bool sticky = (Convert::ToLong(arguments[1]) == 2 ? true : false); bool notify = (Convert::ToLong(arguments[2]) > 0 ? true : false); + bool persistent = (Convert::ToLong(arguments[3]) > 0 ? true : false); double timestamp = Convert::ToDouble(arguments[4]); Host::Ptr host = Host::GetByName(arguments[0]); @@ -713,8 +717,8 @@ void ExternalCommandProcessor::AcknowledgeHostProblemExpire(double, const std::v if (host->GetState() == HostUp) BOOST_THROW_EXCEPTION(std::invalid_argument("The host '" + arguments[0] + "' is OK.")); - Comment::AddComment(host, CommentAcknowledgement, arguments[5], arguments[6], timestamp); - host->AcknowledgeProblem(arguments[5], arguments[6], sticky ? AcknowledgementSticky : AcknowledgementNormal, notify, timestamp); + Comment::AddComment(host, CommentAcknowledgement, arguments[5], arguments[6], persistent, timestamp); + host->AcknowledgeProblem(arguments[5], arguments[6], sticky ? AcknowledgementSticky : AcknowledgementNormal, notify, persistent, timestamp); } void ExternalCommandProcessor::RemoveHostAcknowledgement(double, const std::vector& arguments) @@ -1286,7 +1290,7 @@ void ExternalCommandProcessor::AddHostComment(double, const std::vector& Log(LogNotice, "ExternalCommandProcessor") << "Creating comment for host " << host->GetName(); - (void) Comment::AddComment(host, CommentUser, arguments[2], arguments[3], 0); + (void) Comment::AddComment(host, CommentUser, arguments[2], arguments[3], false, 0); } void ExternalCommandProcessor::DelHostComment(double, const std::vector& arguments) @@ -1310,7 +1314,7 @@ void ExternalCommandProcessor::AddSvcComment(double, const std::vector& Log(LogNotice, "ExternalCommandProcessor") << "Creating comment for service " << service->GetName(); - (void) Comment::AddComment(service, CommentUser, arguments[3], arguments[4], 0); + (void) Comment::AddComment(service, CommentUser, arguments[3], arguments[4], false, 0); } void ExternalCommandProcessor::DelSvcComment(double, const std::vector& arguments)