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