]> granicus.if.org Git - icinga2/blob - lib/icinga/notification.cpp
Merge 'macros' and 'custom' attributes into 'vars', part 1.
[icinga2] / lib / icinga / notification.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 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 "base/exception.h"
30 #include "base/initialize.h"
31 #include "base/scriptvariable.h"
32 #include <boost/foreach.hpp>
33
34 using namespace icinga;
35
36 REGISTER_TYPE(Notification);
37 INITIALIZE_ONCE(&Notification::StaticInitialize);
38
39 boost::signals2::signal<void (const Notification::Ptr&, double, const String&)> Notification::OnNextNotificationChanged;
40
41 void Notification::StaticInitialize(void)
42 {
43         ScriptVariable::Set("NotificationDowntimeStart", NotificationDowntimeStart, true, true);
44         ScriptVariable::Set("NotificationDowntimeEnd", NotificationDowntimeEnd, true, true);
45         ScriptVariable::Set("NotificationDowntimeRemoved", NotificationDowntimeRemoved, true, true);
46         ScriptVariable::Set("NotificationCustom", NotificationCustom, true, true);
47         ScriptVariable::Set("NotificationAcknowledgement", NotificationAcknowledgement, true, true);
48         ScriptVariable::Set("NotificationProblem", NotificationProblem, true, true);
49         ScriptVariable::Set("NotificationRecovery", NotificationRecovery, true, true);
50         ScriptVariable::Set("NotificationFlappingStart", NotificationFlappingStart, true, true);
51         ScriptVariable::Set("NotificationFlappingEnd", NotificationFlappingEnd, true, true);
52
53         ScriptVariable::Set("NotificationFilterDowntimeStart", 1 << NotificationDowntimeStart, true, true);
54         ScriptVariable::Set("NotificationFilterDowntimeEnd", 1 << NotificationDowntimeEnd, true, true);
55         ScriptVariable::Set("NotificationFilterDowntimeRemoved", 1 << NotificationDowntimeRemoved, true, true);
56         ScriptVariable::Set("NotificationFilterCustom", 1 << NotificationCustom, true, true);
57         ScriptVariable::Set("NotificationFilterAcknowledgement", 1 << NotificationAcknowledgement, true, true);
58         ScriptVariable::Set("NotificationFilterProblem", 1 << NotificationProblem, true, true);
59         ScriptVariable::Set("NotificationFilterRecovery", 1 << NotificationRecovery, true, true);
60         ScriptVariable::Set("NotificationFilterFlappingStart", 1 << NotificationFlappingStart, true, true);
61         ScriptVariable::Set("NotificationFilterFlappingEnd", 1 << NotificationFlappingEnd, true, true);
62 }
63
64 void Notification::Start(void)
65 {
66         DynamicObject::Start();
67
68         GetService()->AddNotification(GetSelf());
69 }
70
71 void Notification::Stop(void)
72 {
73         DynamicObject::Stop();
74
75         GetService()->RemoveNotification(GetSelf());
76 }
77
78 Service::Ptr Notification::GetService(void) const
79 {
80         Host::Ptr host = Host::GetByName(GetHostRaw());
81
82         if (GetServiceRaw().IsEmpty())
83                 return host->GetCheckService();
84         else
85                 return host->GetServiceByShortName(GetServiceRaw());
86 }
87
88 NotificationCommand::Ptr Notification::GetNotificationCommand(void) const
89 {
90         return NotificationCommand::GetByName(GetNotificationCommandRaw());
91 }
92
93 std::set<User::Ptr> Notification::GetUsers(void) const
94 {
95         std::set<User::Ptr> result;
96
97         Array::Ptr users = GetUsersRaw();
98
99         if (users) {
100                 ObjectLock olock(users);
101
102                 BOOST_FOREACH(const String& name, users) {
103                         User::Ptr user = User::GetByName(name);
104
105                         if (!user)
106                                 continue;
107
108                         result.insert(user);
109                 }
110         }
111
112         return result;
113 }
114
115 std::set<UserGroup::Ptr> Notification::GetUserGroups(void) const
116 {
117         std::set<UserGroup::Ptr> result;
118
119         Array::Ptr groups = GetUserGroupsRaw();
120
121         if (groups) {
122                 ObjectLock olock(groups);
123
124                 BOOST_FOREACH(const String& name, groups) {
125                         UserGroup::Ptr ug = UserGroup::GetByName(name);
126
127                         if (!ug)
128                                 continue;
129
130                         result.insert(ug);
131                 }
132         }
133
134         return result;
135 }
136
137 TimePeriod::Ptr Notification::GetNotificationPeriod(void) const
138 {
139         return TimePeriod::GetByName(GetNotificationPeriodRaw());
140 }
141
142 double Notification::GetNextNotification(void) const
143 {
144         return GetNextNotificationRaw();
145 }
146
147 /**
148  * Sets the timestamp when the next periodical notification should be sent.
149  * This does not affect notifications that are sent for state changes.
150  */
151 void Notification::SetNextNotification(double time, const String& authority)
152 {
153         SetNextNotificationRaw(time);
154
155         OnNextNotificationChanged(GetSelf(), time, authority);
156 }
157
158 void Notification::UpdateNotificationNumber(void)
159 {
160         SetNotificationNumber(GetNotificationNumber() + 1);
161 }
162
163 void Notification::ResetNotificationNumber(void)
164 {
165         SetNotificationNumber(0);
166 }
167
168 String Notification::NotificationTypeToString(NotificationType type)
169 {
170         switch (type) {
171                 case NotificationDowntimeStart:
172                         return "DOWNTIMESTART";
173                 case NotificationDowntimeEnd:
174                         return "DOWNTIMEEND";
175                 case NotificationDowntimeRemoved:
176                         return "DOWNTIMECANCELLED";
177                 case NotificationCustom:
178                         return "CUSTOM";
179                 case NotificationAcknowledgement:
180                         return "ACKNOWLEDGEMENT";
181                 case NotificationProblem:
182                         return "PROBLEM";
183                 case NotificationRecovery:
184                         return "RECOVERY";
185                 case NotificationFlappingStart:
186                         return "FLAPPINGSTART";
187                 case NotificationFlappingEnd:
188                         return "FLAPPINGEND";
189                 default:
190                         return "UNKNOWN_NOTIFICATION";
191         }
192 }
193
194 void Notification::BeginExecuteNotification(NotificationType type, const CheckResult::Ptr& cr, bool force, const String& author, const String& text)
195 {
196         ASSERT(!OwnsLock());
197
198         if (!force) {
199                 TimePeriod::Ptr tp = GetNotificationPeriod();
200
201                 if (tp && !tp->IsInside(Utility::GetTime())) {
202                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': not in timeperiod");
203                         return;
204                 }
205
206                 double now = Utility::GetTime();
207                 Dictionary::Ptr times = GetTimes();
208                 Service::Ptr service = GetService();
209
210                 if (type == NotificationProblem) {
211                         if (times && times->Contains("begin") && now < service->GetLastHardStateChange() + times->Get("begin")) {
212                                 Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': before escalation range");
213                                 return;
214                         }
215
216                         if (times && times->Contains("end") && now > service->GetLastHardStateChange() + times->Get("end")) {
217                                 Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': after escalation range");
218                                 return;
219                         }
220                 }
221
222                 unsigned long ftype = 1 << type;
223
224                 Log(LogDebug, "icinga", "FType=" + Convert::ToString(ftype) + ", TypeFilter=" + Convert::ToString(GetNotificationTypeFilter()));
225
226                 if (!(ftype & GetNotificationTypeFilter())) {
227                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': type filter does not match");
228                         return;
229                 }
230
231                 unsigned long fstate = 1 << GetService()->GetState();
232
233                 if (!(fstate & GetNotificationStateFilter())) {
234                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': state filter does not match");
235                         return;
236                 }
237         }
238
239         {
240                 ObjectLock olock(this);
241
242                 double now = Utility::GetTime();
243                 SetLastNotification(now);
244
245                 if (type == NotificationProblem)
246                         SetLastProblemNotification(now);
247         }
248
249         std::set<User::Ptr> allUsers;
250
251         std::set<User::Ptr> users = GetUsers();
252         std::copy(users.begin(), users.end(), std::inserter(allUsers, allUsers.begin()));
253
254         BOOST_FOREACH(const UserGroup::Ptr& ug, GetUserGroups()) {
255                 std::set<User::Ptr> members = ug->GetMembers();
256                 std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin()));
257         }
258
259         Service::OnNotificationSendStart(GetSelf(), GetService(), allUsers, type, cr, author, text);
260
261         std::set<User::Ptr> allNotifiedUsers;
262         BOOST_FOREACH(const User::Ptr& user, allUsers) {
263                 if (!CheckNotificationUserFilters(type, user, force))
264                         continue;
265
266                 Log(LogDebug, "icinga", "Sending notification for user '" + user->GetName() + "'");
267                 Utility::QueueAsyncCallback(boost::bind(&Notification::ExecuteNotificationHelper, this, type, user, cr, force, author, text));
268
269                 /* collect all notified users */
270                 allNotifiedUsers.insert(user);
271         }
272
273         /* used in db_ido for notification history */
274         Service::OnNotificationSentToAllUsers(GetSelf(), GetService(), allNotifiedUsers, type, cr, author, text);
275 }
276
277 bool Notification::CheckNotificationUserFilters(NotificationType type, const User::Ptr& user, bool force)
278 {
279         ASSERT(!OwnsLock());
280
281         if (!force) {
282                 TimePeriod::Ptr tp = user->GetNotificationPeriod();
283
284                 if (tp && !tp->IsInside(Utility::GetTime())) {
285                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
286                             GetName() + " and user '" + user->GetName() + "': user not in timeperiod");
287                         return false;
288                 }
289
290                 unsigned long ftype = 1 << type;
291
292                 if (!(ftype & user->GetNotificationTypeFilter())) {
293                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
294                             GetName() + " and user '" + user->GetName() + "': type filter does not match");
295                         return false;
296                 }
297
298                 unsigned long fstate = 1 << GetService()->GetState();
299
300                 if (!(fstate & user->GetNotificationStateFilter())) {
301                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
302                             GetName() + " and user '" + user->GetName() + "': state filter does not match");
303                         return false;
304                 }
305         }
306
307         return true;
308 }
309
310 void Notification::ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const CheckResult::Ptr& cr, bool force, const String& author, const String& text)
311 {
312         ASSERT(!OwnsLock());
313
314         try {
315                 NotificationCommand::Ptr command = GetNotificationCommand();
316
317                 if (!command) {
318                         Log(LogDebug, "icinga", "No notification_command found for notification '" + GetName() + "'. Skipping execution.");
319                         return;
320                 }
321
322                 command->Execute(GetSelf(), user, cr, type, author, text);
323
324                 {
325                         ObjectLock olock(this);
326                         UpdateNotificationNumber();
327                         SetLastNotification(Utility::GetTime());
328                 }
329
330                 /* required by compatlogger */
331                 Service::OnNotificationSentToUser(GetSelf(), GetService(), user, type, cr, author, text, command->GetName());
332
333                 Log(LogInformation, "icinga", "Completed sending notification for service '" + GetService()->GetName() + "'");
334         } catch (const std::exception& ex) {
335                 std::ostringstream msgbuf;
336                 msgbuf << "Exception occured during notification for service '"
337                        << GetService()->GetName() << "': " << DiagnosticInformation(ex);
338                 Log(LogWarning, "icinga", msgbuf.str());
339         }
340 }
341
342 bool Notification::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const
343 {
344         Dictionary::Ptr vars = GetVars();
345
346         if (macro.SubStr(0, 13) == "_NOTIFICATION") {
347                 *result = vars ? vars->Get(macro.SubStr(13)) : "";
348                 return true;
349         }
350
351
352         if (vars && vars->Contains(macro)) {
353                 *result = vars->Get(macro);
354                 return true;
355         }
356
357         return false;
358 }