]> granicus.if.org Git - icinga2/blob - lib/icinga/notification.cpp
compatlog: refactor custom/acknowledgement notifications with author/commenttext
[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 "icinga/notificationmessage.h"
25 #include "remoting/endpointmanager.h"
26 #include "base/dynamictype.h"
27 #include "base/objectlock.h"
28 #include "base/logger_fwd.h"
29 #include "base/utility.h"
30 #include "base/convert.h"
31 #include <boost/tuple/tuple.hpp>
32 #include <boost/foreach.hpp>
33 #include <boost/exception/diagnostic_information.hpp>
34
35 using namespace icinga;
36
37 REGISTER_TYPE(Notification);
38
39 Notification::Notification(const Dictionary::Ptr& serializedUpdate)
40         : DynamicObject(serializedUpdate)
41 {
42         RegisterAttribute("notification_command", Attribute_Config, &m_NotificationCommand);
43         RegisterAttribute("notification_interval", Attribute_Config, &m_NotificationInterval);
44         RegisterAttribute("notification_period", Attribute_Config, &m_NotificationPeriod);
45         RegisterAttribute("last_notification", Attribute_Replicated, &m_LastNotification);
46         RegisterAttribute("next_notification", Attribute_Replicated, &m_NextNotification);
47         RegisterAttribute("macros", Attribute_Config, &m_Macros);
48         RegisterAttribute("users", Attribute_Config, &m_Users);
49         RegisterAttribute("groups", Attribute_Config, &m_Groups);
50         RegisterAttribute("times", Attribute_Config, &m_Times);
51         RegisterAttribute("notification_type_filter", Attribute_Config, &m_NotificationTypeFilter);
52         RegisterAttribute("notification_state_filter", Attribute_Config, &m_NotificationStateFilter);
53         RegisterAttribute("host_name", Attribute_Config, &m_HostName);
54         RegisterAttribute("service", Attribute_Config, &m_Service);
55         RegisterAttribute("export_macros", Attribute_Config, &m_ExportMacros);
56 }
57
58 Notification::~Notification(void)
59 {
60         Service::InvalidateNotificationsCache();
61 }
62
63 Notification::Ptr Notification::GetByName(const String& name)
64 {
65         DynamicObject::Ptr configObject = DynamicObject::GetObject("Notification", name);
66
67         return dynamic_pointer_cast<Notification>(configObject);
68 }
69
70 Service::Ptr Notification::GetService(void) const
71 {
72         Host::Ptr host = Host::GetByName(m_HostName);
73
74         if (!host)
75                 return Service::Ptr();
76
77         if (m_Service.IsEmpty())
78                 return host->GetHostCheckService();
79         else
80                 return host->GetServiceByShortName(m_Service);
81 }
82
83 NotificationCommand::Ptr Notification::GetNotificationCommand(void) const
84 {
85         return NotificationCommand::GetByName(m_NotificationCommand);
86 }
87
88 Dictionary::Ptr Notification::GetMacros(void) const
89 {
90         return m_Macros;
91 }
92
93 Array::Ptr Notification::GetExportMacros(void) const
94 {
95         return m_ExportMacros;
96 }
97
98 std::set<User::Ptr> Notification::GetUsers(void) const
99 {
100         std::set<User::Ptr> result;
101
102         Array::Ptr users = m_Users;
103
104         if (users) {
105                 ObjectLock olock(users);
106
107                 BOOST_FOREACH(const String& name, users) {
108                         User::Ptr user = User::GetByName(name);
109
110                         if (!user)
111                                 continue;
112
113                         result.insert(user);
114                 }
115         }
116
117         return result;
118 }
119
120 std::set<UserGroup::Ptr> Notification::GetGroups(void) const
121 {
122         std::set<UserGroup::Ptr> result;
123
124         Array::Ptr groups = m_Groups;
125
126         if (groups) {
127                 ObjectLock olock(groups);
128
129                 BOOST_FOREACH(const String& name, groups) {
130                         UserGroup::Ptr ug = UserGroup::GetByName(name);
131
132                         if (!ug)
133                                 continue;
134
135                         result.insert(ug);
136                 }
137         }
138
139         return result;
140 }
141
142 Dictionary::Ptr Notification::GetTimes(void) const
143 {
144         return m_Times;
145 }
146
147 unsigned long Notification::GetNotificationTypeFilter(void) const
148 {
149         if (m_NotificationTypeFilter.IsEmpty())
150                 return ~(unsigned long)0; /* All states. */
151         else
152                 return m_NotificationTypeFilter;
153 }
154
155 unsigned long Notification::GetNotificationStateFilter(void) const
156 {
157         if (m_NotificationStateFilter.IsEmpty())
158                 return ~(unsigned long)0; /* All states. */
159         else
160                 return m_NotificationStateFilter;
161 }
162
163 double Notification::GetNotificationInterval(void) const
164 {
165         if (m_NotificationInterval.IsEmpty())
166                 return 300;
167         else
168                 return m_NotificationInterval;
169 }
170
171 TimePeriod::Ptr Notification::GetNotificationPeriod(void) const
172 {
173         return TimePeriod::GetByName(m_NotificationPeriod);
174 }
175
176 double Notification::GetLastNotification(void) const
177 {
178         if (m_LastNotification.IsEmpty())
179                 return 0;
180         else
181                 return m_LastNotification;
182 }
183
184 /**
185  * Sets the timestamp when the last notification was sent.
186  */
187 void Notification::SetLastNotification(double time)
188 {
189         m_LastNotification = time;
190         Touch("last_notification");
191 }
192
193 double Notification::GetNextNotification(void) const
194 {
195         if (m_NextNotification.IsEmpty())
196                 return 0;
197         else
198                 return m_NextNotification;
199 }
200
201 /**
202  * Sets the timestamp when the next periodical notification should be sent.
203  * This does not affect notifications that are sent for state changes.
204  */
205 void Notification::SetNextNotification(double time)
206 {
207         m_NextNotification = time;
208         Touch("next_notification");
209 }
210
211 String Notification::NotificationTypeToString(NotificationType type)
212 {
213         switch (type) {
214                 case NotificationDowntimeStart:
215                         return "DOWNTIMESTART";
216                 case NotificationDowntimeEnd:
217                         return "DOWNTIMEEND";
218                 case NotificationDowntimeRemoved:
219                         return "DOWNTIMECANCELLED";
220                 case NotificationCustom:
221                         return "CUSTOM";
222                 case NotificationAcknowledgement:
223                         return "ACKNOWLEDGEMENT";
224                 case NotificationProblem:
225                         return "PROBLEM";
226                 case NotificationRecovery:
227                         return "RECOVERY";
228                 case NotificationFlappingStart:
229                         return "FLAPPINGSTART";
230                 case NotificationFlappingEnd:
231                         return "FLAPPINGEND";
232                 default:
233                         return "UNKNOWN_NOTIFICATION";
234         }
235 }
236
237 void Notification::BeginExecuteNotification(NotificationType type, const Dictionary::Ptr& cr, bool force, const String& author, const String& text)
238 {
239         ASSERT(!OwnsLock());
240
241         if (!force) {
242                 TimePeriod::Ptr tp = GetNotificationPeriod();
243
244                 if (tp && !tp->IsInside(Utility::GetTime())) {
245                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': not in timeperiod");
246                         return;
247                 }
248
249                 double now = Utility::GetTime();
250                 Dictionary::Ptr times = GetTimes();
251                 Service::Ptr service = GetService();
252
253                 if (times && times->Contains("begin") && now < service->GetLastHardStateChange() + times->Get("begin")) {
254                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': before escalation range");
255                         return;
256                 }
257
258                 if (times && times->Contains("end") && now > service->GetLastHardStateChange() + times->Get("end")) {
259                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': after escalation range");
260                         return;
261                 }
262
263                 unsigned long ftype = 1 << type;
264
265                 Log(LogDebug, "icinga", "FType=" + Convert::ToString(ftype) + ", TypeFilter=" + Convert::ToString(GetNotificationTypeFilter()));
266
267                 if (!(ftype & GetNotificationTypeFilter())) {
268                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': type filter does not match");
269                         return;
270                 }
271
272                 unsigned long fstate = 1 << GetService()->GetState();
273
274                 if (!(fstate & GetNotificationStateFilter())) {
275                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': state filter does not match");
276                         return;
277                 }
278         }
279
280         {
281                 ObjectLock olock(this);
282
283                 SetLastNotification(Utility::GetTime());
284         }
285
286         std::set<User::Ptr> allUsers;
287
288         std::set<User::Ptr> users = GetUsers();
289         std::copy(users.begin(), users.end(), std::inserter(allUsers, allUsers.begin()));
290
291         BOOST_FOREACH(const UserGroup::Ptr& ug, GetGroups()) {
292                 std::set<User::Ptr> members = ug->GetMembers();
293                 std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin()));
294         }
295
296         BOOST_FOREACH(const User::Ptr& user, allUsers) {
297                 Log(LogDebug, "icinga", "Sending notification for user '" + user->GetName() + "'");
298                 Utility::QueueAsyncCallback(boost::bind(&Notification::ExecuteNotificationHelper, this, type, user, cr, force, author, text));
299         }
300 }
301
302 void Notification::ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const Dictionary::Ptr& cr, bool force, const String& author, const String& text)
303 {
304         ASSERT(!OwnsLock());
305
306         if (!force) {
307                 TimePeriod::Ptr tp = user->GetNotificationPeriod();
308
309                 if (tp && !tp->IsInside(Utility::GetTime())) {
310                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
311                             GetName() + " and user '" + user->GetName() + "': user not in timeperiod");
312                         return;
313                 }
314
315                 unsigned long ftype = 1 << type;
316
317                 if (!(ftype & user->GetNotificationTypeFilter())) {
318                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
319                             GetName() + " and user '" + user->GetName() + "': type filter does not match");
320                         return;
321                 }
322
323                 unsigned long fstate = 1 << GetService()->GetState();
324
325                 if (!(fstate & user->GetNotificationStateFilter())) {
326                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
327                             GetName() + " and user '" + user->GetName() + "': state filter does not match");
328                         return;
329                 }
330         }
331
332         try {
333                 GetNotificationCommand()->Execute(GetSelf(), user, cr, type);
334
335                 RequestMessage rm;
336                 rm.SetMethod("icinga::NotificationSent");
337
338                 NotificationMessage params;
339
340                 params.SetService(GetService()->GetName());
341                 params.SetUser(user->GetName());
342                 params.SetType(type);
343                 params.SetAuthor(author);
344                 params.SetCommentText(text);
345                 params.SetCheckResult(cr);
346
347                 rm.SetParams(params);
348
349                 EndpointManager::GetInstance()->SendMulticastMessage(rm);
350
351                 Log(LogInformation, "icinga", "Completed sending notification for service '" + GetService()->GetName() + "'");
352         } catch (const std::exception& ex) {
353                 std::ostringstream msgbuf;
354                 msgbuf << "Exception occured during notification for service '"
355                        << GetService()->GetName() << "': " << boost::diagnostic_information(ex);
356                 String message = msgbuf.str();
357
358                 Log(LogWarning, "icinga", message);
359         }
360 }
361
362 void Notification::OnAttributeChanged(const String& name)
363 {
364         ASSERT(!OwnsLock());
365
366         if (name == "host_name" || name == "service")
367                 Service::InvalidateNotificationsCache();
368 }
369
370 bool Notification::ResolveMacro(const String& macro, const Dictionary::Ptr&, String *result) const
371 {
372         Dictionary::Ptr macros = GetMacros();
373
374         if (macros && macros->Contains(macro)) {
375                 *result = macros->Get(macro);
376                 return true;
377         }
378
379         return false;
380 }