diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index 087c2a19d..d5597065f 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -223,6 +223,30 @@ void Notification::ResetNotificationNumber() SetNotificationNumber(0); } +/** + * Check whether the given notification type is a Recovery or FlappingEnd notification and the Checkable object + * has already recovered. + * + * For the latter case, if the Checkable has recovered while it was in a Flapping state, the recovery notification + * will be silently discarded and leave some internal states of the Notification object that depend on it in an + * inconsistent state. So, use this helper function whether to reset one of these states. + * + * @param checkable The checkable object the notification is for + * @param cr The current check result passed to the notification + * @param type The requested notification type + * + * @return bool + */ +static bool IsRecoveryOrFlappingEndAndCheckableIsOK(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, NotificationType type) +{ + if (type == NotificationRecovery) { + return true; + } + + // Check whether missed the Checkable recovery because of its Flapping state. + return type == NotificationFlappingEnd && cr && checkable->IsStateOK(cr->GetState()); +} + void Notification::BeginExecuteNotification(NotificationType type, const CheckResult::Ptr& cr, bool force, bool reminder, const String& author, const String& text) { String notificationName = GetName(); @@ -233,15 +257,18 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe << "notifications of type '" << notificationTypeName << "' for notification object '" << notificationName << "'."; - if (type == NotificationRecovery) { + Checkable::Ptr checkable = GetCheckable(); + + // Clear the last notified problem state per user if we're sending a recovery notification or if we're sending a + // flapping end notification and the checkable is already in an OK state. This is necessary since we might have + // missed the recovery notification due to the flapping state. + if (IsRecoveryOrFlappingEndAndCheckableIsOK(checkable, cr, type)) { auto states (GetLastNotifiedStatePerUser()); states->Clear(); OnLastNotifiedStatePerUserCleared(this, nullptr); } - Checkable::Ptr checkable = GetCheckable(); - if (!force) { TimePeriod::Ptr tp = GetPeriod(); @@ -338,7 +365,7 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe */ { ObjectLock olock(this); - if (type == NotificationRecovery && GetInterval() <= 0) + if (GetInterval() <= 0 && IsRecoveryOrFlappingEndAndCheckableIsOK(checkable, cr, type)) SetNoMoreNotifications(false); } @@ -389,7 +416,7 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe if (type == NotificationProblem && GetInterval() <= 0) SetNoMoreNotifications(true); - else if (type != NotificationCustom) + else if (IsRecoveryOrFlappingEndAndCheckableIsOK(checkable, cr, type)) SetNoMoreNotifications(false); if (type == NotificationProblem && GetInterval() > 0) @@ -488,7 +515,7 @@ void Notification::BeginExecuteNotification(NotificationType type, const CheckRe } /* if this was a recovery notification, reset all notified users */ - if (type == NotificationRecovery) + if (IsRecoveryOrFlappingEndAndCheckableIsOK(checkable, cr, type)) notifiedProblemUsers->Clear(); /* used in db_ido for notification history */