]> granicus.if.org Git - icinga2/blob - lib/icinga/service-notification.cpp
Implemented LAST*STATE* macros.
[icinga2] / lib / icinga / service-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 "i2-icinga.h"
21
22 using namespace icinga;
23
24 boost::mutex Service::m_NotificationMutex;
25 map<String, set<Notification::WeakPtr> > Service::m_NotificationsCache;
26 bool Service::m_NotificationsCacheNeedsUpdate = false;
27 Timer::Ptr Service::m_NotificationsCacheTimer;
28
29 /**
30  * @threadsafety Always.
31  */
32 void Service::RequestNotifications(NotificationType type, const Dictionary::Ptr& cr)
33 {
34         {
35                 ObjectLock olock(this);
36                 SetLastNotification(Utility::GetTime());
37         }
38
39         RequestMessage msg;
40         msg.SetMethod("icinga::SendNotifications");
41
42         NotificationRequestMessage params;
43         msg.SetParams(params);
44
45         params.SetService(GetName());
46         params.SetType(type);
47         params.SetCheckResult(cr);
48
49         Logger::Write(LogDebug, "icinga", "Sending notification anycast request for service '" + GetName() + "'");
50         EndpointManager::GetInstance()->SendAnycastMessage(Endpoint::Ptr(), msg);
51 }
52
53 /**
54  * @threadsafety Always.
55  */
56 void Service::SendNotifications(NotificationType type, const Dictionary::Ptr& cr)
57 {
58         if (!GetEnableNotifications()) {
59                 Logger::Write(LogInformation, "icinga", "Notifications are disabled for service '" + GetName() + "'.");
60                 return;
61         }
62
63         Logger::Write(LogInformation, "icinga", "Sending notifications for service '" + GetName() + "'");
64
65         set<Notification::Ptr> notifications = GetNotifications();
66
67         if (notifications.empty())
68                 Logger::Write(LogInformation, "icinga", "Service '" + GetName() + "' does not have any notifications.");
69
70         BOOST_FOREACH(const Notification::Ptr& notification, notifications) {
71                 try {
72                         notification->BeginExecuteNotification(type, cr);
73                 } catch (const exception& ex) {
74                         stringstream msgbuf;
75                         msgbuf << "Exception occured during notification for service '"
76                                << GetName() << "': " << diagnostic_information(ex);
77                         String message = msgbuf.str();
78
79                         Logger::Write(LogWarning, "icinga", message);
80                 }
81         }
82 }
83
84 /**
85  * @threadsafety Always.
86  */
87 void Service::InvalidateNotificationsCache(void)
88 {
89         boost::mutex::scoped_lock lock(m_NotificationMutex);
90
91         if (m_NotificationsCacheNeedsUpdate)
92                 return; /* Someone else has already requested a refresh. */
93
94         if (!m_NotificationsCacheTimer) {
95                 m_NotificationsCacheTimer = boost::make_shared<Timer>();
96                 m_NotificationsCacheTimer->SetInterval(0.5);
97                 m_NotificationsCacheTimer->OnTimerExpired.connect(boost::bind(&Service::RefreshNotificationsCache));
98                 m_NotificationsCacheTimer->Start();
99         }
100
101         m_NotificationsCacheNeedsUpdate = true;
102 }
103
104 /**
105  * @threadsafety Always.
106  */
107 void Service::RefreshNotificationsCache(void)
108 {
109         {
110                 boost::mutex::scoped_lock lock(m_NotificationMutex);
111
112                 if (!m_NotificationsCacheNeedsUpdate)
113                         return;
114
115                 m_NotificationsCacheNeedsUpdate = false;
116         }
117
118         Logger::Write(LogInformation, "icinga", "Updating Service notifications cache.");
119
120         map<String, set<Notification::WeakPtr> > newNotificationsCache;
121
122         BOOST_FOREACH(const DynamicObject::Ptr& object, DynamicType::GetObjects("Notification")) {
123                 const Notification::Ptr& notification = static_pointer_cast<Notification>(object);
124
125                 Service::Ptr service = notification->GetService();
126
127                 if (!service)
128                         continue;
129
130                 newNotificationsCache[service->GetName()].insert(notification);
131         }
132
133         boost::mutex::scoped_lock lock(m_NotificationMutex);
134         m_NotificationsCache.swap(newNotificationsCache);
135 }
136
137 /**
138  * @threadsafety Always.
139  */
140 set<Notification::Ptr> Service::GetNotifications(void) const
141 {
142         set<Notification::Ptr> notifications;
143
144         {
145                 boost::mutex::scoped_lock lock(m_NotificationMutex);
146
147                 BOOST_FOREACH(const Notification::WeakPtr& wservice, m_NotificationsCache[GetName()]) {
148                         Notification::Ptr notification = wservice.lock();
149
150                         if (!notification)
151                                 continue;
152
153                         notifications.insert(notification);
154                 }
155         }
156
157         return notifications;
158 }
159
160 /**
161  * @threadsafety Always.
162  */
163 template<typename TDict>
164 static void CopyNotificationAttributes(TDict notificationDesc, const ConfigItemBuilder::Ptr& builder)
165 {
166         /* TODO: we only need to copy macros if this is an inline definition,
167          * i.e. "typeid(notificationDesc)" != Notification, however for now we just
168          * copy them anyway. */
169         Value macros = notificationDesc->Get("macros");
170         if (!macros.IsEmpty())
171                 builder->AddExpression("macros", OperatorPlus, macros);
172
173         Value users = notificationDesc->Get("users");
174         if (!users.IsEmpty())
175                 builder->AddExpression("users", OperatorPlus, users);
176
177         Value groups = notificationDesc->Get("groups");
178         if (!groups.IsEmpty())
179                 builder->AddExpression("groups", OperatorPlus, groups);
180
181         /*Value notificationInterval = notificationDesc->Get("notification_interval");
182         if (!notificationInterval.IsEmpty())
183                 builder->AddExpression("notification_interval", OperatorSet, notificationInterval);*/
184 }
185
186 void Service::UpdateSlaveNotifications(void)
187 {
188         Dictionary::Ptr oldNotifications;
189         vector<Dictionary::Ptr> notificationDescsList;
190         ConfigItem::Ptr item;
191
192         item = ConfigItem::GetObject("Service", GetName());
193
194         /* Don't create slave notifications unless we own this object
195          * and it's not a template. */
196         if (!item || IsAbstract())
197                 return;
198
199         {
200                 ObjectLock olock(this);
201                 oldNotifications = m_SlaveNotifications;
202         }
203
204         notificationDescsList.push_back(Get("notifications"));
205
206         Dictionary::Ptr newNotifications;
207         newNotifications = boost::make_shared<Dictionary>();
208
209         Host::Ptr host = GetHost();
210
211         if (!host)
212                 return;
213
214         notificationDescsList.push_back(host->Get("notifications"));
215
216         BOOST_FOREACH(const Dictionary::Ptr& notificationDescs, notificationDescsList) {
217                 if (!notificationDescs)
218                         continue;
219
220                 ObjectLock olock(notificationDescs);
221
222                 String nfcname;
223                 Value nfcdesc;
224                 BOOST_FOREACH(tie(nfcname, nfcdesc), notificationDescs) {
225                         if (nfcdesc.IsScalar())
226                                 nfcname = nfcdesc;
227
228                         stringstream namebuf;
229                         namebuf << GetName() << "-" << nfcname;
230                         String name = namebuf.str();
231
232                         ConfigItemBuilder::Ptr builder = boost::make_shared<ConfigItemBuilder>(item->GetDebugInfo());
233                         builder->SetType("Notification");
234                         builder->SetName(name);
235                         builder->AddExpression("host_name", OperatorSet, host->GetName());
236                         builder->AddExpression("service", OperatorSet, GetShortName());
237
238                         CopyNotificationAttributes(this, builder);
239
240                         if (nfcdesc.IsScalar()) {
241                                 builder->AddParent(nfcdesc);
242                         } else if (nfcdesc.IsObjectType<Dictionary>()) {
243                                 Dictionary::Ptr notification = nfcdesc;
244
245                                 Dictionary::Ptr templates = notification->Get("templates");
246
247                                 if (templates) {
248                                         ObjectLock tlock(templates);
249
250                                         String tmpl;
251                                         BOOST_FOREACH(tie(tuples::ignore, tmpl), templates) {
252                                                 builder->AddParent(tmpl);
253                                         }
254                                 } else {
255                                         builder->AddParent(nfcname);
256                                 }
257
258                                 CopyNotificationAttributes(notification, builder);
259                         } else {
260                                 BOOST_THROW_EXCEPTION(invalid_argument("Notification description must be either a string or a dictionary."));
261                         }
262
263                         ConfigItem::Ptr notificationItem = builder->Compile();
264                         notificationItem->Commit();
265
266                         newNotifications->Set(name, notificationItem);
267                 }
268         }
269
270         if (oldNotifications) {
271                 ObjectLock olock(oldNotifications);
272
273                 ConfigItem::Ptr notification;
274                 BOOST_FOREACH(tie(tuples::ignore, notification), oldNotifications) {
275                         if (!notification)
276                                 continue;
277
278                         if (!newNotifications->Contains(notification->GetName()))
279                                 notification->Unregister();
280                 }
281         }
282
283         {
284                 ObjectLock olock(this);
285                 m_SlaveNotifications = newNotifications;
286         }
287 }
288
289 /**
290  * @threadsafety Always.
291  */
292 double Service::GetLastNotification(void) const
293 {
294         if (m_LastNotification.IsEmpty())
295                 return 0;
296         else
297                 return m_LastNotification;
298 }
299
300 /**
301  * @threadsafety Always.
302  */
303 void Service::SetLastNotification(double time)
304 {
305         m_LastNotification = time;
306         Touch("last_notification");
307 }
308
309 /**
310  * @threadsafety Always.
311  */
312 bool Service::GetEnableNotifications(void) const
313 {
314         if (m_EnableNotifications.IsEmpty())
315                 return true;
316         else
317                 return m_EnableNotifications;
318 }
319
320 /**
321  * @threadsafety Always.
322  */
323 void Service::SetEnableNotifications(bool enabled)
324 {
325         m_EnableNotifications = enabled;
326         Touch("enable_notifications");
327 }
328
329 /**
330  * @threadsafety Always.
331  */
332 double Service::GetNotificationInterval(void) const
333 {
334         if (m_NotificationInterval.IsEmpty())
335                 return 300;
336         else
337                 return m_NotificationInterval;
338 }