]> granicus.if.org Git - icinga2/blob - lib/icinga/notification.cpp
Fix spelling errors.
[icinga2] / lib / icinga / notification.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "icinga/notification.hpp"
4 #include "icinga/notification-ti.cpp"
5 #include "icinga/notificationcommand.hpp"
6 #include "icinga/service.hpp"
7 #include "remote/apilistener.hpp"
8 #include "base/objectlock.hpp"
9 #include "base/logger.hpp"
10 #include "base/utility.hpp"
11 #include "base/convert.hpp"
12 #include "base/exception.hpp"
13 #include "base/initialize.hpp"
14 #include "base/scriptglobal.hpp"
15 #include <algorithm>
16
17 using namespace icinga;
18
19 REGISTER_TYPE(Notification);
20 INITIALIZE_ONCE(&Notification::StaticInitialize);
21
22 std::map<String, int> Notification::m_StateFilterMap;
23 std::map<String, int> Notification::m_TypeFilterMap;
24
25 boost::signals2::signal<void (const Notification::Ptr&, const MessageOrigin::Ptr&)> Notification::OnNextNotificationChanged;
26 boost::signals2::signal<void (const Notification::Ptr&, const NotificationResult::Ptr&, const MessageOrigin::Ptr&)> Notification::OnNewNotificationResult;
27
28 String NotificationNameComposer::MakeName(const String& shortName, const Object::Ptr& context) const
29 {
30         Notification::Ptr notification = dynamic_pointer_cast<Notification>(context);
31
32         if (!notification)
33                 return "";
34
35         String name = notification->GetHostName();
36
37         if (!notification->GetServiceName().IsEmpty())
38                 name += "!" + notification->GetServiceName();
39
40         name += "!" + shortName;
41
42         return name;
43 }
44
45 Dictionary::Ptr NotificationNameComposer::ParseName(const String& name) const
46 {
47         std::vector<String> tokens = name.Split("!");
48
49         if (tokens.size() < 2)
50                 BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid Notification name."));
51
52         Dictionary::Ptr result = new Dictionary();
53         result->Set("host_name", tokens[0]);
54
55         if (tokens.size() > 2) {
56                 result->Set("service_name", tokens[1]);
57                 result->Set("name", tokens[2]);
58         } else {
59                 result->Set("name", tokens[1]);
60         }
61
62         return result;
63 }
64
65 void Notification::StaticInitialize()
66 {
67         ScriptGlobal::Set("Icinga.OK", "OK", true);
68         ScriptGlobal::Set("Icinga.Warning", "Warning", true);
69         ScriptGlobal::Set("Icinga.Critical", "Critical", true);
70         ScriptGlobal::Set("Icinga.Unknown", "Unknown", true);
71         ScriptGlobal::Set("Icinga.Up", "Up", true);
72         ScriptGlobal::Set("Icinga.Down", "Down", true);
73
74         ScriptGlobal::Set("Icinga.DowntimeStart", "DowntimeStart", true);
75         ScriptGlobal::Set("Icinga.DowntimeEnd", "DowntimeEnd", true);
76         ScriptGlobal::Set("Icinga.DowntimeRemoved", "DowntimeRemoved", true);
77         ScriptGlobal::Set("Icinga.Custom", "Custom", true);
78         ScriptGlobal::Set("Icinga.Acknowledgement", "Acknowledgement", true);
79         ScriptGlobal::Set("Icinga.Problem", "Problem", true);
80         ScriptGlobal::Set("Icinga.Recovery", "Recovery", true);
81         ScriptGlobal::Set("Icinga.FlappingStart", "FlappingStart", true);
82         ScriptGlobal::Set("Icinga.FlappingEnd", "FlappingEnd", true);
83
84         m_StateFilterMap["OK"] = StateFilterOK;
85         m_StateFilterMap["Warning"] = StateFilterWarning;
86         m_StateFilterMap["Critical"] = StateFilterCritical;
87         m_StateFilterMap["Unknown"] = StateFilterUnknown;
88         m_StateFilterMap["Up"] = StateFilterUp;
89         m_StateFilterMap["Down"] = StateFilterDown;
90
91         m_TypeFilterMap["DowntimeStart"] = NotificationDowntimeStart;
92         m_TypeFilterMap["DowntimeEnd"] = NotificationDowntimeEnd;
93         m_TypeFilterMap["DowntimeRemoved"] = NotificationDowntimeRemoved;
94         m_TypeFilterMap["Custom"] = NotificationCustom;
95         m_TypeFilterMap["Acknowledgement"] = NotificationAcknowledgement;
96         m_TypeFilterMap["Problem"] = NotificationProblem;
97         m_TypeFilterMap["Recovery"] = NotificationRecovery;
98         m_TypeFilterMap["FlappingStart"] = NotificationFlappingStart;
99         m_TypeFilterMap["FlappingEnd"] = NotificationFlappingEnd;
100 }
101
102 void Notification::OnConfigLoaded()
103 {
104         ObjectImpl<Notification>::OnConfigLoaded();
105
106         SetTypeFilter(FilterArrayToInt(GetTypes(), GetTypeFilterMap(), ~0));
107         SetStateFilter(FilterArrayToInt(GetStates(), GetStateFilterMap(), ~0));
108 }
109
110 void Notification::OnAllConfigLoaded()
111 {
112         ObjectImpl<Notification>::OnAllConfigLoaded();
113
114         Host::Ptr host = Host::GetByName(GetHostName());
115
116         if (GetServiceName().IsEmpty())
117                 m_Checkable = host;
118         else
119                 m_Checkable = host->GetServiceByShortName(GetServiceName());
120
121         if (!m_Checkable)
122                 BOOST_THROW_EXCEPTION(ScriptError("Notification object refers to a host/service which doesn't exist.", GetDebugInfo()));
123
124         GetCheckable()->RegisterNotification(this);
125 }
126
127 void Notification::Start(bool runtimeCreated)
128 {
129         Checkable::Ptr obj = GetCheckable();
130
131         if (obj)
132                 obj->RegisterNotification(this);
133
134         if (ApiListener::IsHACluster() && GetNextNotification() < Utility::GetTime() + 60)
135                 SetNextNotification(Utility::GetTime() + 60, true);
136
137         ObjectImpl<Notification>::Start(runtimeCreated);
138 }
139
140 void Notification::Stop(bool runtimeRemoved)
141 {
142         ObjectImpl<Notification>::Stop(runtimeRemoved);
143
144         Checkable::Ptr obj = GetCheckable();
145
146         if (obj)
147                 obj->UnregisterNotification(this);
148 }
149
150 Checkable::Ptr Notification::GetCheckable() const
151 {
152         return static_pointer_cast<Checkable>(m_Checkable);
153 }
154
155 NotificationCommand::Ptr Notification::GetCommand() const
156 {
157         return NotificationCommand::GetByName(GetCommandRaw());
158 }
159
160 std::set<User::Ptr> Notification::GetUsers() const
161 {
162         std::set<User::Ptr> result;
163
164         Array::Ptr users = GetUsersRaw();
165
166         if (users) {
167                 ObjectLock olock(users);
168
169                 for (const String& name : users) {
170                         User::Ptr user = User::GetByName(name);
171
172                         if (!user)
173                                 continue;
174
175                         result.insert(user);
176                 }
177         }
178
179         return result;
180 }
181
182 std::set<UserGroup::Ptr> Notification::GetUserGroups() const
183 {
184         std::set<UserGroup::Ptr> result;
185
186         Array::Ptr groups = GetUserGroupsRaw();
187
188         if (groups) {
189                 ObjectLock olock(groups);
190
191                 for (const String& name : groups) {
192                         UserGroup::Ptr ug = UserGroup::GetByName(name);
193
194                         if (!ug)
195                                 continue;
196
197                         result.insert(ug);
198                 }
199         }
200
201         return result;
202 }
203
204 TimePeriod::Ptr Notification::GetPeriod() const
205 {
206         return TimePeriod::GetByName(GetPeriodRaw());
207 }
208
209 void Notification::UpdateNotificationNumber()
210 {
211         SetNotificationNumber(GetNotificationNumber() + 1);
212 }
213
214 void Notification::ResetNotificationNumber()
215 {
216         SetNotificationNumber(0);
217 }
218
219 void Notification::BeginExecuteNotification(NotificationType type, const CheckResult::Ptr& cr, bool force, bool reminder, const String& author, const String& text)
220 {
221         String notificationName = GetName();
222         String notificationTypeName = NotificationTypeToString(type);
223
224         Log(LogNotice, "Notification")
225                 << "Attempting to send " << (reminder ? "reminder " : "")
226                 << "notifications of type '" << notificationTypeName
227                 << "' for notification object '" << notificationName << "'.";
228
229         Checkable::Ptr checkable = GetCheckable();
230
231         if (!force) {
232                 TimePeriod::Ptr tp = GetPeriod();
233
234                 if (tp && !tp->IsInside(Utility::GetTime())) {
235                         Log(LogNotice, "Notification")
236                                 << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '" << notificationName
237                                 << "': not in timeperiod '" << tp->GetName() << "'";
238                         return;
239                 }
240
241                 double now = Utility::GetTime();
242                 Dictionary::Ptr times = GetTimes();
243
244                 if (times && type == NotificationProblem) {
245                         Value timesBegin = times->Get("begin");
246                         Value timesEnd = times->Get("end");
247
248                         if (timesBegin != Empty && timesBegin >= 0 && now < checkable->GetLastHardStateChange() + timesBegin) {
249                                 Log(LogNotice, "Notification")
250                                         << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '"
251                                         << notificationName << "': before specified begin time (" << Utility::FormatDuration(timesBegin) << ")";
252
253                                 /* we need to adjust the next notification time
254                                  * delaying the first notification
255                                  */
256                                 SetNextNotification(checkable->GetLastHardStateChange() + timesBegin + 1.0);
257
258                                 return;
259                         }
260
261                         if (timesEnd != Empty && timesEnd >= 0 && now > checkable->GetLastHardStateChange() + timesEnd) {
262                                 Log(LogNotice, "Notification")
263                                         << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '"
264                                         << notificationName << "': after specified end time (" << Utility::FormatDuration(timesEnd) << ")";
265                                 return;
266                         }
267                 }
268
269                 unsigned long ftype = type;
270
271                 Log(LogDebug, "Notification")
272                         << "Type '" << NotificationTypeToString(type)
273                         << "', TypeFilter: " << NotificationFilterToString(GetTypeFilter(), GetTypeFilterMap())
274                         << " (FType=" << ftype << ", TypeFilter=" << GetTypeFilter() << ")";
275
276                 if (!(ftype & GetTypeFilter())) {
277                         Log(LogNotice, "Notification")
278                                 << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '"
279                                 << notificationName << "': type '"
280                                 << NotificationTypeToString(type) << "' does not match type filter: "
281                                 << NotificationFilterToString(GetTypeFilter(), GetTypeFilterMap()) << ".";
282
283                         /* Ensure to reset no_more_notifications on Recovery notifications,
284                          * even if the admin did not configure them in the filter.
285                          */
286                         {
287                                 ObjectLock olock(this);
288                                 if (type == NotificationRecovery && GetInterval() <= 0)
289                                         SetNoMoreNotifications(false);
290                         }
291
292                         return;
293                 }
294
295                 /* Check state filters for problem notifications. Recovery notifications will be filtered away later. */
296                 if (type == NotificationProblem) {
297                         Host::Ptr host;
298                         Service::Ptr service;
299                         tie(host, service) = GetHostService(checkable);
300
301                         unsigned long fstate;
302                         String stateStr;
303
304                         if (service) {
305                                 fstate = ServiceStateToFilter(service->GetState());
306                                 stateStr = NotificationServiceStateToString(service->GetState());
307                         } else {
308                                 fstate = HostStateToFilter(host->GetState());
309                                 stateStr = NotificationHostStateToString(host->GetState());
310                         }
311
312                         Log(LogDebug, "Notification")
313                                 << "State '" << stateStr << "', StateFilter: " << NotificationFilterToString(GetStateFilter(), GetStateFilterMap())
314                                 << " (FState=" << fstate << ", StateFilter=" << GetStateFilter() << ")";
315
316                         if (!(fstate & GetStateFilter())) {
317                                 Log(LogNotice, "Notification")
318                                         << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '"
319                                         << notificationName << "': state '" << stateStr
320                                         << "' does not match state filter: " << NotificationFilterToString(GetStateFilter(), GetStateFilterMap()) << ".";
321                                 return;
322                         }
323                 }
324         } else {
325                 Log(LogNotice, "Notification")
326                         << "Not checking " << (reminder ? "reminder " : "") << "notification filters for notification object '"
327                         << notificationName << "': Notification was forced.";
328         }
329
330         {
331                 ObjectLock olock(this);
332
333                 UpdateNotificationNumber();
334                 double now = Utility::GetTime();
335                 SetLastNotification(now);
336
337                 if (type == NotificationProblem && GetInterval() <= 0)
338                         SetNoMoreNotifications(true);
339                 else
340                         SetNoMoreNotifications(false);
341
342                 if (type == NotificationProblem && GetInterval() > 0)
343                         SetNextNotification(now + GetInterval());
344
345                 if (type == NotificationProblem)
346                         SetLastProblemNotification(now);
347         }
348
349         std::set<User::Ptr> allUsers;
350
351         std::set<User::Ptr> users = GetUsers();
352         std::copy(users.begin(), users.end(), std::inserter(allUsers, allUsers.begin()));
353
354         for (const UserGroup::Ptr& ug : GetUserGroups()) {
355                 std::set<User::Ptr> members = ug->GetMembers();
356                 std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin()));
357         }
358
359         std::set<User::Ptr> allNotifiedUsers;
360         Array::Ptr notifiedProblemUsers = GetNotifiedProblemUsers();
361
362         for (const User::Ptr& user : allUsers) {
363                 String userName = user->GetName();
364
365                 if (!user->GetEnableNotifications()) {
366                         Log(LogNotice, "Notification")
367                                 << "Notification object '" << notificationName << "': Disabled notifications for user '"
368                                 << userName << "'. Not sending notification.";
369                         continue;
370                 }
371
372                 if (!CheckNotificationUserFilters(type, user, force, reminder)) {
373                         Log(LogNotice, "Notification")
374                                 << "Notification object '" << notificationName << "': Filters for user '" << userName << "' not matched. Not sending notification.";
375                         continue;
376                 }
377
378                 /* on recovery, check if user was notified before */
379                 if (type == NotificationRecovery) {
380                         if (!notifiedProblemUsers->Contains(userName) && (NotificationProblem & user->GetTypeFilter())) {
381                                 Log(LogNotice, "Notification")
382                                         << "Notification object '" << notificationName << "': We did not notify user '" << userName
383                                         << "' (Problem types enabled) for a problem before. Not sending Recovery notification.";
384                                 continue;
385                         }
386                 }
387
388                 /* on acknowledgement, check if user was notified before */
389                 if (type == NotificationAcknowledgement) {
390                         if (!notifiedProblemUsers->Contains(userName) && (NotificationProblem & user->GetTypeFilter())) {
391                                 Log(LogNotice, "Notification")
392                                         << "Notification object '" << notificationName << "': We did not notify user '" << userName
393                                         << "' (Problem types enabled) for a problem before. Not sending acknowledgement notification.";
394                                 continue;
395                         }
396                 }
397
398                 Log(LogInformation, "Notification")
399                         << "Sending " << (reminder ? "reminder " : "") << "'" << NotificationTypeToString(type) << "' notification '"
400                         << notificationName << "' for user '" << userName << "'";
401
402                 Utility::QueueAsyncCallback(std::bind(&Notification::ExecuteNotificationHelper, this, type, user, cr, force, author, text));
403
404                 /* collect all notified users */
405                 allNotifiedUsers.insert(user);
406
407                 /* store all notified users for later recovery checks */
408                 if (type == NotificationProblem && !notifiedProblemUsers->Contains(userName))
409                         notifiedProblemUsers->Add(userName);
410         }
411
412         /* if this was a recovery notification, reset all notified users */
413         if (type == NotificationRecovery)
414                 notifiedProblemUsers->Clear();
415
416         /* used in db_ido for notification history */
417         Service::OnNotificationSentToAllUsers(this, checkable, allNotifiedUsers, type, cr, author, text, nullptr);
418 }
419
420 bool Notification::CheckNotificationUserFilters(NotificationType type, const User::Ptr& user, bool force, bool reminder)
421 {
422         String notificationName = GetName();
423         String userName = user->GetName();
424
425         if (!force) {
426                 TimePeriod::Ptr tp = user->GetPeriod();
427
428                 if (tp && !tp->IsInside(Utility::GetTime())) {
429                         Log(LogNotice, "Notification")
430                                 << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '"
431                                 << notificationName << " and user '" << userName
432                                 << "': user period not in timeperiod '" << tp->GetName() << "'";
433                         return false;
434                 }
435
436                 unsigned long ftype = type;
437
438                 Log(LogDebug, "Notification")
439                         << "User '" << userName << "' notification '" << notificationName
440                         << "', Type '" << NotificationTypeToString(type)
441                         << "', TypeFilter: " << NotificationFilterToString(user->GetTypeFilter(), GetTypeFilterMap())
442                         << " (FType=" << ftype << ", TypeFilter=" << GetTypeFilter() << ")";
443
444
445                 if (!(ftype & user->GetTypeFilter())) {
446                         Log(LogNotice, "Notification")
447                                 << "Not sending " << (reminder ? "reminder " : "") << "notifications for notification object '"
448                                 << notificationName << " and user '" << userName << "': type '"
449                                 << NotificationTypeToString(type) << "' does not match type filter: "
450                                 << NotificationFilterToString(user->GetTypeFilter(), GetTypeFilterMap()) << ".";
451                         return false;
452                 }
453
454                 /* check state filters it this is not a recovery notification */
455                 if (type != NotificationRecovery) {
456                         Checkable::Ptr checkable = GetCheckable();
457                         Host::Ptr host;
458                         Service::Ptr service;
459                         tie(host, service) = GetHostService(checkable);
460
461                         unsigned long fstate;
462                         String stateStr;
463
464                         if (service) {
465                                 fstate = ServiceStateToFilter(service->GetState());
466                                 stateStr = NotificationServiceStateToString(service->GetState());
467                         } else {
468                                 fstate = HostStateToFilter(host->GetState());
469                                 stateStr = NotificationHostStateToString(host->GetState());
470                         }
471
472                         Log(LogDebug, "Notification")
473                                 << "User '" << userName << "' notification '" << notificationName
474                                 << "', State '" << stateStr << "', StateFilter: "
475                                 << NotificationFilterToString(user->GetStateFilter(), GetStateFilterMap())
476                                 << " (FState=" << fstate << ", StateFilter=" << user->GetStateFilter() << ")";
477
478                         if (!(fstate & user->GetStateFilter())) {
479                                 Log(LogNotice, "Notification")
480                                         << "Not " << (reminder ? "reminder " : "") << "sending notifications for notification object '"
481                                         << notificationName << " and user '" << userName << "': state '" << stateStr
482                                         << "' does not match state filter: " << NotificationFilterToString(user->GetStateFilter(), GetStateFilterMap()) << ".";
483                                 return false;
484                         }
485                 }
486         } else {
487                 Log(LogNotice, "Notification")
488                         << "Not checking " << (reminder ? "reminder " : "") << "notification filters for notification object '"
489                         << notificationName << "' and user '" << userName << "': Notification was forced.";
490         }
491
492         return true;
493 }
494
495 void Notification::ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const CheckResult::Ptr& cr, bool force, const String& author, const String& text)
496 {
497         String notificationName = GetName();
498         String userName = user->GetName();
499         String checkableName = GetCheckable()->GetName();
500
501         NotificationCommand::Ptr command = GetCommand();
502
503         if (!command) {
504                 Log(LogDebug, "Notification")
505                         << "No command found for notification '" << notificationName << "'. Skipping execution.";
506                 return;
507         }
508
509         String commandName = command->GetName();
510
511         try {
512                 NotificationResult::Ptr nr = new NotificationResult();
513
514                 nr->SetExecutionStart(Utility::GetTime());
515
516                 command->Execute(this, user, cr, nr, type, author, text);
517
518                 /* required by compatlogger */
519                 Checkable::OnNotificationSentToUser(this, GetCheckable(), user, type, cr, nr, author, text, command->GetName(), nullptr);
520
521                 Log(LogInformation, "Notification")
522                         << "Completed sending '" << NotificationTypeToString(type)
523                         << "' notification '" << notificationName
524                         << "' for checkable '" << checkableName
525                         << "' and user '" << userName << "' using command '" << commandName << "'.";
526         } catch (const std::exception& ex) {
527                 Log(LogWarning, "Notification")
528                         << "Exception occurred during notification '" << notificationName
529                         << "' for checkable '" << checkableName
530                         << "' and user '" << userName << "' using command '" << commandName << "': "
531                         << DiagnosticInformation(ex, false);
532         }
533 }
534
535 void Notification::ProcessNotificationResult(const NotificationResult::Ptr& nr, const MessageOrigin::Ptr& origin)
536 {
537         if (!nr)
538                 return;
539
540         double now = Utility::GetTime();
541
542         if (nr->GetExecutionStart() == 0)
543                 nr->SetExecutionStart(now);
544
545         if (nr->GetExecutionEnd() == 0)
546                 nr->SetExecutionEnd(now);
547
548         /* Determine the execution endpoint from a locally executed check. */
549         if (!origin || origin->IsLocal())
550                 nr->SetExecutionEndpoint(IcingaApplication::GetInstance()->GetNodeName());
551
552         if (!IsActive())
553                 return;
554
555         {
556                 ObjectLock olock(this);
557
558                 SetLastNotificationResult(nr);
559         }
560
561         /* Notify cluster, API and feature events. */
562         OnNewNotificationResult(this, nr, origin);
563 }
564
565 int icinga::ServiceStateToFilter(ServiceState state)
566 {
567         switch (state) {
568                 case ServiceOK:
569                         return StateFilterOK;
570                 case ServiceWarning:
571                         return StateFilterWarning;
572                 case ServiceCritical:
573                         return StateFilterCritical;
574                 case ServiceUnknown:
575                         return StateFilterUnknown;
576                 default:
577                         VERIFY(!"Invalid state type.");
578         }
579 }
580
581 int icinga::HostStateToFilter(HostState state)
582 {
583         switch (state) {
584                 case HostUp:
585                         return StateFilterUp;
586                 case HostDown:
587                         return StateFilterDown;
588                 default:
589                         VERIFY(!"Invalid state type.");
590         }
591 }
592
593 String Notification::NotificationFilterToString(int filter, const std::map<String, int>& filterMap)
594 {
595         std::vector<String> sFilters;
596
597         typedef std::pair<String, int> kv_pair;
598         for (const kv_pair& kv : filterMap) {
599                 if (filter & kv.second)
600                         sFilters.push_back(kv.first);
601         }
602
603         return Utility::NaturalJoin(sFilters);
604 }
605
606 /*
607  * Main interface to translate NotificationType values into strings.
608  */
609 String Notification::NotificationTypeToString(NotificationType type)
610 {
611         auto typeMap = Notification::m_TypeFilterMap;
612
613         auto it = std::find_if(typeMap.begin(), typeMap.end(),
614                 [&type](const std::pair<String, int>& p) {
615                         return p.second == type;
616         });
617
618         if (it == typeMap.end())
619                 return Empty;
620
621         return it->first;
622 }
623
624
625 /*
626  * Compat interface used in external features.
627  */
628 String Notification::NotificationTypeToStringCompat(NotificationType type)
629 {
630         switch (type) {
631                 case NotificationDowntimeStart:
632                         return "DOWNTIMESTART";
633                 case NotificationDowntimeEnd:
634                         return "DOWNTIMEEND";
635                 case NotificationDowntimeRemoved:
636                         return "DOWNTIMECANCELLED";
637                 case NotificationCustom:
638                         return "CUSTOM";
639                 case NotificationAcknowledgement:
640                         return "ACKNOWLEDGEMENT";
641                 case NotificationProblem:
642                         return "PROBLEM";
643                 case NotificationRecovery:
644                         return "RECOVERY";
645                 case NotificationFlappingStart:
646                         return "FLAPPINGSTART";
647                 case NotificationFlappingEnd:
648                         return "FLAPPINGEND";
649                 default:
650                         return "UNKNOWN_NOTIFICATION";
651         }
652 }
653
654 String Notification::NotificationServiceStateToString(ServiceState state)
655 {
656         switch (state) {
657                 case ServiceOK:
658                         return "OK";
659                 case ServiceWarning:
660                         return "Warning";
661                 case ServiceCritical:
662                         return "Critical";
663                 case ServiceUnknown:
664                         return "Unknown";
665                 default:
666                         VERIFY(!"Invalid state type.");
667         }
668 }
669
670 String Notification::NotificationHostStateToString(HostState state)
671 {
672         switch (state) {
673                 case HostUp:
674                         return "Up";
675                 case HostDown:
676                         return "Down";
677                 default:
678                         VERIFY(!"Invalid state type.");
679         }
680 }
681
682 void Notification::Validate(int types, const ValidationUtils& utils)
683 {
684         ObjectImpl<Notification>::Validate(types, utils);
685
686         if (!(types & FAConfig))
687                 return;
688
689         Array::Ptr users = GetUsersRaw();
690         Array::Ptr groups = GetUserGroupsRaw();
691
692         if ((!users || users->GetLength() == 0) && (!groups || groups->GetLength() == 0))
693                 BOOST_THROW_EXCEPTION(ValidationError(this, std::vector<String>(), "Validation failed: No users/user_groups specified."));
694 }
695
696 void Notification::ValidateStates(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
697 {
698         ObjectImpl<Notification>::ValidateStates(lvalue, utils);
699
700         int filter = FilterArrayToInt(lvalue(), GetStateFilterMap(), 0);
701
702         if (GetServiceName().IsEmpty() && (filter == -1 || (filter & ~(StateFilterUp | StateFilterDown)) != 0))
703                 BOOST_THROW_EXCEPTION(ValidationError(this, { "states" }, "State filter is invalid."));
704
705         if (!GetServiceName().IsEmpty() && (filter == -1 || (filter & ~(StateFilterOK | StateFilterWarning | StateFilterCritical | StateFilterUnknown)) != 0))
706                 BOOST_THROW_EXCEPTION(ValidationError(this, { "states" }, "State filter is invalid."));
707 }
708
709 void Notification::ValidateTypes(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
710 {
711         ObjectImpl<Notification>::ValidateTypes(lvalue, utils);
712
713         int filter = FilterArrayToInt(lvalue(), GetTypeFilterMap(), 0);
714
715         if (filter == -1 || (filter & ~(NotificationDowntimeStart | NotificationDowntimeEnd | NotificationDowntimeRemoved |
716                 NotificationCustom | NotificationAcknowledgement | NotificationProblem | NotificationRecovery |
717                 NotificationFlappingStart | NotificationFlappingEnd)) != 0)
718                 BOOST_THROW_EXCEPTION(ValidationError(this, { "types" }, "Type filter is invalid."));
719 }
720
721 void Notification::ValidateTimes(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
722 {
723         ObjectImpl<Notification>::ValidateTimes(lvalue, utils);
724
725         Dictionary::Ptr times = lvalue();
726
727         if (!times)
728                 return;
729
730         double begin;
731         double end;
732
733         try {
734                 begin = Convert::ToDouble(times->Get("begin"));
735         } catch (const std::exception&) {
736                 BOOST_THROW_EXCEPTION(ValidationError(this, { "times" }, "'begin' is invalid, must be duration or number." ));
737         }
738
739         try {
740                 end = Convert::ToDouble(times->Get("end"));
741         } catch (const std::exception&) {
742                 BOOST_THROW_EXCEPTION(ValidationError(this, { "times" }, "'end' is invalid, must be duration or number." ));
743         }
744
745         /* Also solve logical errors where begin > end. */
746         if (begin > 0 && end > 0 && begin > end)
747                 BOOST_THROW_EXCEPTION(ValidationError(this, { "times" }, "'begin' must be smaller than 'end'."));
748 }
749
750 Endpoint::Ptr Notification::GetCommandEndpoint() const
751 {
752         return Endpoint::GetByName(GetCommandEndpointRaw());
753 }
754
755 const std::map<String, int>& Notification::GetStateFilterMap()
756 {
757         return m_StateFilterMap;
758 }
759
760 const std::map<String, int>& Notification::GetTypeFilterMap()
761 {
762         return m_TypeFilterMap;
763 }