]> granicus.if.org Git - icinga2/blob - lib/icinga/notification.cpp
Refactor JSON-RPC library and implement check-result messages.
[icinga2] / lib / icinga / notification.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012 Icinga Development Team (http://www.icinga.org/)        *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "icinga/notification.h"
21 #include "icinga/notificationcommand.h"
22 #include "icinga/macroprocessor.h"
23 #include "icinga/service.h"
24 #include "base/dynamictype.h"
25 #include "base/objectlock.h"
26 #include "base/logger_fwd.h"
27 #include "base/utility.h"
28 #include "base/convert.h"
29 #include <boost/tuple/tuple.hpp>
30 #include <boost/foreach.hpp>
31 #include <boost/exception/diagnostic_information.hpp>
32
33 using namespace icinga;
34
35 REGISTER_TYPE(Notification);
36
37 void Notification::Start(void)
38 {
39         DynamicObject::Start();
40
41         GetService()->AddNotification(GetSelf());
42 }
43
44 void Notification::Stop(void)
45 {
46         DynamicObject::Stop();
47
48         GetService()->RemoveNotification(GetSelf());
49 }
50
51 Service::Ptr Notification::GetService(void) const
52 {
53         Host::Ptr host = Host::GetByName(m_HostName);
54
55         if (!host)
56                 return Service::Ptr();
57
58         if (m_Service.IsEmpty())
59                 return host->GetHostCheckService();
60         else
61                 return host->GetServiceByShortName(m_Service);
62 }
63
64 NotificationCommand::Ptr Notification::GetNotificationCommand(void) const
65 {
66         return NotificationCommand::GetByName(m_NotificationCommand);
67 }
68
69 Dictionary::Ptr Notification::GetMacros(void) const
70 {
71         return m_Macros;
72 }
73
74 Array::Ptr Notification::GetExportMacros(void) const
75 {
76         return m_ExportMacros;
77 }
78
79 std::set<User::Ptr> Notification::GetUsers(void) const
80 {
81         std::set<User::Ptr> result;
82
83         Array::Ptr users = m_Users;
84
85         if (users) {
86                 ObjectLock olock(users);
87
88                 BOOST_FOREACH(const String& name, users) {
89                         User::Ptr user = User::GetByName(name);
90
91                         if (!user)
92                                 continue;
93
94                         result.insert(user);
95                 }
96         }
97
98         return result;
99 }
100
101 std::set<UserGroup::Ptr> Notification::GetGroups(void) const
102 {
103         std::set<UserGroup::Ptr> result;
104
105         Array::Ptr groups = m_Groups;
106
107         if (groups) {
108                 ObjectLock olock(groups);
109
110                 BOOST_FOREACH(const String& name, groups) {
111                         UserGroup::Ptr ug = UserGroup::GetByName(name);
112
113                         if (!ug)
114                                 continue;
115
116                         result.insert(ug);
117                 }
118         }
119
120         return result;
121 }
122
123 Dictionary::Ptr Notification::GetTimes(void) const
124 {
125         return m_Times;
126 }
127
128 unsigned long Notification::GetNotificationTypeFilter(void) const
129 {
130         if (m_NotificationTypeFilter.IsEmpty())
131                 return ~(unsigned long)0; /* All states. */
132         else
133                 return m_NotificationTypeFilter;
134 }
135
136 unsigned long Notification::GetNotificationStateFilter(void) const
137 {
138         if (m_NotificationStateFilter.IsEmpty())
139                 return ~(unsigned long)0; /* All states. */
140         else
141                 return m_NotificationStateFilter;
142 }
143
144 double Notification::GetNotificationInterval(void) const
145 {
146         if (m_NotificationInterval.IsEmpty())
147                 return 300;
148         else
149                 return m_NotificationInterval;
150 }
151
152 TimePeriod::Ptr Notification::GetNotificationPeriod(void) const
153 {
154         return TimePeriod::GetByName(m_NotificationPeriod);
155 }
156
157 double Notification::GetLastNotification(void) const
158 {
159         if (m_LastNotification.IsEmpty())
160                 return 0;
161         else
162                 return m_LastNotification;
163 }
164
165 /**
166  * Sets the timestamp when the last notification was sent.
167  */
168 void Notification::SetLastNotification(double time)
169 {
170         m_LastNotification = time;
171 }
172
173 double Notification::GetNextNotification(void) const
174 {
175         if (m_NextNotification.IsEmpty())
176                 return 0;
177         else
178                 return m_NextNotification;
179 }
180
181 /**
182  * Sets the timestamp when the next periodical notification should be sent.
183  * This does not affect notifications that are sent for state changes.
184  */
185 void Notification::SetNextNotification(double time)
186 {
187         m_NextNotification = time;
188 }
189
190 long Notification::GetNotificationNumber(void) const
191 {
192         if (m_NotificationNumber.IsEmpty())
193                 return 0;
194         else
195                 return m_NotificationNumber;
196 }
197
198 void Notification::UpdateNotificationNumber(void)
199 {
200         m_NotificationNumber = m_NotificationNumber + 1;
201 }
202
203 void Notification::ResetNotificationNumber(void)
204 {
205         m_NotificationNumber = 0;
206 }
207
208 String Notification::NotificationTypeToString(NotificationType type)
209 {
210         switch (type) {
211                 case NotificationDowntimeStart:
212                         return "DOWNTIMESTART";
213                 case NotificationDowntimeEnd:
214                         return "DOWNTIMEEND";
215                 case NotificationDowntimeRemoved:
216                         return "DOWNTIMECANCELLED";
217                 case NotificationCustom:
218                         return "CUSTOM";
219                 case NotificationAcknowledgement:
220                         return "ACKNOWLEDGEMENT";
221                 case NotificationProblem:
222                         return "PROBLEM";
223                 case NotificationRecovery:
224                         return "RECOVERY";
225                 case NotificationFlappingStart:
226                         return "FLAPPINGSTART";
227                 case NotificationFlappingEnd:
228                         return "FLAPPINGEND";
229                 default:
230                         return "UNKNOWN_NOTIFICATION";
231         }
232 }
233
234 void Notification::BeginExecuteNotification(NotificationType type, const Dictionary::Ptr& cr, bool force, const String& author, const String& text)
235 {
236         ASSERT(!OwnsLock());
237
238         if (!force) {
239                 TimePeriod::Ptr tp = GetNotificationPeriod();
240
241                 if (tp && !tp->IsInside(Utility::GetTime())) {
242                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': not in timeperiod");
243                         return;
244                 }
245
246                 double now = Utility::GetTime();
247                 Dictionary::Ptr times = GetTimes();
248                 Service::Ptr service = GetService();
249
250                 if (type == NotificationProblem) {
251                         if (times && times->Contains("begin") && now < service->GetLastHardStateChange() + times->Get("begin")) {
252                                 Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': before escalation range");
253                                 return;
254                         }
255
256                         if (times && times->Contains("end") && now > service->GetLastHardStateChange() + times->Get("end")) {
257                                 Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': after escalation range");
258                                 return;
259                         }
260                 }
261
262                 unsigned long ftype = 1 << type;
263
264                 Log(LogDebug, "icinga", "FType=" + Convert::ToString(ftype) + ", TypeFilter=" + Convert::ToString(GetNotificationTypeFilter()));
265
266                 if (!(ftype & GetNotificationTypeFilter())) {
267                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': type filter does not match");
268                         return;
269                 }
270
271                 unsigned long fstate = 1 << GetService()->GetState();
272
273                 if (!(fstate & GetNotificationStateFilter())) {
274                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': state filter does not match");
275                         return;
276                 }
277         }
278
279         {
280                 ObjectLock olock(this);
281
282                 SetLastNotification(Utility::GetTime());
283         }
284
285         std::set<User::Ptr> allUsers;
286
287         std::set<User::Ptr> users = GetUsers();
288         std::copy(users.begin(), users.end(), std::inserter(allUsers, allUsers.begin()));
289
290         BOOST_FOREACH(const UserGroup::Ptr& ug, GetGroups()) {
291                 std::set<User::Ptr> members = ug->GetMembers();
292                 std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin()));
293         }
294
295         BOOST_FOREACH(const User::Ptr& user, allUsers) {
296                 Log(LogDebug, "icinga", "Sending notification for user '" + user->GetName() + "'");
297                 Utility::QueueAsyncCallback(boost::bind(&Notification::ExecuteNotificationHelper, this, type, user, cr, force, author, text));
298         }
299 }
300
301 void Notification::ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const Dictionary::Ptr& cr, bool force, const String& author, const String& text)
302 {
303         ASSERT(!OwnsLock());
304
305         if (!force) {
306                 TimePeriod::Ptr tp = user->GetNotificationPeriod();
307
308                 if (tp && !tp->IsInside(Utility::GetTime())) {
309                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
310                             GetName() + " and user '" + user->GetName() + "': user not in timeperiod");
311                         return;
312                 }
313
314                 unsigned long ftype = 1 << type;
315
316                 if (!(ftype & user->GetNotificationTypeFilter())) {
317                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
318                             GetName() + " and user '" + user->GetName() + "': type filter does not match");
319                         return;
320                 }
321
322                 unsigned long fstate = 1 << GetService()->GetState();
323
324                 if (!(fstate & user->GetNotificationStateFilter())) {
325                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
326                             GetName() + " and user '" + user->GetName() + "': state filter does not match");
327                         return;
328                 }
329         }
330
331         try {
332                 GetNotificationCommand()->Execute(GetSelf(), user, cr, type);
333
334                 {
335                         ObjectLock olock(this);
336                         UpdateNotificationNumber();
337                         SetLastNotification(Utility::GetTime());
338                 }
339
340                 Service::OnNotificationSentChanged(GetSelf(), user, type, cr, author, text);
341
342                 Log(LogInformation, "icinga", "Completed sending notification for service '" + GetService()->GetName() + "'");
343         } catch (const std::exception& ex) {
344                 std::ostringstream msgbuf;
345                 msgbuf << "Exception occured during notification for service '"
346                        << GetService()->GetName() << "': " << boost::diagnostic_information(ex);
347                 String message = msgbuf.str();
348
349                 Log(LogWarning, "icinga", message);
350         }
351 }
352
353 bool Notification::ResolveMacro(const String& macro, const Dictionary::Ptr&, String *result) const
354 {
355         Dictionary::Ptr macros = GetMacros();
356
357         if (macros && macros->Contains(macro)) {
358                 *result = macros->Get(macro);
359                 return true;
360         }
361
362         return false;
363 }
364
365 void Notification::InternalSerialize(const Dictionary::Ptr& bag, int attributeTypes) const
366 {
367         DynamicObject::InternalSerialize(bag, attributeTypes);
368
369         bag->Set("notification_command", m_NotificationCommand);
370         bag->Set("notification_interval", m_NotificationInterval);
371         bag->Set("notification_period", m_NotificationPeriod);
372         bag->Set("last_notification", m_LastNotification);
373         bag->Set("next_notification", m_NextNotification);
374         bag->Set("notification_number", m_NotificationNumber);
375         bag->Set("macros", m_Macros);
376         bag->Set("users", m_Users);
377         bag->Set("groups", m_Groups);
378         bag->Set("times", m_Times);
379         bag->Set("notification_type_filter", m_NotificationTypeFilter);
380         bag->Set("notification_state_filter", m_NotificationStateFilter);
381         bag->Set("host_name", m_HostName);
382         bag->Set("export_macros", m_ExportMacros);
383         bag->Set("service", m_Service);
384 }
385
386 void Notification::InternalDeserialize(const Dictionary::Ptr& bag, int attributeTypes)
387 {
388         DynamicObject::InternalDeserialize(bag, attributeTypes);
389
390         m_NotificationCommand = bag->Get("notification_command");
391         m_NotificationInterval = bag->Get("notification_interval");
392         m_NotificationPeriod = bag->Get("notification_period");
393         m_LastNotification = bag->Get("last_notification");
394         m_NextNotification = bag->Get("next_notification");
395         m_NotificationNumber = bag->Get("notification_number");
396         m_Macros = bag->Get("macros");
397         m_Users = bag->Get("users");
398         m_Groups = bag->Get("groups");
399         m_Times = bag->Get("times");
400         m_NotificationTypeFilter = bag->Get("notification_type_filter");
401         m_NotificationStateFilter = bag->Get("notification_state_filter");
402         m_HostName = bag->Get("host_name");
403         m_ExportMacros = bag->Get("export_macros");
404         m_Service = bag->Get("service");
405 }