From 8a714d66f11e5f5ecaecc14e42ed698a2ee66aec Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 15 Jun 2016 11:27:01 +0200 Subject: [PATCH] Ensure that sent notifications are synced between Icinga instances fixes #11562 --- lib/icinga/apievents.cpp | 2 +- lib/icinga/apievents.hpp | 2 +- lib/icinga/checkable-notification.cpp | 8 +- lib/icinga/checkable.hpp | 9 +- lib/icinga/clusterevents.cpp | 222 ++++++++++++++++++++++++++ lib/icinga/clusterevents.hpp | 8 + lib/icinga/notification.cpp | 16 +- lib/icinga/notification.ti | 2 +- 8 files changed, 247 insertions(+), 22 deletions(-) diff --git a/lib/icinga/apievents.cpp b/lib/icinga/apievents.cpp index e2844a78f..d04032c7c 100644 --- a/lib/icinga/apievents.cpp +++ b/lib/icinga/apievents.cpp @@ -107,7 +107,7 @@ void ApiEvents::StateChangeHandler(const Checkable::Ptr& checkable, const CheckR void ApiEvents::NotificationSentToAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, NotificationType type, - const CheckResult::Ptr& cr, const String& author, const String& text) + const CheckResult::Ptr& cr, const String& author, const String& text, const MessageOrigin::Ptr& origin) { std::vector queues = EventQueue::GetQueuesForType("Notification"); diff --git a/lib/icinga/apievents.hpp b/lib/icinga/apievents.hpp index a60e777ac..dfbd64c3d 100644 --- a/lib/icinga/apievents.hpp +++ b/lib/icinga/apievents.hpp @@ -40,7 +40,7 @@ public: static void NotificationSentToAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, NotificationType type, const CheckResult::Ptr& cr, const String& author, - const String& text); + const String& text, const MessageOrigin::Ptr& origin); static void FlappingChangedHandler(const Checkable::Ptr& checkable, const MessageOrigin::Ptr& origin); diff --git a/lib/icinga/checkable-notification.cpp b/lib/icinga/checkable-notification.cpp index b0d3f8e81..5dc22f1d7 100644 --- a/lib/icinga/checkable-notification.cpp +++ b/lib/icinga/checkable-notification.cpp @@ -29,11 +29,11 @@ using namespace icinga; boost::signals2::signal&, - const NotificationType&, const CheckResult::Ptr&, const String&, const String&)> Checkable::OnNotificationSentToAllUsers; -boost::signals2::signal&, - const NotificationType&, const CheckResult::Ptr&, const String&, const String&)> Checkable::OnNotificationSendStart; + const NotificationType&, const CheckResult::Ptr&, const String&, const String&, + const MessageOrigin::Ptr&)> Checkable::OnNotificationSentToAllUsers; boost::signals2::signal Checkable::OnNotificationSentToUser; + const NotificationType&, const CheckResult::Ptr&, const String&, const String&, const String&, + const MessageOrigin::Ptr&)> Checkable::OnNotificationSentToUser; void Checkable::ResetNotificationNumbers(void) { diff --git a/lib/icinga/checkable.hpp b/lib/icinga/checkable.hpp index 21e55bc25..89dcefe76 100644 --- a/lib/icinga/checkable.hpp +++ b/lib/icinga/checkable.hpp @@ -111,15 +111,12 @@ public: static boost::signals2::signal, const MessageOrigin::Ptr&)> OnReachabilityChanged; static boost::signals2::signal OnNotificationsRequested; - static boost::signals2::signal&, - const NotificationType&, const CheckResult::Ptr&, const String&, - const String&)> OnNotificationSendStart; static boost::signals2::signal OnNotificationSentToUser; + const NotificationType&, const CheckResult::Ptr&, const String&, const String&, const String&, + const MessageOrigin::Ptr&)> OnNotificationSentToUser; static boost::signals2::signal&, const NotificationType&, const CheckResult::Ptr&, const String&, - const String&)> OnNotificationSentToAllUsers; + const String&, const MessageOrigin::Ptr&)> OnNotificationSentToAllUsers; static boost::signals2::signal OnAcknowledgementSet; static boost::signals2::signal OnAcknowledgementCleared; diff --git a/lib/icinga/clusterevents.cpp b/lib/icinga/clusterevents.cpp index 977210940..6dbfe1048 100644 --- a/lib/icinga/clusterevents.cpp +++ b/lib/icinga/clusterevents.cpp @@ -49,6 +49,8 @@ REGISTER_APIFUNCTION(ClearAcknowledgement, event, &ClusterEvents::Acknowledgemen REGISTER_APIFUNCTION(UpdateRepository, event, &ClusterEvents::UpdateRepositoryAPIHandler); REGISTER_APIFUNCTION(ExecuteCommand, event, &ClusterEvents::ExecuteCommandAPIHandler); REGISTER_APIFUNCTION(SendNotifications, event, &ClusterEvents::SendNotificationsAPIHandler); +REGISTER_APIFUNCTION(NotificationSentUser, event, &ClusterEvents::NotificationSentUserAPIHandler); +REGISTER_APIFUNCTION(NotificationSentAllUsers, event, &ClusterEvents::NotificationSentAllUsersAPIHandler); static Timer::Ptr l_RepositoryTimer; @@ -60,6 +62,8 @@ void ClusterEvents::StaticInitialize(void) Checkable::OnForceNextCheckChanged.connect(&ClusterEvents::ForceNextCheckChangedHandler); Checkable::OnForceNextNotificationChanged.connect(&ClusterEvents::ForceNextNotificationChangedHandler); Checkable::OnNotificationsRequested.connect(&ClusterEvents::SendNotificationsHandler); + Checkable::OnNotificationSentToUser.connect(&ClusterEvents::NotificationSentUserHandler); + Checkable::OnNotificationSentToAllUsers.connect(&ClusterEvents::NotificationSentAllUsersHandler); Checkable::OnAcknowledgementSet.connect(&ClusterEvents::AcknowledgementSetHandler); Checkable::OnAcknowledgementCleared.connect(&ClusterEvents::AcknowledgementClearedHandler); @@ -828,3 +832,221 @@ Value ClusterEvents::SendNotificationsAPIHandler(const MessageOrigin::Ptr& origi return Empty; } + +void ClusterEvents::NotificationSentUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user, + NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const String& command, + const MessageOrigin::Ptr& origin) +{ + ApiListener::Ptr listener = ApiListener::GetInstance(); + + if (!listener) + return; + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + Dictionary::Ptr params = new Dictionary(); + params->Set("host", host->GetName()); + if (service) + params->Set("service", service->GetShortName()); + params->Set("notification", notification->GetName()); + params->Set("user", user->GetName()); + params->Set("type", notificationType); + params->Set("cr", Serialize(cr)); + params->Set("author", author); + params->Set("text", commentText); + params->Set("command", command); + + Dictionary::Ptr message = new Dictionary(); + message->Set("jsonrpc", "2.0"); + message->Set("method", "event::NotificationSentUser"); + message->Set("params", params); + + listener->RelayMessage(origin, ConfigObject::Ptr(), message, true); +} + +Value ClusterEvents::NotificationSentUserAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) +{ + Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + + if (!endpoint) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'sent notification to user' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed)."; + return Empty; + } + + if (!params) + return Empty; + + Host::Ptr host = Host::GetByName(params->Get("host")); + + if (!host) + return Empty; + + Checkable::Ptr checkable; + + if (params->Contains("service")) + checkable = host->GetServiceByShortName(params->Get("service")); + else + checkable = host; + + if (!checkable) + return Empty; + + if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'sent notification to user' message from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + return Empty; + } + + CheckResult::Ptr cr = new CheckResult(); + + Dictionary::Ptr vcr = params->Get("cr"); + Array::Ptr vperf = vcr->Get("performance_data"); + vcr->Remove("performance_data"); + + Deserialize(cr, params->Get("cr"), true); + + NotificationType type = static_cast(static_cast(params->Get("type"))); + String author = params->Get("author"); + String text = params->Get("text"); + + Notification::Ptr notification = Notification::GetByName(params->Get("notification")); + + if (!notification) + return Empty; + + User::Ptr user = User::GetByName(params->Get("user")); + + if (!user) + return Empty; + + String command = params->Get("command"); + + Checkable::OnNotificationSentToUser(notification, checkable, user, type, cr, author, text, command, origin); + + return Empty; +} + +void ClusterEvents::NotificationSentAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, + NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const MessageOrigin::Ptr& origin) +{ + ApiListener::Ptr listener = ApiListener::GetInstance(); + + if (!listener) + return; + + Host::Ptr host; + Service::Ptr service; + tie(host, service) = GetHostService(checkable); + + Dictionary::Ptr params = new Dictionary(); + params->Set("host", host->GetName()); + if (service) + params->Set("service", service->GetShortName()); + params->Set("notification", notification->GetName()); + + Array::Ptr ausers = new Array(); + BOOST_FOREACH(const User::Ptr& user, users) { + ausers->Add(user->GetName()); + } + params->Set("users", ausers); + + params->Set("type", notificationType); + params->Set("cr", Serialize(cr)); + params->Set("author", author); + params->Set("text", commentText); + + params->Set("last_notification", notification->GetLastNotification()); + params->Set("next_notifications", notification->GetNextNotification()); + params->Set("notification_number", notification->GetNotificationNumber()); + params->Set("last_problem_notification", notification->GetLastProblemNotification()); + + Dictionary::Ptr message = new Dictionary(); + message->Set("jsonrpc", "2.0"); + message->Set("method", "event::NotificationSentAllUsers"); + message->Set("params", params); + + listener->RelayMessage(origin, ConfigObject::Ptr(), message, true); +} + +Value ClusterEvents::NotificationSentAllUsersAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params) +{ + Endpoint::Ptr endpoint = origin->FromClient->GetEndpoint(); + + if (!endpoint) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'sent notification to all users' message from '" << origin->FromClient->GetIdentity() << "': Invalid endpoint origin (client not allowed)."; + return Empty; + } + + if (!params) + return Empty; + + Host::Ptr host = Host::GetByName(params->Get("host")); + + if (!host) + return Empty; + + Checkable::Ptr checkable; + + if (params->Contains("service")) + checkable = host->GetServiceByShortName(params->Get("service")); + else + checkable = host; + + if (!checkable) + return Empty; + + if (origin->FromZone && origin->FromZone != Zone::GetLocalZone()) { + Log(LogNotice, "ClusterEvents") + << "Discarding 'sent notification to all users' message from '" << origin->FromClient->GetIdentity() << "': Unauthorized access."; + return Empty; + } + + CheckResult::Ptr cr = new CheckResult(); + + Dictionary::Ptr vcr = params->Get("cr"); + Array::Ptr vperf = vcr->Get("performance_data"); + vcr->Remove("performance_data"); + + Deserialize(cr, params->Get("cr"), true); + + NotificationType type = static_cast(static_cast(params->Get("type"))); + String author = params->Get("author"); + String text = params->Get("text"); + + Notification::Ptr notification = Notification::GetByName(params->Get("notification")); + + if (!notification) + return Empty; + + Array::Ptr ausers = params->Get("users"); + + if (!ausers) + return Empty; + + std::set users; + + { + ObjectLock olock(ausers); + BOOST_FOREACH(const String& auser, ausers) { + User::Ptr user = User::GetByName(auser); + + if (!user) + continue; + + users.insert(user); + } + } + + notification->SetLastNotification(params->Get("last_notification")); + notification->SetNextNotification(params->Get("next_notification")); + notification->SetNotificationNumber(params->Get("notification_number")); + notification->SetLastProblemNotification(params->Get("last_problem_notification")); + + Checkable::OnNotificationSentToAllUsers(notification, checkable, users, type, cr, author, text, origin); + + return Empty; +} diff --git a/lib/icinga/clusterevents.hpp b/lib/icinga/clusterevents.hpp index 56a8bd49f..3b9b7c4be 100644 --- a/lib/icinga/clusterevents.hpp +++ b/lib/icinga/clusterevents.hpp @@ -70,6 +70,14 @@ public: static void SendNotificationsHandler(const Checkable::Ptr& checkable, NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text, const MessageOrigin::Ptr& origin); static Value SendNotificationsAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + + static void NotificationSentUserHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user, + NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const String& command, const MessageOrigin::Ptr& origin); + static Value NotificationSentUserAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); + + static void NotificationSentAllUsersHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set& users, + NotificationType notificationType, const CheckResult::Ptr& cr, const String& author, const String& commentText, const MessageOrigin::Ptr& origin); + static Value NotificationSentAllUsersAPIHandler(const MessageOrigin::Ptr& origin, const Dictionary::Ptr& params); }; } diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index 7d1f7247d..98b67676e 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -345,7 +345,11 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin())); } - Service::OnNotificationSendStart(this, checkable, allUsers, type, cr, author, text); + { + ObjectLock olock(this); + UpdateNotificationNumber(); + SetLastNotification(Utility::GetTime()); + } std::set allNotifiedUsers; Array::Ptr notifiedUsers = GetNotifiedUsers(); @@ -392,7 +396,7 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe notifiedUsers->Clear(); /* used in db_ido for notification history */ - Service::OnNotificationSentToAllUsers(this, checkable, allNotifiedUsers, type, cr, author, text); + Service::OnNotificationSentToAllUsers(this, checkable, allNotifiedUsers, type, cr, author, text, MessageOrigin::Ptr()); } bool Notification::CheckNotificationUserFilters(NotificationType type, const User::Ptr& user, bool force) @@ -474,14 +478,8 @@ void Notification::ExecuteNotificationHelper(NotificationType type, const User:: command->Execute(this, user, cr, type, author, text); - { - ObjectLock olock(this); - UpdateNotificationNumber(); - SetLastNotification(Utility::GetTime()); - } - /* required by compatlogger */ - Service::OnNotificationSentToUser(this, GetCheckable(), user, type, cr, author, text, command->GetName()); + Service::OnNotificationSentToUser(this, GetCheckable(), user, type, cr, author, text, command->GetName(), MessageOrigin::Ptr()); Log(LogInformation, "Notification") << "Completed sending notification '" << GetName() diff --git a/lib/icinga/notification.ti b/lib/icinga/notification.ti index 18604e2e9..0d234c99a 100644 --- a/lib/icinga/notification.ti +++ b/lib/icinga/notification.ti @@ -92,7 +92,7 @@ class Notification : CustomVarObject < NotificationNameComposer [state] double last_notification; [state] double next_notification; - [state, set_protected] Value notification_number; + [state] int notification_number; [state] double last_problem_notification; [config, navigation] name(Endpoint) command_endpoint (CommandEndpointRaw) {