1 /******************************************************************************
3 * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
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. *
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. *
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 ******************************************************************************/
20 #include "icinga/notification.hpp"
21 #include "icinga/notificationcommand.hpp"
22 #include "icinga/service.hpp"
23 #include "config/configcompilercontext.hpp"
24 #include "base/objectlock.hpp"
25 #include "base/logger.hpp"
26 #include "base/utility.hpp"
27 #include "base/convert.hpp"
28 #include "base/exception.hpp"
29 #include "base/initialize.hpp"
30 #include "base/scriptvariable.hpp"
31 #include "base/scriptfunction.hpp"
32 #include <boost/foreach.hpp>
34 using namespace icinga;
36 REGISTER_TYPE(Notification);
37 REGISTER_SCRIPTFUNCTION(ValidateNotificationFilters, &Notification::ValidateFilters);
38 INITIALIZE_ONCE(&Notification::StaticInitialize);
40 boost::signals2::signal<void (const Notification::Ptr&, double, const MessageOrigin&)> Notification::OnNextNotificationChanged;
42 String NotificationNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const
44 Notification::Ptr notification = dynamic_pointer_cast<Notification>(context);
49 String name = notification->GetHostName();
51 if (!notification->GetServiceName().IsEmpty())
52 name += "!" + notification->GetServiceName();
54 name += "!" + shortName;
59 void Notification::StaticInitialize(void)
61 ScriptVariable::Set("OK", StateFilterOK, true, true);
62 ScriptVariable::Set("Warning", StateFilterWarning, true, true);
63 ScriptVariable::Set("Critical", StateFilterCritical, true, true);
64 ScriptVariable::Set("Unknown", StateFilterUnknown, true, true);
65 ScriptVariable::Set("Up", StateFilterUp, true, true);
66 ScriptVariable::Set("Down", StateFilterDown, true, true);
68 ScriptVariable::Set("DowntimeStart", 1 << NotificationDowntimeStart, true, true);
69 ScriptVariable::Set("DowntimeEnd", 1 << NotificationDowntimeEnd, true, true);
70 ScriptVariable::Set("DowntimeRemoved", 1 << NotificationDowntimeRemoved, true, true);
71 ScriptVariable::Set("Custom", 1 << NotificationCustom, true, true);
72 ScriptVariable::Set("Acknowledgement", 1 << NotificationAcknowledgement, true, true);
73 ScriptVariable::Set("Problem", 1 << NotificationProblem, true, true);
74 ScriptVariable::Set("Recovery", 1 << NotificationRecovery, true, true);
75 ScriptVariable::Set("FlappingStart", 1 << NotificationFlappingStart, true, true);
76 ScriptVariable::Set("FlappingEnd", 1 << NotificationFlappingEnd, true, true);
79 void Notification::OnConfigLoaded(void)
81 SetTypeFilter(FilterArrayToInt(GetTypes(), ~0));
82 SetStateFilter(FilterArrayToInt(GetStates(), ~0));
84 Checkable::Ptr obj = GetCheckable();
87 obj->AddNotification(this);
90 void Notification::Start(void)
92 DynamicObject::Start();
94 Checkable::Ptr obj = GetCheckable();
97 obj->AddNotification(this);
100 void Notification::Stop(void)
102 DynamicObject::Stop();
104 Checkable::Ptr obj = GetCheckable();
107 obj->RemoveNotification(this);
110 Checkable::Ptr Notification::GetCheckable(void) const
112 Host::Ptr host = Host::GetByName(GetHostName());
114 if (GetServiceName().IsEmpty())
117 return host->GetServiceByShortName(GetServiceName());
120 NotificationCommand::Ptr Notification::GetCommand(void) const
122 return NotificationCommand::GetByName(GetCommandRaw());
125 std::set<User::Ptr> Notification::GetUsers(void) const
127 std::set<User::Ptr> result;
129 Array::Ptr users = GetUsersRaw();
132 ObjectLock olock(users);
134 BOOST_FOREACH(const String& name, users) {
135 User::Ptr user = User::GetByName(name);
147 std::set<UserGroup::Ptr> Notification::GetUserGroups(void) const
149 std::set<UserGroup::Ptr> result;
151 Array::Ptr groups = GetUserGroupsRaw();
154 ObjectLock olock(groups);
156 BOOST_FOREACH(const String& name, groups) {
157 UserGroup::Ptr ug = UserGroup::GetByName(name);
169 TimePeriod::Ptr Notification::GetPeriod(void) const
171 return TimePeriod::GetByName(GetPeriodRaw());
174 double Notification::GetNextNotification(void) const
176 return GetNextNotificationRaw();
180 * Sets the timestamp when the next periodical notification should be sent.
181 * This does not affect notifications that are sent for state changes.
183 void Notification::SetNextNotification(double time, const MessageOrigin& origin)
185 SetNextNotificationRaw(time);
187 OnNextNotificationChanged(this, time, origin);
190 void Notification::UpdateNotificationNumber(void)
192 SetNotificationNumber(GetNotificationNumber() + 1);
195 void Notification::ResetNotificationNumber(void)
197 SetNotificationNumber(0);
200 String Notification::NotificationTypeToString(NotificationType type)
203 case NotificationDowntimeStart:
204 return "DOWNTIMESTART";
205 case NotificationDowntimeEnd:
206 return "DOWNTIMEEND";
207 case NotificationDowntimeRemoved:
208 return "DOWNTIMECANCELLED";
209 case NotificationCustom:
211 case NotificationAcknowledgement:
212 return "ACKNOWLEDGEMENT";
213 case NotificationProblem:
215 case NotificationRecovery:
217 case NotificationFlappingStart:
218 return "FLAPPINGSTART";
219 case NotificationFlappingEnd:
220 return "FLAPPINGEND";
222 return "UNKNOWN_NOTIFICATION";
226 void Notification::BeginExecuteNotification(NotificationType type, const CheckResult::Ptr& cr, bool force, const String& author, const String& text)
230 Checkable::Ptr checkable = GetCheckable();
233 TimePeriod::Ptr tp = GetPeriod();
235 if (tp && !tp->IsInside(Utility::GetTime())) {
236 Log(LogNotice, "Notification")
237 << "Not sending notifications for notification object '" << GetName() << "': not in timeperiod";
241 double now = Utility::GetTime();
242 Dictionary::Ptr times = GetTimes();
244 if (type == NotificationProblem) {
245 if (times && times->Contains("begin") && now < checkable->GetLastHardStateChange() + times->Get("begin")) {
246 Log(LogNotice, "Notification")
247 << "Not sending notifications for notification object '" << GetName() << "': before escalation range";
251 if (times && times->Contains("end") && now > checkable->GetLastHardStateChange() + times->Get("end")) {
252 Log(LogNotice, "Notification")
253 << "Not sending notifications for notification object '" << GetName() << "': after escalation range";
258 unsigned long ftype = 1 << type;
260 Log(LogDebug, "Notification")
261 << "FType=" << ftype << ", TypeFilter=" << GetTypeFilter();
263 if (!(ftype & GetTypeFilter())) {
264 Log(LogNotice, "Notification")
265 << "Not sending notifications for notification object '" << GetName() << "': type filter does not match";
270 Service::Ptr service;
271 tie(host, service) = GetHostService(checkable);
273 unsigned long fstate;
276 fstate = ServiceStateToFilter(service->GetState());
278 fstate = HostStateToFilter(host->GetState());
280 if (!(fstate & GetStateFilter())) {
281 Log(LogNotice, "Notification")
282 << "Not sending notifications for notification object '" << GetName() << "': state filter does not match";
288 ObjectLock olock(this);
290 double now = Utility::GetTime();
291 SetLastNotification(now);
293 if (type == NotificationProblem)
294 SetLastProblemNotification(now);
297 std::set<User::Ptr> allUsers;
299 std::set<User::Ptr> users = GetUsers();
300 std::copy(users.begin(), users.end(), std::inserter(allUsers, allUsers.begin()));
302 BOOST_FOREACH(const UserGroup::Ptr& ug, GetUserGroups()) {
303 std::set<User::Ptr> members = ug->GetMembers();
304 std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin()));
307 Service::OnNotificationSendStart(this, checkable, allUsers, type, cr, author, text);
309 std::set<User::Ptr> allNotifiedUsers;
310 BOOST_FOREACH(const User::Ptr& user, allUsers) {
311 if (!user->GetEnableNotifications() || !CheckNotificationUserFilters(type, user, force))
314 Log(LogInformation, "Notification")
315 << "Sending notification for user '" << user->GetName() << "'";
317 Utility::QueueAsyncCallback(boost::bind(&Notification::ExecuteNotificationHelper, this, type, user, cr, force, author, text));
319 /* collect all notified users */
320 allNotifiedUsers.insert(user);
323 /* used in db_ido for notification history */
324 Service::OnNotificationSentToAllUsers(this, checkable, allNotifiedUsers, type, cr, author, text);
327 bool Notification::CheckNotificationUserFilters(NotificationType type, const User::Ptr& user, bool force)
332 TimePeriod::Ptr tp = user->GetPeriod();
334 if (tp && !tp->IsInside(Utility::GetTime())) {
335 Log(LogNotice, "Notification")
336 << "Not sending notifications for notification object '"
337 << GetName() << " and user '" << user->GetName() << "': user not in timeperiod";
341 unsigned long ftype = 1 << type;
343 if (!(ftype & user->GetTypeFilter())) {
344 Log(LogNotice, "Notification")
345 << "Not sending notifications for notification object '"
346 << GetName() << " and user '" << user->GetName() << "': type filter does not match";
350 Checkable::Ptr checkable = GetCheckable();
352 Service::Ptr service;
353 tie(host, service) = GetHostService(checkable);
355 unsigned long fstate;
358 fstate = ServiceStateToFilter(service->GetState());
360 fstate = HostStateToFilter(host->GetState());
362 if (!(fstate & user->GetStateFilter())) {
363 Log(LogNotice, "Notification")
364 << "Not sending notifications for notification object '"
365 << GetName() << " and user '" << user->GetName() << "': state filter does not match";
373 void Notification::ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const CheckResult::Ptr& cr, bool force, const String& author, const String& text)
378 NotificationCommand::Ptr command = GetCommand();
381 Log(LogDebug, "Notification")
382 << "No notification_command found for notification '" << GetName() << "'. Skipping execution.";
386 command->Execute(this, user, cr, type, author, text);
389 ObjectLock olock(this);
390 UpdateNotificationNumber();
391 SetLastNotification(Utility::GetTime());
394 /* required by compatlogger */
395 Service::OnNotificationSentToUser(this, GetCheckable(), user, type, cr, author, text, command->GetName());
397 Log(LogInformation, "Notification")
398 << "Completed sending notification for object '" << GetCheckable()->GetName() << "'";
399 } catch (const std::exception& ex) {
400 Log(LogWarning, "Notification")
401 << "Exception occured during notification for object '"
402 << GetCheckable()->GetName() << "': " << DiagnosticInformation(ex);
406 int icinga::ServiceStateToFilter(ServiceState state)
410 return StateFilterOK;
412 return StateFilterWarning;
413 case ServiceCritical:
414 return StateFilterCritical;
416 return StateFilterUnknown;
418 VERIFY(!"Invalid state type.");
422 int icinga::HostStateToFilter(HostState state)
426 return StateFilterUp;
428 return StateFilterDown;
430 VERIFY(!"Invalid state type.");
434 int icinga::FilterArrayToInt(const Array::Ptr& typeFilters, int defaultValue)
436 Value resultTypeFilter;
441 resultTypeFilter = 0;
443 ObjectLock olock(typeFilters);
444 BOOST_FOREACH(const Value& typeFilter, typeFilters) {
445 resultTypeFilter = resultTypeFilter | typeFilter;
448 return resultTypeFilter;
451 void Notification::ValidateFilters(const String& location, const Dictionary::Ptr& attrs)
453 int sfilter = FilterArrayToInt(attrs->Get("states"), 0);
455 if (attrs->Get("service_name") == Empty && (sfilter & ~(StateFilterUp | StateFilterDown)) != 0) {
456 ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
457 location + ": State filter is invalid.");
460 if (attrs->Get("service_name") != Empty && (sfilter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0) {
461 ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
462 location + ": State filter is invalid.");
465 int tfilter = FilterArrayToInt(attrs->Get("types"), 0);
467 if ((tfilter & ~(1 << NotificationDowntimeStart | 1 << NotificationDowntimeEnd | 1 << NotificationDowntimeRemoved |
468 1 << NotificationCustom | 1 << NotificationAcknowledgement | 1 << NotificationProblem | 1 << NotificationRecovery |
469 1 << NotificationFlappingStart | 1 << NotificationFlappingEnd)) != 0) {
470 ConfigCompilerContext::GetInstance()->AddMessage(true, "Validation failed for " +
471 location + ": Type filter is invalid.");