From 644982f50aa440413b5939404244f468f75518a6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Wed, 14 Jun 2023 17:00:56 +0200 Subject: [PATCH] Notification#BeginExecuteNotification(): discard likely duplicate problem notifications --- lib/icinga/notification.cpp | 16 ++++++++ test/CMakeLists.txt | 1 + test/icinga-notification.cpp | 71 ++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index e545b304dfa..2338f280bfb 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -440,6 +440,22 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe } } + if (type == NotificationProblem && !reminder && !checkable->GetVolatile()) { + auto [host, service] = GetHostService(checkable); + uint_fast8_t state = service ? service->GetState() : host->GetState(); + + if (state == (uint_fast8_t)GetLastNotifiedStatePerUser()->Get(userName)) { + auto stateStr (service ? NotificationServiceStateToString(service->GetState()) : NotificationHostStateToString(host->GetState())); + + Log(LogNotice, "Notification") + << "Notification object '" << notificationName << "': We already notified user '" << userName << "' for a " << stateStr + << " problem. Likely after that another state change notification was filtered out by config. Not sending duplicate '" + << stateStr << "' notification."; + + continue; + } + } + Log(LogInformation, "Notification") << "Sending " << (reminder ? "reminder " : "") << "'" << NotificationTypeToString(type) << "' notification '" << notificationName << "' for user '" << userName << "'"; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 8919de304dc..d947d3ba05c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -138,6 +138,7 @@ add_boost_test(base icinga_notification/strings icinga_notification/state_filter icinga_notification/type_filter + icinga_notification/duplicate_dueto_filter icinga_macros/simple icinga_legacytimeperiod/simple icinga_legacytimeperiod/advanced diff --git a/test/icinga-notification.cpp b/test/icinga-notification.cpp index 5cf3f49e821..72e16062520 100644 --- a/test/icinga-notification.cpp +++ b/test/icinga-notification.cpp @@ -1,11 +1,58 @@ /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ +#include "icinga/host.hpp" #include "icinga/notification.hpp" +#include "icinga/notificationcommand.hpp" +#include "icinga/service.hpp" +#include "icinga/user.hpp" #include #include using namespace icinga; +struct DuplicateDueToFilterHelper +{ + Host::Ptr h = new Host(); + Service::Ptr s = new Service(); + User::Ptr u = new User(); + NotificationCommand::Ptr nc = new NotificationCommand(); + Notification::Ptr n = new Notification(); + unsigned int called = 0; + + DuplicateDueToFilterHelper() + { + h->SetName("example.com", true); + h->Register(); + + s->SetShortName("disk", true); + h->AddService(s); + + u->SetName("jdoe", true); + u->SetTypeFilter(~0); + u->SetStateFilter(~0); + u->Register(); + + nc->SetName("mail", true); + nc->SetExecute(new Function("", [this]() { ++called; }), true); + nc->Register(); + + n->SetFieldByName("host_name", "example.com", false, DebugInfo()); + n->SetFieldByName("service_name", "disk", false, DebugInfo()); + n->SetFieldByName("command", "mail", false, DebugInfo()); + n->SetUsersRaw(new Array({"jdoe"}), true); + n->SetTypeFilter(~NotificationRecovery); + n->SetStateFilter(~StateFilterWarning); + ((ConfigObject*)n.get())->OnAllConfigLoaded(); // link Service + } + + ~DuplicateDueToFilterHelper() + { + h->Unregister(); + u->Unregister(); + nc->Unregister(); + } +}; + BOOST_AUTO_TEST_SUITE(icinga_notification) BOOST_AUTO_TEST_CASE(strings) @@ -102,4 +149,28 @@ BOOST_AUTO_TEST_CASE(type_filter) std::cout << "#4 Notification type: " << ftype << " against " << notification->GetTypeFilter() << " must fail." << std::endl; BOOST_CHECK(!(notification->GetTypeFilter() & ftype)); } + +BOOST_AUTO_TEST_CASE(duplicate_dueto_filter) +{ + DuplicateDueToFilterHelper helper; + + helper.s->SetState(ServiceCritical, true); + Application::GetTP().Start(); + helper.n->BeginExecuteNotification(NotificationProblem, nullptr, false, false, "", ""); + Application::GetTP().Stop(); + BOOST_CHECK(helper.called == 1u); + + helper.s->SetState(ServiceWarning, true); + Application::GetTP().Start(); + helper.n->BeginExecuteNotification(NotificationProblem, nullptr, false, false, "", ""); + Application::GetTP().Stop(); + BOOST_CHECK(helper.called == 1u); + + helper.s->SetState(ServiceCritical, true); + Application::GetTP().Start(); + helper.n->BeginExecuteNotification(NotificationProblem, nullptr, false, false, "", ""); + Application::GetTP().Stop(); + BOOST_CHECK(helper.called == 1u); +} + BOOST_AUTO_TEST_SUITE_END()