diff options
Diffstat (limited to '')
-rw-r--r-- | lib/notification/CMakeLists.txt | 34 | ||||
-rw-r--r-- | lib/notification/notificationcomponent.cpp | 269 | ||||
-rw-r--r-- | lib/notification/notificationcomponent.hpp | 38 | ||||
-rw-r--r-- | lib/notification/notificationcomponent.ti | 19 |
4 files changed, 360 insertions, 0 deletions
diff --git a/lib/notification/CMakeLists.txt b/lib/notification/CMakeLists.txt new file mode 100644 index 0000000..783b4fa --- /dev/null +++ b/lib/notification/CMakeLists.txt @@ -0,0 +1,34 @@ +# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ + +mkclass_target(notificationcomponent.ti notificationcomponent-ti.cpp notificationcomponent-ti.hpp) + +set(notification_SOURCES + notificationcomponent.cpp notificationcomponent.hpp notificationcomponent-ti.hpp +) + +if(ICINGA2_UNITY_BUILD) + mkunity_target(notification notification notification_SOURCES) +endif() + +add_library(notification OBJECT ${notification_SOURCES}) + +add_dependencies(notification base config icinga) + +set_target_properties ( + notification PROPERTIES + FOLDER Components +) + +install_if_not_exists( + ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/notification.conf + ${ICINGA2_CONFIGDIR}/features-available +) + +if(NOT WIN32) + install(CODE "file(MAKE_DIRECTORY \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled\")") + install(CODE "execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink ../features-available/notification.conf \"\$ENV{DESTDIR}${ICINGA2_FULL_CONFIGDIR}/features-enabled/notification.conf\")") +else() + install_if_not_exists(${PROJECT_SOURCE_DIR}/etc/icinga2/features-enabled/notification.conf ${ICINGA2_CONFIGDIR}/features-enabled) +endif() + +set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE) diff --git a/lib/notification/notificationcomponent.cpp b/lib/notification/notificationcomponent.cpp new file mode 100644 index 0000000..53fcffe --- /dev/null +++ b/lib/notification/notificationcomponent.cpp @@ -0,0 +1,269 @@ +/* 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 = new Timer(); + m_NotificationTimer->SetInterval(5); + m_NotificationTimer->OnTimerExpired.connect([this](const Timer * const&) { NotificationTimerHandler(); }); + m_NotificationTimer->Start(); +} + +void NotificationComponent::Stop(bool runtimeRemoved) +{ + 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); +} diff --git a/lib/notification/notificationcomponent.hpp b/lib/notification/notificationcomponent.hpp new file mode 100644 index 0000000..09434e2 --- /dev/null +++ b/lib/notification/notificationcomponent.hpp @@ -0,0 +1,38 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#ifndef NOTIFICATIONCOMPONENT_H +#define NOTIFICATIONCOMPONENT_H + +#include "notification/notificationcomponent-ti.hpp" +#include "icinga/service.hpp" +#include "base/configobject.hpp" +#include "base/timer.hpp" + +namespace icinga +{ + +/** + * @ingroup notification + */ +class NotificationComponent final : public ObjectImpl<NotificationComponent> +{ +public: + DECLARE_OBJECT(NotificationComponent); + DECLARE_OBJECTNAME(NotificationComponent); + + static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata); + + void Start(bool runtimeCreated) override; + void Stop(bool runtimeRemoved) override; + +private: + Timer::Ptr m_NotificationTimer; + + void NotificationTimerHandler(); + void SendNotificationsHandler(const Checkable::Ptr& checkable, NotificationType type, + const CheckResult::Ptr& cr, const String& author, const String& text); +}; + +} + +#endif /* NOTIFICATIONCOMPONENT_H */ diff --git a/lib/notification/notificationcomponent.ti b/lib/notification/notificationcomponent.ti new file mode 100644 index 0000000..13af136 --- /dev/null +++ b/lib/notification/notificationcomponent.ti @@ -0,0 +1,19 @@ +/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ + +#include "base/configobject.hpp" + +library notification; + +namespace icinga +{ + +class NotificationComponent : ConfigObject +{ + activation_priority 200; + + [config] bool enable_ha (EnableHA) { + default {{{ return true; }}} + }; +}; + +} |