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