]> granicus.if.org Git - icinga2/blob - lib/icinga/notification.cpp
Implement flapping detection.
[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 <boost/tuple/tuple.hpp>
29 #include <boost/foreach.hpp>
30 #include <boost/exception/diagnostic_information.hpp>
31
32 using namespace icinga;
33
34 REGISTER_TYPE(Notification);
35
36 Notification::Notification(const Dictionary::Ptr& serializedUpdate)
37         : DynamicObject(serializedUpdate)
38 {
39         RegisterAttribute("notification_command", Attribute_Config, &m_NotificationCommand);
40         RegisterAttribute("notification_interval", Attribute_Config, &m_NotificationInterval);
41         RegisterAttribute("notification_period", Attribute_Config, &m_NotificationPeriod);
42         RegisterAttribute("last_notification", Attribute_Replicated, &m_LastNotification);
43         RegisterAttribute("next_notification", Attribute_Replicated, &m_NextNotification);
44         RegisterAttribute("macros", Attribute_Config, &m_Macros);
45         RegisterAttribute("users", Attribute_Config, &m_Users);
46         RegisterAttribute("groups", Attribute_Config, &m_Groups);
47         RegisterAttribute("times", Attribute_Config, &m_Times);
48         RegisterAttribute("host_name", Attribute_Config, &m_HostName);
49         RegisterAttribute("service", Attribute_Config, &m_Service);
50         RegisterAttribute("export_macros", Attribute_Config, &m_ExportMacros);
51 }
52
53 Notification::~Notification(void)
54 {
55         Service::InvalidateNotificationsCache();
56 }
57
58 Notification::Ptr Notification::GetByName(const String& name)
59 {
60         DynamicObject::Ptr configObject = DynamicObject::GetObject("Notification", name);
61
62         return dynamic_pointer_cast<Notification>(configObject);
63 }
64
65 Service::Ptr Notification::GetService(void) const
66 {
67         Host::Ptr host = Host::GetByName(m_HostName);
68
69         if (!host)
70                 return Service::Ptr();
71
72         if (m_Service.IsEmpty())
73                 return host->GetHostCheckService();
74         else
75                 return host->GetServiceByShortName(m_Service);
76 }
77
78 NotificationCommand::Ptr Notification::GetNotificationCommand(void) const
79 {
80         return NotificationCommand::GetByName(m_NotificationCommand);
81 }
82
83 Dictionary::Ptr Notification::GetMacros(void) const
84 {
85         return m_Macros;
86 }
87
88 Array::Ptr Notification::GetExportMacros(void) const
89 {
90         return m_ExportMacros;
91 }
92
93 std::set<User::Ptr> Notification::GetUsers(void) const
94 {
95         std::set<User::Ptr> result;
96
97         Array::Ptr users = m_Users;
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::GetGroups(void) const
116 {
117         std::set<UserGroup::Ptr> result;
118
119         Array::Ptr groups = m_Groups;
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 Dictionary::Ptr Notification::GetTimes(void) const
138 {
139         return m_Times;
140 }
141
142 double Notification::GetNotificationInterval(void) const
143 {
144         if (m_NotificationInterval.IsEmpty())
145                 return 300;
146         else
147                 return m_NotificationInterval;
148 }
149
150 TimePeriod::Ptr Notification::GetNotificationPeriod(void) const
151 {
152         return TimePeriod::GetByName(m_NotificationPeriod);
153 }
154
155 double Notification::GetLastNotification(void) const
156 {
157         if (m_LastNotification.IsEmpty())
158                 return 0;
159         else
160                 return m_LastNotification;
161 }
162
163 /**
164  * Sets the timestamp when the last notification was sent.
165  */
166 void Notification::SetLastNotification(double time)
167 {
168         m_LastNotification = time;
169         Touch("last_notification");
170 }
171
172 double Notification::GetNextNotification(void) const
173 {
174         if (m_NextNotification.IsEmpty())
175                 return 0;
176         else
177                 return m_NextNotification;
178 }
179
180 /**
181  * Sets the timestamp when the next periodical notification should be sent.
182  * This does not affect notifications that are sent for state changes.
183  */
184 void Notification::SetNextNotification(double time)
185 {
186         m_NextNotification = time;
187         Touch("next_notification");
188 }
189
190 String Notification::NotificationTypeToString(NotificationType type)
191 {
192         switch (type) {
193                 case NotificationDowntimeStart:
194                         return "DOWNTIMESTART";
195                 case NotificationDowntimeEnd:
196                         return "DOWNTIMEEND";
197                 case NotificationDowntimeRemoved:
198                         return "DOWNTIMECANCELLED";
199                 case NotificationCustom:
200                         return "CUSTOM";
201                 case NotificationAcknowledgement:
202                         return "ACKNOWLEDGEMENT";
203                 case NotificationProblem:
204                         return "PROBLEM";
205                 case NotificationRecovery:
206                         return "RECOVERY";
207                 case NotificationFlappingStart:
208                         return "FLAPPINGSTART";
209                 case NotificationFlappingEnd:
210                         return "FLAPPINGEND";
211                 default:
212                         return "UNKNOWN_NOTIFICATION";
213         }
214 }
215
216 void Notification::BeginExecuteNotification(NotificationType type, const Dictionary::Ptr& cr, bool force)
217 {
218         ASSERT(!OwnsLock());
219
220         if (!force) {
221                 TimePeriod::Ptr tp = GetNotificationPeriod();
222
223                 if (tp && !tp->IsInside(Utility::GetTime())) {
224                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': not in timeperiod");
225                         return;
226                 }
227
228                 double now = Utility::GetTime();
229                 Dictionary::Ptr times = GetTimes();
230                 Service::Ptr service = GetService();
231
232                 if (times && times->Contains("begin") && now < service->GetLastHardStateChange() + times->Get("begin")) {
233                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': before escalation range");
234                         return;
235                 }
236
237                 if (times && times->Contains("end") && now > service->GetLastHardStateChange() + times->Get("end")) {
238                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" + GetName() + "': after escalation range");
239                         return;
240                 }
241         }
242
243         {
244                 ObjectLock olock(this);
245
246                 SetLastNotification(Utility::GetTime());
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, GetGroups()) {
255                 std::set<User::Ptr> members = ug->GetMembers();
256                 std::copy(members.begin(), members.end(), std::inserter(allUsers, allUsers.begin()));
257         }
258
259         BOOST_FOREACH(const User::Ptr& user, allUsers) {
260                 Log(LogDebug, "icinga", "Sending notification for user '" + user->GetName() + "'");
261                 Utility::QueueAsyncCallback(boost::bind(&Notification::ExecuteNotificationHelper, this, type, user, cr, force));
262         }
263 }
264
265 void Notification::ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const Dictionary::Ptr& cr, bool ignore_timeperiod)
266 {
267         ASSERT(!OwnsLock());
268
269         if (!ignore_timeperiod) {
270                 TimePeriod::Ptr tp = user->GetNotificationPeriod();
271
272                 if (tp && !tp->IsInside(Utility::GetTime())) {
273                         Log(LogInformation, "icinga", "Not sending notifications for notification object '" +
274                             GetName() + " and user '" + user->GetName() + "': user not in timeperiod");
275                         return;
276                 }
277         }
278
279         try {
280                 GetNotificationCommand()->Execute(GetSelf(), user, cr, type);
281
282                 Log(LogInformation, "icinga", "Completed sending notification for service '" + GetService()->GetName() + "'");
283         } catch (const std::exception& ex) {
284                 std::ostringstream msgbuf;
285                 msgbuf << "Exception occured during notification for service '"
286                        << GetService()->GetName() << "': " << boost::diagnostic_information(ex);
287                 String message = msgbuf.str();
288
289                 Log(LogWarning, "icinga", message);
290         }
291 }
292
293 void Notification::OnAttributeChanged(const String& name)
294 {
295         ASSERT(!OwnsLock());
296
297         if (name == "host_name" || name == "service")
298                 Service::InvalidateNotificationsCache();
299 }
300
301 bool Notification::ResolveMacro(const String& macro, const Dictionary::Ptr&, String *result) const
302 {
303         Dictionary::Ptr macros = GetMacros();
304
305         if (macros && macros->Contains(macro)) {
306                 *result = macros->Get(macro);
307                 return true;
308         }
309
310         return false;
311 }