summaryrefslogtreecommitdiffstats
path: root/lib/notification/notificationcomponent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/notification/notificationcomponent.cpp')
-rw-r--r--lib/notification/notificationcomponent.cpp271
1 files changed, 271 insertions, 0 deletions
diff --git a/lib/notification/notificationcomponent.cpp b/lib/notification/notificationcomponent.cpp
new file mode 100644
index 0000000..982a838
--- /dev/null
+++ b/lib/notification/notificationcomponent.cpp
@@ -0,0 +1,271 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "notification/notificationcomponent.hpp"
+#include "notification/notificationcomponent-ti.cpp"
+#include "icinga/service.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "base/configtype.hpp"
+#include "base/objectlock.hpp"
+#include "base/logger.hpp"
+#include "base/utility.hpp"
+#include "base/exception.hpp"
+#include "base/statsfunction.hpp"
+#include "remote/apilistener.hpp"
+
+using namespace icinga;
+
+REGISTER_TYPE(NotificationComponent);
+
+REGISTER_STATSFUNCTION(NotificationComponent, &NotificationComponent::StatsFunc);
+
+void NotificationComponent::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr&)
+{
+ DictionaryData nodes;
+
+ for (const NotificationComponent::Ptr& notification_component : ConfigType::GetObjectsByType<NotificationComponent>()) {
+ nodes.emplace_back(notification_component->GetName(), 1); //add more stats
+ }
+
+ status->Set("notificationcomponent", new Dictionary(std::move(nodes)));
+}
+
+/**
+ * Starts the component.
+ */
+void NotificationComponent::Start(bool runtimeCreated)
+{
+ ObjectImpl<NotificationComponent>::Start(runtimeCreated);
+
+ Log(LogInformation, "NotificationComponent")
+ << "'" << GetName() << "' started.";
+
+ Checkable::OnNotificationsRequested.connect([this](const Checkable::Ptr& checkable, NotificationType type, const CheckResult::Ptr& cr,
+ const String& author, const String& text, const MessageOrigin::Ptr&) {
+ SendNotificationsHandler(checkable, type, cr, author, text);
+ });
+
+ m_NotificationTimer = Timer::Create();
+ m_NotificationTimer->SetInterval(5);
+ m_NotificationTimer->OnTimerExpired.connect([this](const Timer * const&) { NotificationTimerHandler(); });
+ m_NotificationTimer->Start();
+}
+
+void NotificationComponent::Stop(bool runtimeRemoved)
+{
+ m_NotificationTimer->Stop(true);
+
+ Log(LogInformation, "NotificationComponent")
+ << "'" << GetName() << "' stopped.";
+
+ ObjectImpl<NotificationComponent>::Stop(runtimeRemoved);
+}
+
+static inline
+void SubtractSuppressedNotificationTypes(const Notification::Ptr& notification, int types)
+{
+ ObjectLock olock (notification);
+
+ int suppressedTypesBefore (notification->GetSuppressedNotifications());
+ int suppressedTypesAfter (suppressedTypesBefore & ~types);
+
+ if (suppressedTypesAfter != suppressedTypesBefore) {
+ notification->SetSuppressedNotifications(suppressedTypesAfter);
+ }
+}
+
+static inline
+void FireSuppressedNotifications(const Notification::Ptr& notification)
+{
+ int suppressedTypes (notification->GetSuppressedNotifications());
+ if (!suppressedTypes)
+ return;
+
+ int subtract = 0;
+ auto checkable (notification->GetCheckable());
+
+ for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
+ if ((suppressedTypes & type) && !checkable->NotificationReasonApplies(type)) {
+ subtract |= type;
+ suppressedTypes &= ~type;
+ }
+ }
+
+ if (suppressedTypes) {
+ auto tp (notification->GetPeriod());
+
+ if ((!tp || tp->IsInside(Utility::GetTime())) && !checkable->IsLikelyToBeCheckedSoon()) {
+ for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
+ if (!(suppressedTypes & type) || checkable->NotificationReasonSuppressed(type))
+ continue;
+
+ auto notificationName (notification->GetName());
+
+ Log(LogNotice, "NotificationComponent")
+ << "Attempting to re-send previously suppressed notification '" << notificationName << "'.";
+
+ subtract |= type;
+ SubtractSuppressedNotificationTypes(notification, subtract);
+ subtract = 0;
+
+ try {
+ notification->BeginExecuteNotification(type, checkable->GetLastCheckResult(), false, false);
+ } catch (const std::exception& ex) {
+ Log(LogWarning, "NotificationComponent")
+ << "Exception occurred during notification for object '"
+ << notificationName << "': " << DiagnosticInformation(ex, false);
+ }
+ }
+ }
+ }
+
+ if (subtract) {
+ SubtractSuppressedNotificationTypes(notification, subtract);
+ }
+}
+
+/**
+ * Periodically sends notifications.
+ *
+ * @param - Event arguments for the timer.
+ */
+void NotificationComponent::NotificationTimerHandler()
+{
+ double now = Utility::GetTime();
+
+ /* Function already checks whether 'api' feature is enabled. */
+ Endpoint::Ptr myEndpoint = Endpoint::GetLocalEndpoint();
+
+ for (const Notification::Ptr& notification : ConfigType::GetObjectsByType<Notification>()) {
+ if (!notification->IsActive())
+ continue;
+
+ String notificationName = notification->GetName();
+ bool updatedObjectAuthority = ApiListener::UpdatedObjectAuthority();
+
+ /* Skip notification if paused, in a cluster setup & HA feature is enabled. */
+ if (notification->IsPaused()) {
+ if (updatedObjectAuthority) {
+ auto stashedNotifications (notification->GetStashedNotifications());
+ ObjectLock olock(stashedNotifications);
+
+ if (stashedNotifications->GetLength()) {
+ Log(LogNotice, "NotificationComponent")
+ << "Notification '" << notificationName << "': HA cluster active, this endpoint does not have the authority. Dropping all stashed notifications.";
+
+ stashedNotifications->Clear();
+ }
+ }
+
+ if (myEndpoint && GetEnableHA()) {
+ Log(LogNotice, "NotificationComponent")
+ << "Reminder notification '" << notificationName << "': HA cluster active, this endpoint does not have the authority (paused=true). Skipping.";
+ continue;
+ }
+ }
+
+ Checkable::Ptr checkable = notification->GetCheckable();
+
+ if (!IcingaApplication::GetInstance()->GetEnableNotifications() || !checkable->GetEnableNotifications())
+ continue;
+
+ bool reachable = checkable->IsReachable(DependencyNotification);
+
+ if (reachable) {
+ {
+ Array::Ptr unstashedNotifications = new Array();
+
+ {
+ auto stashedNotifications (notification->GetStashedNotifications());
+ ObjectLock olock(stashedNotifications);
+
+ stashedNotifications->CopyTo(unstashedNotifications);
+ stashedNotifications->Clear();
+ }
+
+ ObjectLock olock(unstashedNotifications);
+
+ for (Dictionary::Ptr unstashedNotification : unstashedNotifications) {
+ if (!unstashedNotification)
+ continue;
+
+ try {
+ Log(LogNotice, "NotificationComponent")
+ << "Attempting to send stashed notification '" << notificationName << "'.";
+
+ notification->BeginExecuteNotification(
+ (NotificationType)(int)unstashedNotification->Get("notification_type"),
+ (CheckResult::Ptr)unstashedNotification->Get("cr"),
+ (bool)unstashedNotification->Get("force"),
+ (bool)unstashedNotification->Get("reminder"),
+ (String)unstashedNotification->Get("author"),
+ (String)unstashedNotification->Get("text")
+ );
+ } catch (const std::exception& ex) {
+ Log(LogWarning, "NotificationComponent")
+ << "Exception occurred during notification for object '"
+ << notificationName << "': " << DiagnosticInformation(ex, false);
+ }
+ }
+ }
+
+ FireSuppressedNotifications(notification);
+ }
+
+ if (notification->GetInterval() <= 0 && notification->GetNoMoreNotifications()) {
+ Log(LogNotice, "NotificationComponent")
+ << "Reminder notification '" << notificationName << "': Notification was sent out once and interval=0 disables reminder notifications.";
+ continue;
+ }
+
+ if (notification->GetNextNotification() > now)
+ continue;
+
+ {
+ ObjectLock olock(notification);
+ notification->SetNextNotification(Utility::GetTime() + notification->GetInterval());
+ }
+
+ {
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ ObjectLock olock(checkable);
+
+ if (checkable->GetStateType() == StateTypeSoft)
+ continue;
+
+ /* Don't send reminder notifications for OK/Up states. */
+ if ((service && service->GetState() == ServiceOK) || (!service && host->GetState() == HostUp))
+ continue;
+
+ /* Don't send reminder notifications before initial ones. */
+ if (checkable->GetSuppressedNotifications() & NotificationProblem || notification->GetSuppressedNotifications() & NotificationProblem)
+ continue;
+
+ /* Skip in runtime filters. */
+ if (!reachable || checkable->IsInDowntime() || checkable->IsAcknowledged() || checkable->IsFlapping())
+ continue;
+ }
+
+ try {
+ Log(LogNotice, "NotificationComponent")
+ << "Attempting to send reminder notification '" << notificationName << "'.";
+
+ notification->BeginExecuteNotification(NotificationProblem, checkable->GetLastCheckResult(), false, true);
+ } catch (const std::exception& ex) {
+ Log(LogWarning, "NotificationComponent")
+ << "Exception occurred during notification for object '"
+ << notificationName << "': " << DiagnosticInformation(ex, false);
+ }
+ }
+}
+
+/**
+ * Processes icinga::SendNotifications messages.
+ */
+void NotificationComponent::SendNotificationsHandler(const Checkable::Ptr& checkable, NotificationType type,
+ const CheckResult::Ptr& cr, const String& author, const String& text)
+{
+ checkable->SendNotifications(type, cr, author, text);
+}