]> granicus.if.org Git - icinga2/blob - lib/icinga/checkable-notification.cpp
Fix spelling errors.
[icinga2] / lib / icinga / checkable-notification.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "icinga/checkable.hpp"
4 #include "icinga/host.hpp"
5 #include "icinga/icingaapplication.hpp"
6 #include "icinga/service.hpp"
7 #include "base/dictionary.hpp"
8 #include "base/objectlock.hpp"
9 #include "base/logger.hpp"
10 #include "base/exception.hpp"
11 #include "base/context.hpp"
12 #include "base/convert.hpp"
13 #include "remote/apilistener.hpp"
14
15 using namespace icinga;
16
17 boost::signals2::signal<void (const Notification::Ptr&, const Checkable::Ptr&, const std::set<User::Ptr>&,
18         const NotificationType&, const CheckResult::Ptr&, const String&, const String&,
19         const MessageOrigin::Ptr&)> Checkable::OnNotificationSentToAllUsers;
20 boost::signals2::signal<void (const Notification::Ptr&, const Checkable::Ptr&, const User::Ptr&,
21         const NotificationType&, const CheckResult::Ptr&, const NotificationResult::Ptr&, const String&,
22         const String&, const String&, const MessageOrigin::Ptr&)> Checkable::OnNotificationSentToUser;
23
24 void Checkable::ResetNotificationNumbers()
25 {
26         for (const Notification::Ptr& notification : GetNotifications()) {
27                 ObjectLock olock(notification);
28                 notification->ResetNotificationNumber();
29         }
30 }
31
32 void Checkable::SendNotifications(NotificationType type, const CheckResult::Ptr& cr, const String& author, const String& text)
33 {
34         String checkableName = GetName();
35
36         CONTEXT("Sending notifications for object '" + checkableName + "'");
37
38         bool force = GetForceNextNotification();
39
40         SetForceNextNotification(false);
41
42         if (!IcingaApplication::GetInstance()->GetEnableNotifications() || !GetEnableNotifications()) {
43                 if (!force) {
44                         Log(LogInformation, "Checkable")
45                                 << "Notifications are disabled for checkable '" << checkableName << "'.";
46                         return;
47                 }
48         }
49
50         std::set<Notification::Ptr> notifications = GetNotifications();
51
52         String notificationTypeName = Notification::NotificationTypeToString(type);
53
54         // Bail early if there are no notifications.
55         if (notifications.empty()) {
56                 Log(LogNotice, "Checkable")
57                         << "Skipping checkable '" << checkableName << "' which doesn't have any notification objects configured.";
58                 return;
59         }
60
61         Log(LogInformation, "Checkable")
62                 << "Checkable '" << checkableName << "' has " << notifications.size()
63                 << " notification(s). Checking filters for type '" << notificationTypeName << "', sends will be logged.";
64
65         for (const Notification::Ptr& notification : notifications) {
66                 // Re-send stashed notifications from cold startup.
67                 if (ApiListener::UpdatedObjectAuthority()) {
68                         try {
69                                 if (!notification->IsPaused()) {
70                                         auto stashedNotifications (notification->GetStashedNotifications());
71
72                                         if (stashedNotifications->GetLength()) {
73                                                 Log(LogNotice, "Notification")
74                                                         << "Notification '" << notification->GetName() << "': there are some stashed notifications. Stashing notification to preserve order.";
75
76                                                 stashedNotifications->Add(new Dictionary({
77                                                         {"type", type},
78                                                         {"cr", cr},
79                                                         {"force", force},
80                                                         {"reminder", false},
81                                                         {"author", author},
82                                                         {"text", text}
83                                                 }));
84                                         } else {
85                                                 notification->BeginExecuteNotification(type, cr, force, false, author, text);
86                                         }
87                                 } else {
88                                         Log(LogNotice, "Notification")
89                                                 << "Notification '" << notification->GetName() << "': HA cluster active, this endpoint does not have the authority (paused=true). Skipping.";
90                                 }
91                         } catch (const std::exception& ex) {
92                                 Log(LogWarning, "Checkable")
93                                         << "Exception occurred during notification '" << notification->GetName() << "' for checkable '"
94                                         << GetName() << "': " << DiagnosticInformation(ex, false);
95                         }
96                 } else {
97                         // Cold startup phase. Stash notification for later.
98                         Log(LogNotice, "Notification")
99                                 << "Notification '" << notification->GetName() << "': object authority hasn't been updated, yet. Stashing notification.";
100
101                         notification->GetStashedNotifications()->Add(new Dictionary({
102                                 {"type", type},
103                                 {"cr", cr},
104                                 {"force", force},
105                                 {"reminder", false},
106                                 {"author", author},
107                                 {"text", text}
108                         }));
109                 }
110         }
111 }
112
113 std::set<Notification::Ptr> Checkable::GetNotifications() const
114 {
115         boost::mutex::scoped_lock lock(m_NotificationMutex);
116         return m_Notifications;
117 }
118
119 void Checkable::RegisterNotification(const Notification::Ptr& notification)
120 {
121         boost::mutex::scoped_lock lock(m_NotificationMutex);
122         m_Notifications.insert(notification);
123 }
124
125 void Checkable::UnregisterNotification(const Notification::Ptr& notification)
126 {
127         boost::mutex::scoped_lock lock(m_NotificationMutex);
128         m_Notifications.erase(notification);
129 }
130
131 static void FireSuppressedNotifications(Checkable* checkable)
132 {
133         if (!checkable->IsActive())
134                 return;
135
136         if (checkable->IsPaused())
137                 return;
138
139         if (!checkable->GetEnableNotifications())
140                 return;
141
142         int suppressed_types (checkable->GetSuppressedNotifications());
143         if (!suppressed_types)
144                 return;
145
146         int subtract = 0;
147
148         for (auto type : {NotificationProblem, NotificationRecovery, NotificationFlappingStart, NotificationFlappingEnd}) {
149                 if (suppressed_types & type) {
150                         bool still_applies;
151                         auto cr (checkable->GetLastCheckResult());
152
153                         switch (type) {
154                                 case NotificationProblem:
155                                         still_applies = cr && !checkable->IsStateOK(cr->GetState()) && checkable->GetStateType() == StateTypeHard;
156                                         break;
157                                 case NotificationRecovery:
158                                         still_applies = cr && checkable->IsStateOK(cr->GetState());
159                                         break;
160                                 case NotificationFlappingStart:
161                                         still_applies = checkable->IsFlapping();
162                                         break;
163                                 case NotificationFlappingEnd:
164                                         still_applies = !checkable->IsFlapping();
165                                         break;
166                                 default:
167                                         break;
168                         }
169
170                         if (still_applies) {
171                                 bool still_suppressed;
172
173                                 switch (type) {
174                                         case NotificationProblem:
175                                                 /* Fall through. */
176                                         case NotificationRecovery:
177                                                 still_suppressed = !checkable->IsReachable(DependencyNotification) || checkable->IsInDowntime() || checkable->IsAcknowledged();
178                                                 break;
179                                         case NotificationFlappingStart:
180                                                 /* Fall through. */
181                                         case NotificationFlappingEnd:
182                                                 still_suppressed = checkable->IsInDowntime();
183                                                 break;
184                                         default:
185                                                 break;
186                                 }
187
188                                 if (!still_suppressed && checkable->GetEnableActiveChecks()) {
189                                         /* If e.g. the downtime just ended, but the service is still not ok, we would re-send the stashed problem notification.
190                                          * But if the next check result recovers the service soon, we would send a recovery notification soon after the problem one.
191                                          * This is not desired, especially for lots of services at once.
192                                          * Because of that if there's likely to be a check result soon,
193                                          * we delay the re-sending of the stashed notification until the next check.
194                                          * That check either doesn't change anything and we finally re-send the stashed problem notification
195                                          * or recovers the service and we drop the stashed notification. */
196
197                                         /* One minute unless the check interval is too short so the next check will always run during the next minute. */
198                                         auto threshold (checkable->GetCheckInterval() - 10);
199
200                                         if (threshold > 60)
201                                                 threshold = 60;
202                                         else if (threshold < 0)
203                                                 threshold = 0;
204
205                                         still_suppressed = checkable->GetNextCheck() <= Utility::GetTime() + threshold;
206                                 }
207
208                                 if (!still_suppressed) {
209                                         Checkable::OnNotificationsRequested(checkable, type, cr, "", "", nullptr);
210
211                                         subtract |= type;
212                                 }
213                         } else {
214                                 subtract |= type;
215                         }
216                 }
217         }
218
219         if (subtract) {
220                 ObjectLock olock (checkable);
221
222                 int suppressed_types_before (checkable->GetSuppressedNotifications());
223                 int suppressed_types_after (suppressed_types_before & ~subtract);
224
225                 if (suppressed_types_after != suppressed_types_before) {
226                         checkable->SetSuppressedNotifications(suppressed_types_after);
227                 }
228         }
229 }
230
231 /**
232  * Re-sends all notifications previously suppressed by e.g. downtimes if the notification reason still applies.
233  */
234 void Checkable::FireSuppressedNotifications(const Timer * const&)
235 {
236         for (auto& host : ConfigType::GetObjectsByType<Host>()) {
237                 ::FireSuppressedNotifications(host.get());
238         }
239
240         for (auto& service : ConfigType::GetObjectsByType<Service>()) {
241                 ::FireSuppressedNotifications(service.get());
242         }
243 }