]> granicus.if.org Git - icinga2/blob - lib/db_ido/dbevents.cpp
Implement host checks.
[icinga2] / lib / db_ido / dbevents.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2014 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 "db_ido/dbevents.h"
21 #include "db_ido/dbtype.h"
22 #include "db_ido/dbvalue.h"
23 #include "base/convert.h"
24 #include "base/objectlock.h"
25 #include "base/initialize.h"
26 #include "base/dynamictype.h"
27 #include "base/utility.h"
28 #include "remote/endpoint.h"
29 #include "icinga/notification.h"
30 #include "icinga/checkcommand.h"
31 #include "icinga/eventcommand.h"
32 #include "icinga/externalcommandprocessor.h"
33 #include "icinga/compatutility.h"
34 #include "icinga/icingaapplication.h"
35 #include <boost/foreach.hpp>
36 #include <boost/algorithm/string/join.hpp>
37
38 using namespace icinga;
39
40 INITIALIZE_ONCE(&DbEvents::StaticInitialize);
41
42 void DbEvents::StaticInitialize(void)
43 {
44         /* Status */
45         Service::OnCommentAdded.connect(boost::bind(&DbEvents::AddComment, _1, _2));
46         Service::OnCommentRemoved.connect(boost::bind(&DbEvents::RemoveComment, _1, _2));
47         Service::OnDowntimeAdded.connect(boost::bind(&DbEvents::AddDowntime, _1, _2));
48         Service::OnDowntimeRemoved.connect(boost::bind(&DbEvents::RemoveDowntime, _1, _2));
49         Service::OnDowntimeTriggered.connect(boost::bind(&DbEvents::TriggerDowntime, _1, _2));
50
51         /* History */
52         Service::OnCommentAdded.connect(boost::bind(&DbEvents::AddCommentHistory, _1, _2));
53         Service::OnDowntimeAdded.connect(boost::bind(&DbEvents::AddDowntimeHistory, _1, _2));
54         Service::OnAcknowledgementSet.connect(boost::bind(&DbEvents::AddAcknowledgementHistory, _1, _2, _3, _4, _5));
55
56         Service::OnNotificationSentToAllUsers.connect(bind(&DbEvents::AddNotificationHistory, _1, _2, _3, _4, _5, _6, _7));
57
58         Service::OnStateChange.connect(boost::bind(&DbEvents::AddStateChangeHistory, _1, _2, _3));
59
60         Service::OnNewCheckResult.connect(bind(&DbEvents::AddCheckResultLogHistory, _1, _2));
61         Service::OnNotificationSentToUser.connect(bind(&DbEvents::AddNotificationSentLogHistory, _1, _2, _3, _4, _5, _6, _7));
62         Service::OnFlappingChanged.connect(bind(&DbEvents::AddFlappingLogHistory, _1, _2));
63         Service::OnDowntimeTriggered.connect(boost::bind(&DbEvents::AddTriggerDowntimeLogHistory, _1, _2));
64         Service::OnDowntimeRemoved.connect(boost::bind(&DbEvents::AddRemoveDowntimeLogHistory, _1, _2));
65
66         Service::OnFlappingChanged.connect(bind(&DbEvents::AddFlappingHistory, _1, _2));
67         Service::OnNewCheckResult.connect(bind(&DbEvents::AddServiceCheckHistory, _1, _2));
68
69         Service::OnEventCommandExecuted.connect(bind(&DbEvents::AddEventHandlerHistory, _1));
70
71         ExternalCommandProcessor::OnNewExternalCommand.connect(boost::bind(&DbEvents::AddExternalCommandHistory, _1, _2, _3));
72 }
73
74 /* comments */
75 void DbEvents::AddComments(const Checkable::Ptr& checkable)
76 {
77         /* dump all comments */
78         Dictionary::Ptr comments = checkable->GetComments();
79
80         if (comments->GetLength() > 0)
81                 RemoveComments(checkable);
82
83         ObjectLock olock(comments);
84
85         BOOST_FOREACH(const Dictionary::Pair& kv, comments) {
86                 AddComment(checkable, kv.second);
87         }
88 }
89
90 void DbEvents::AddComment(const Checkable::Ptr& checkable, const Comment::Ptr& comment)
91 {
92         AddCommentInternal(checkable, comment, false);
93 }
94
95 void DbEvents::AddCommentHistory(const Checkable::Ptr& checkable, const Comment::Ptr& comment)
96 {
97         AddCommentInternal(checkable, comment, true);
98 }
99
100 void DbEvents::AddCommentInternal(const Checkable::Ptr& checkable, const Comment::Ptr& comment, bool historical)
101 {
102         if (!comment) {
103                 Log(LogWarning, "db_ido", "comment does not exist. not adding it.");
104                 return;
105         }
106
107         Log(LogDebug, "db_ido", "adding service comment (id = " + Convert::ToString(comment->GetLegacyId()) + ") for '" + checkable->GetName() + "'");
108
109         /* add the service comment */
110         AddCommentByType(checkable, comment, historical);
111 }
112
113 void DbEvents::AddCommentByType(const DynamicObject::Ptr& object, const Comment::Ptr& comment, bool historical)
114 {
115         unsigned long entry_time = static_cast<long>(comment->GetEntryTime());
116         unsigned long entry_time_usec = (comment->GetEntryTime() - entry_time) * 1000 * 1000;
117
118         Dictionary::Ptr fields1 = make_shared<Dictionary>();
119         fields1->Set("entry_time", DbValue::FromTimestamp(entry_time));
120         fields1->Set("entry_time_usec", entry_time_usec);
121         fields1->Set("entry_type", comment->GetEntryType());
122         fields1->Set("object_id", object);
123
124         if (object->GetType() == DynamicType::GetByName("Host")) {
125                 fields1->Set("comment_type", 2);
126                 /* requires idoutils 1.10 schema fix */
127                 fields1->Set("internal_comment_id", comment->GetLegacyId());
128         } else if (object->GetType() == DynamicType::GetByName("Service")) {
129                 fields1->Set("comment_type", 1);
130                 fields1->Set("internal_comment_id", comment->GetLegacyId());
131         } else {
132                 Log(LogDebug, "db_ido", "unknown object type for adding comment.");
133                 return;
134         }
135
136         fields1->Set("comment_time", DbValue::FromTimestamp(entry_time)); /* same as entry_time */
137         fields1->Set("author_name", comment->GetAuthor());
138         fields1->Set("comment_data", comment->GetText());
139         fields1->Set("is_persistent", 1);
140         fields1->Set("comment_source", 1); /* external */
141         fields1->Set("expires", (comment->GetExpireTime() > 0) ? 1 : 0);
142         fields1->Set("expiration_time", DbValue::FromTimestamp(comment->GetExpireTime()));
143         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
144
145         String node = IcingaApplication::GetInstance()->GetNodeName();
146
147         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
148         if (endpoint)
149                 fields1->Set("endpoint_object_id", endpoint);
150
151         DbQuery query1;
152         if (!historical) {
153                 query1.Table = "comments";
154         } else {
155                 query1.Table = "commenthistory";
156         }
157         query1.Type = DbQueryInsert;
158         query1.Category = DbCatComment;
159         query1.Fields = fields1;
160         DbObject::OnQuery(query1);
161 }
162
163 void DbEvents::RemoveComments(const Checkable::Ptr& checkable)
164 {
165         Log(LogDebug, "db_ido", "removing service comments for '" + checkable->GetName() + "'");
166
167         DbQuery query1;
168         query1.Table = "comments";
169         query1.Type = DbQueryDelete;
170         query1.Category = DbCatComment;
171         query1.WhereCriteria = make_shared<Dictionary>();
172         query1.WhereCriteria->Set("object_id", checkable);
173         DbObject::OnQuery(query1);
174 }
175
176 void DbEvents::RemoveComment(const Checkable::Ptr& checkable, const Comment::Ptr& comment)
177 {
178         if (!comment) {
179                 Log(LogWarning, "db_ido", "comment does not exist. not deleting it.");
180                 return;
181         }
182
183         Log(LogDebug, "db_ido", "removing service comment (id = " + Convert::ToString(comment->GetLegacyId()) + ") for '" + checkable->GetName() + "'");
184
185         /* Status */
186         DbQuery query1;
187         query1.Table = "comments";
188         query1.Type = DbQueryDelete;
189         query1.Category = DbCatComment;
190         query1.WhereCriteria = make_shared<Dictionary>();
191         query1.WhereCriteria->Set("object_id", checkable);
192         query1.WhereCriteria->Set("internal_comment_id", comment->GetLegacyId());
193         DbObject::OnQuery(query1);
194
195         /* History - update deletion time for service/host */
196         unsigned long entry_time = static_cast<long>(comment->GetEntryTime());
197
198         double now = Utility::GetTime();
199         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
200
201         DbQuery query2;
202         query2.Table = "commenthistory";
203         query2.Type = DbQueryUpdate;
204         query2.Category = DbCatComment;
205
206         Dictionary::Ptr fields2 = make_shared<Dictionary>();
207         fields2->Set("deletion_time", DbValue::FromTimestamp(time_bag.first));
208         fields2->Set("deletion_time_usec", time_bag.second);
209         query2.Fields = fields2;
210
211         query2.WhereCriteria = make_shared<Dictionary>();
212         query2.WhereCriteria->Set("internal_comment_id", comment->GetLegacyId());
213         query2.WhereCriteria->Set("comment_time", DbValue::FromTimestamp(entry_time));
214         query2.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
215
216         DbObject::OnQuery(query2);
217 }
218
219 /* downtimes */
220 void DbEvents::AddDowntimes(const Checkable::Ptr& checkable)
221 {
222         /* dump all downtimes */
223         Dictionary::Ptr downtimes = checkable->GetDowntimes();
224
225         if (downtimes->GetLength() > 0)
226                 RemoveDowntimes(checkable);
227
228         ObjectLock olock(downtimes);
229
230         BOOST_FOREACH(const Dictionary::Pair& kv, downtimes) {
231                 AddDowntime(checkable, kv.second);
232         }
233 }
234
235 void DbEvents::AddDowntime(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
236 {
237         AddDowntimeInternal(checkable, downtime, false);
238 }
239
240 void DbEvents::AddDowntimeHistory(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
241 {
242         AddDowntimeInternal(checkable, downtime, true);
243 }
244
245 void DbEvents::AddDowntimeInternal(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, bool historical)
246 {
247         if (!downtime) {
248                 Log(LogWarning, "db_ido", "downtime does not exist. not adding it.");
249                 return;
250         }
251
252         Log(LogDebug, "db_ido", "adding service downtime (id = " + Convert::ToString(downtime->GetLegacyId()) + ") for '" + checkable->GetName() + "'");
253
254         /* add the downtime */
255         AddDowntimeByType(checkable, downtime, historical);}
256
257 void DbEvents::AddDowntimeByType(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime, bool historical)
258 {
259         Dictionary::Ptr fields1 = make_shared<Dictionary>();
260         fields1->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
261         fields1->Set("object_id", checkable);
262
263         if (checkable->GetType() == DynamicType::GetByName("Host")) {
264                 fields1->Set("downtime_type", 2);
265                 /* requires idoutils 1.10 schema fix */
266                 fields1->Set("internal_downtime_id", downtime->GetLegacyId());
267         } else if (checkable->GetType() == DynamicType::GetByName("Service")) {
268                 fields1->Set("downtime_type", 1);
269                 fields1->Set("internal_downtime_id", downtime->GetLegacyId());
270         } else {
271                 Log(LogDebug, "db_ido", "unknown object type for adding downtime.");
272                 return;
273         }
274
275         fields1->Set("author_name", downtime->GetAuthor());
276         fields1->Set("comment_data", downtime->GetComment());
277         fields1->Set("triggered_by_id", Service::GetDowntimeByID(downtime->GetTriggeredBy()));
278         fields1->Set("is_fixed", downtime->GetFixed());
279         fields1->Set("duration", downtime->GetDuration());
280         fields1->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
281         fields1->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
282         fields1->Set("was_started", Empty);
283         fields1->Set("actual_start_time", Empty);
284         fields1->Set("actual_start_time_usec", Empty);
285         fields1->Set("is_in_effect", Empty);
286         fields1->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()));
287         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
288
289         String node = IcingaApplication::GetInstance()->GetNodeName();
290
291         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
292         if (endpoint)
293                 fields1->Set("endpoint_object_id", endpoint);
294
295         DbQuery query1;
296
297         if (!historical)
298                 query1.Table = "scheduleddowntime";
299         else
300                 query1.Table = "downtimehistory";
301
302         query1.Type = DbQueryInsert;
303         query1.Category = DbCatDowntime;
304         query1.Fields = fields1;
305         DbObject::OnQuery(query1);
306 }
307
308 void DbEvents::RemoveDowntimes(const Checkable::Ptr& checkable)
309 {
310         Log(LogDebug, "db_ido", "removing service downtimes for '" + checkable->GetName() + "'");
311
312         DbQuery query1;
313         query1.Table = "scheduleddowntime";
314         query1.Type = DbQueryDelete;
315         query1.Category = DbCatDowntime;
316         query1.WhereCriteria = make_shared<Dictionary>();
317         query1.WhereCriteria->Set("object_id", checkable);
318         DbObject::OnQuery(query1);
319 }
320
321 void DbEvents::RemoveDowntime(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
322 {
323         if (!downtime) {
324                 Log(LogWarning, "db_ido", "downtime does not exist. not adding it.");
325                 return;
326         }
327
328         Log(LogDebug, "db_ido", "removing service downtime (id = " + Convert::ToString(downtime->GetLegacyId()) + ") for '" + checkable->GetName() + "'");
329
330         /* Status */
331         DbQuery query1;
332         query1.Table = "scheduleddowntime";
333         query1.Type = DbQueryDelete;
334         query1.Category = DbCatDowntime;
335         query1.WhereCriteria = make_shared<Dictionary>();
336         query1.WhereCriteria->Set("object_id", checkable);
337         query1.WhereCriteria->Set("internal_downtime_id", downtime->GetLegacyId());
338         DbObject::OnQuery(query1);
339
340         /* History - update actual_end_time, was_cancelled for service (and host in case) */
341         double now = Utility::GetTime();
342         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
343
344         DbQuery query3;
345         query3.Table = "downtimehistory";
346         query3.Type = DbQueryUpdate;
347         query3.Category = DbCatDowntime;
348
349         Dictionary::Ptr fields3 = make_shared<Dictionary>();
350         fields3->Set("was_cancelled", downtime->GetWasCancelled() ? 1 : 0);
351         fields3->Set("actual_end_time", DbValue::FromTimestamp(time_bag.first));
352         fields3->Set("actual_end_time_usec", time_bag.second);
353         query3.Fields = fields3;
354
355         query3.WhereCriteria = make_shared<Dictionary>();
356         query3.WhereCriteria->Set("internal_downtime_id", downtime->GetLegacyId());
357         query3.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
358         query3.WhereCriteria->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
359         query3.WhereCriteria->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
360         query3.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
361
362         DbObject::OnQuery(query3);
363 }
364
365 void DbEvents::TriggerDowntime(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
366 {
367         if (!downtime) {
368                 Log(LogWarning, "db_ido", "downtime does not exist. not updating it.");
369                 return;
370         }
371
372         Log(LogDebug, "db_ido", "updating triggered service downtime (id = " + Convert::ToString(downtime->GetLegacyId()) + ") for '" + checkable->GetName() + "'");
373
374         double now = Utility::GetTime();
375         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
376
377         /* Status */
378         DbQuery query1;
379         query1.Table = "scheduleddowntime";
380         query1.Type = DbQueryUpdate;
381         query1.Category = DbCatDowntime;
382
383         Dictionary::Ptr fields1 = make_shared<Dictionary>();
384         fields1->Set("was_started", 1);
385         fields1->Set("actual_start_time", DbValue::FromTimestamp(time_bag.first));
386         fields1->Set("actual_start_time_usec", time_bag.second);
387         fields1->Set("is_in_effect", 1);
388         fields1->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()));
389         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
390
391         query1.WhereCriteria = make_shared<Dictionary>();
392         query1.WhereCriteria->Set("object_id", checkable);
393         query1.WhereCriteria->Set("internal_downtime_id", downtime->GetLegacyId());
394
395         query1.Fields = fields1;
396         DbObject::OnQuery(query1);
397
398         /* History - downtime was started for service (and host in case) */
399         DbQuery query3;
400         query3.Table = "downtimehistory";
401         query3.Type = DbQueryUpdate;
402         query3.Category = DbCatDowntime;
403
404         Dictionary::Ptr fields3 = make_shared<Dictionary>();
405         fields3->Set("was_started", 1);
406         fields3->Set("is_in_effect", 1);
407         fields3->Set("actual_start_time", DbValue::FromTimestamp(time_bag.first));
408         fields3->Set("actual_start_time_usec", time_bag.second);
409         fields3->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()));
410         query3.Fields = fields3;
411
412         query3.WhereCriteria = make_shared<Dictionary>();
413         query3.WhereCriteria->Set("internal_downtime_id", downtime->GetLegacyId());
414         query3.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
415         query3.WhereCriteria->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
416         query3.WhereCriteria->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
417         query3.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
418
419         DbObject::OnQuery(query3);
420 }
421
422 /* acknowledgements */
423 void DbEvents::AddAcknowledgementHistory(const Checkable::Ptr& checkable, const String& author, const String& comment,
424     AcknowledgementType type, double expiry)
425 {
426         Log(LogDebug, "db_ido", "add acknowledgement history for '" + checkable->GetName() + "'");
427
428         double now = Utility::GetTime();
429         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
430
431         unsigned long end_time = static_cast<long>(expiry);
432
433         DbQuery query1;
434         query1.Table = "acknowledgements";
435         query1.Type = DbQueryInsert;
436         query1.Category = DbCatAcknowledgement;
437
438         Host::Ptr host;
439         Service::Ptr service;
440         tie(host, service) = GetHostService(checkable);
441
442         Dictionary::Ptr fields1 = make_shared<Dictionary>();
443         fields1->Set("entry_time", DbValue::FromTimestamp(time_bag.first));
444         fields1->Set("entry_time_usec", time_bag.second);
445         fields1->Set("acknowledgement_type", type);
446         fields1->Set("object_id", checkable);
447         fields1->Set("state", service ? service->GetState() : host->GetState());
448         fields1->Set("author_name", author);
449         fields1->Set("comment_data", comment);
450         fields1->Set("is_sticky", type == AcknowledgementSticky ? 1 : 0);
451         fields1->Set("end_time", DbValue::FromTimestamp(end_time));
452         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
453
454         String node = IcingaApplication::GetInstance()->GetNodeName();
455
456         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
457         if (endpoint)
458                 fields1->Set("endpoint_object_id", endpoint);
459
460         query1.Fields = fields1;
461         DbObject::OnQuery(query1);
462 }
463
464 /* notifications */
465 void DbEvents::AddNotificationHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set<User::Ptr>& users, NotificationType type,
466     const CheckResult::Ptr& cr, const String& author, const String& text)
467 {
468         Log(LogDebug, "db_ido", "add notification history for '" + checkable->GetName() + "'");
469
470         /* start and end happen at the same time */
471         double now = Utility::GetTime();
472         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
473
474         DbQuery query1;
475         query1.Table = "notifications";
476         query1.Type = DbQueryInsert;
477         query1.Category = DbCatNotification;
478         /* store the object ptr for caching the insert id for this object */
479         query1.NotificationObject = notification;
480
481         Host::Ptr host;
482         Service::Ptr service;
483         tie(host, service) = GetHostService(checkable);
484
485         Dictionary::Ptr fields1 = make_shared<Dictionary>();
486         fields1->Set("notification_type", 1); /* service */
487         fields1->Set("notification_reason", CompatUtility::MapNotificationReasonType(type));
488         fields1->Set("object_id", checkable);
489         fields1->Set("start_time", DbValue::FromTimestamp(time_bag.first));
490         fields1->Set("start_time_usec", time_bag.second);
491         fields1->Set("end_time", DbValue::FromTimestamp(time_bag.first));
492         fields1->Set("end_time_usec", time_bag.second);
493         fields1->Set("state", service ? service->GetState() : host->GetState());
494
495         if (cr) {
496                 fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
497                 fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
498         }
499
500         fields1->Set("escalated", 0);
501         fields1->Set("contacts_notified", static_cast<long>(users.size()));
502         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
503
504         String node = IcingaApplication::GetInstance()->GetNodeName();
505
506         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
507         if (endpoint)
508                 fields1->Set("endpoint_object_id", endpoint);
509
510         query1.Fields = fields1;
511         DbObject::OnQuery(query1);
512
513         DbQuery query2;
514         query2.Table = "contactnotifications";
515         query2.Type = DbQueryInsert;
516         query2.Category = DbCatNotification;
517
518         /* filtered users */
519         BOOST_FOREACH(const User::Ptr& user, users) {
520                 Log(LogDebug, "db_ido", "add contact notification history for service '" + checkable->GetName() + "' and user '" + user->GetName() + "'.");
521
522                 Dictionary::Ptr fields2 = make_shared<Dictionary>();
523                 fields2->Set("contact_object_id", user);
524                 fields2->Set("start_time", DbValue::FromTimestamp(time_bag.first));
525                 fields2->Set("start_time_usec", time_bag.second);
526                 fields2->Set("end_time", DbValue::FromTimestamp(time_bag.first));
527                 fields2->Set("end_time_usec", time_bag.second);
528
529                 fields2->Set("notification_id", notification); /* DbConnection class fills in real ID from notification insert id cache */
530                 fields2->Set("instance_id", 0); /* DbConnection class fills in real ID */
531
532                 query2.Fields = fields2;
533                 DbObject::OnQuery(query2);
534         }
535 }
536
537 /* statehistory */
538 void DbEvents::AddStateChangeHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type)
539 {
540         Log(LogDebug, "db_ido", "add state change history for '" + checkable->GetName() + "'");
541
542         double now = Utility::GetTime();
543         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
544
545         DbQuery query1;
546         query1.Table = "statehistory";
547         query1.Type = DbQueryInsert;
548         query1.Category = DbCatStateHistory;
549
550         Host::Ptr host;
551         Service::Ptr service;
552         tie(host, service) = GetHostService(checkable);
553
554         Dictionary::Ptr fields1 = make_shared<Dictionary>();
555         fields1->Set("state_time", DbValue::FromTimestamp(time_bag.first));
556         fields1->Set("state_time_usec", time_bag.second);
557         fields1->Set("object_id", checkable);
558         fields1->Set("state_change", 1); /* service */
559         fields1->Set("state", service ? service->GetState() : host->GetState());
560         fields1->Set("state_type", checkable->GetStateType());
561         fields1->Set("current_check_attempt", checkable->GetCheckAttempt());
562         fields1->Set("max_check_attempts", checkable->GetMaxCheckAttempts());
563
564         if (service) {
565                 fields1->Set("last_state", service->GetLastState());
566                 fields1->Set("last_hard_state", service->GetLastHardState());
567         } else {
568                 fields1->Set("last_state", host->GetLastState());
569                 fields1->Set("last_hard_state", host->GetLastHardState());
570         }
571
572         if (cr) {
573                 fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
574                 fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
575                 fields1->Set("check_source", cr->GetCheckSource());
576         }
577
578         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
579
580         String node = IcingaApplication::GetInstance()->GetNodeName();
581
582         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
583         if (endpoint)
584                 fields1->Set("endpoint_object_id", endpoint);
585
586         query1.Fields = fields1;
587         DbObject::OnQuery(query1);
588 }
589
590 /* logentries */
591 void DbEvents::AddCheckResultLogHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
592 {
593         Dictionary::Ptr vars_after = cr->GetVarsAfter();
594
595         long state_after = vars_after->Get("state");
596         long stateType_after = vars_after->Get("state_type");
597         long attempt_after = vars_after->Get("attempt");
598         bool reachable_after = vars_after->Get("reachable");
599         bool host_reachable_after = vars_after->Get("host_reachable");
600
601         Dictionary::Ptr vars_before = cr->GetVarsBefore();
602
603         if (vars_before) {
604                 long state_before = vars_before->Get("state");
605                 long stateType_before = vars_before->Get("state_type");
606                 long attempt_before = vars_before->Get("attempt");
607                 bool reachable_before = vars_before->Get("reachable");
608
609                 if (state_before == state_after && stateType_before == stateType_after &&
610                     attempt_before == attempt_after && reachable_before == reachable_after)
611                         return; /* Nothing changed, ignore this checkresult. */
612         }
613
614         LogEntryType type;
615         String output;
616
617         if (cr)
618                 output = CompatUtility::GetCheckResultOutput(cr);
619
620         Host::Ptr host;
621         Service::Ptr service;
622         tie(host, service) = GetHostService(checkable);
623
624         std::ostringstream msgbuf;
625
626         if (service) {
627                 msgbuf << "SERVICE ALERT: "
628                        << host->GetName() << ";"
629                        << service->GetShortName() << ";"
630                        << Service::StateToString(static_cast<ServiceState>(state_after)) << ";"
631                        << Service::StateTypeToString(static_cast<StateType>(stateType_after)) << ";"
632                        << attempt_after << ";"
633                        << output << ""
634                        << "";
635
636                 switch (service->GetState()) {
637                         case StateOK:
638                                 type = LogEntryTypeServiceOk;
639                                 break;
640                         case StateUnknown:
641                                 type = LogEntryTypeServiceUnknown;
642                                 break;
643                         case StateWarning:
644                                 type = LogEntryTypeServiceWarning;
645                                 break;
646                         case StateCritical:
647                                 type = LogEntryTypeServiceCritical;
648                                 break;
649                         default:
650                                 Log(LogCritical, "db_ido", "Unknown service state: " + Convert::ToString(state_after));
651                                 return;
652                 }
653         } else {
654                 msgbuf << "HOST ALERT: "
655                        << host->GetName() << ";"
656                        << Host::StateToString(Host::CalculateState(static_cast<ServiceState>(state_after), host_reachable_after)) << ";"
657                        << Service::StateTypeToString(static_cast<StateType>(stateType_after)) << ";"
658                        << attempt_after << ";"
659                        << output << ""
660                        << "";
661
662                 switch (host->GetState()) {
663                         case HostUp:
664                                 type = LogEntryTypeHostUp;
665                                 break;
666                         case HostDown:
667                                 type = LogEntryTypeHostDown;
668                                 break;
669                         case HostUnreachable:
670                                 type = LogEntryTypeHostUnreachable;
671                                 break;
672                         default:
673                                 Log(LogCritical, "db_ido", "Unknown host state: " + Convert::ToString(state_after));
674                                 return;
675                 }
676
677         }
678
679         AddLogHistory(checkable, msgbuf.str(), type);
680 }
681
682 void DbEvents::AddTriggerDowntimeLogHistory(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
683 {
684         if (!downtime)
685                 return;
686
687         Host::Ptr host;
688         Service::Ptr service;
689         tie(host, service) = GetHostService(checkable);
690
691         std::ostringstream msgbuf;
692
693         if (service) {
694                 msgbuf << "SERVICE DOWNTIME ALERT: "
695                         << host->GetName() << ";"
696                         << service->GetShortName() << ";"
697                         << "STARTED" << "; "
698                         << "Service has entered a period of scheduled downtime."
699                         << "";
700         } else {
701                 msgbuf << "HOST DOWNTIME ALERT: "
702                         << host->GetName() << ";"
703                         << "STARTED" << "; "
704                         << "Service has entered a period of scheduled downtime."
705                         << "";
706         }
707
708         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
709 }
710
711 void DbEvents::AddRemoveDowntimeLogHistory(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
712 {
713         if (!downtime)
714                 return;
715
716         String downtime_output;
717         String downtime_state_str;
718
719         if (downtime->GetWasCancelled()) {
720                 downtime_output = "Scheduled downtime for service has been cancelled.";
721                 downtime_state_str = "CANCELLED";
722         } else {
723                 downtime_output = "Service has exited from a period of scheduled downtime.";
724                 downtime_state_str = "STOPPED";
725         }
726
727         Host::Ptr host;
728         Service::Ptr service;
729         tie(host, service) = GetHostService(checkable);
730
731         std::ostringstream msgbuf;
732
733         if (service) {
734                 msgbuf << "SERVICE DOWNTIME ALERT: "
735                         << host->GetName() << ";"
736                         << service->GetShortName() << ";"
737                         << downtime_state_str << "; "
738                         << downtime_output
739                         << "";
740         } else {
741                 msgbuf << "HOST DOWNTIME ALERT: "
742                         << host->GetName() << ";"
743                         << downtime_state_str << "; "
744                         << downtime_output
745                         << "";
746         }
747
748         AddLogHistory(service, msgbuf.str(), LogEntryTypeInfoMessage);
749 }
750
751 void DbEvents::AddNotificationSentLogHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user,
752     NotificationType notification_type, const CheckResult::Ptr& cr,
753     const String& author, const String& comment_text)
754 {
755         CheckCommand::Ptr commandObj = checkable->GetCheckCommand();
756
757         String check_command = "";
758         if (commandObj)
759                 check_command = commandObj->GetName();
760
761         String notification_type_str = Notification::NotificationTypeToString(notification_type);
762
763         String author_comment = "";
764         if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) {
765                 author_comment = ";" + author + ";" + comment_text;
766         }
767
768         if (!cr)
769                 return;
770
771         String output;
772
773         if (cr)
774                 output = CompatUtility::GetCheckResultOutput(cr);
775
776         Host::Ptr host;
777         Service::Ptr service;
778         tie(host, service) = GetHostService(checkable);
779
780         std::ostringstream msgbuf;
781
782         if (service) {
783                 msgbuf << "SERVICE NOTIFICATION: "
784                        << user->GetName() << ";"
785                        << host->GetName() << ";"
786                        << service->GetShortName() << ";"
787                        << notification_type_str << " "
788                        << "(" << Service::StateToString(service->GetState()) << ");"
789                        << check_command << ";"
790                        << output << author_comment
791                        << "";
792         } else {
793                 msgbuf << "HOST NOTIFICATION: "
794                        << user->GetName() << ";"
795                        << host->GetName() << ";"
796                        << notification_type_str << " "
797                        << "(" << Host::StateToString(host->GetState()) << ");"
798                        << check_command << ";"
799                        << output << author_comment
800                        << "";
801         }
802
803         AddLogHistory(service, msgbuf.str(), LogEntryTypeHostNotification);
804 }
805
806 void DbEvents::AddFlappingLogHistory(const Checkable::Ptr& checkable, FlappingState flapping_state)
807 {
808         String flapping_state_str;
809         String flapping_output;
810
811         switch (flapping_state) {
812                 case FlappingStarted:
813                         flapping_output = "Service appears to have started flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change >= " + Convert::ToString(checkable->GetFlappingThreshold()) + "% threshold)";
814                         flapping_state_str = "STARTED";
815                         break;
816                 case FlappingStopped:
817                         flapping_output = "Service appears to have stopped flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change < " + Convert::ToString(checkable->GetFlappingThreshold()) + "% threshold)";
818                         flapping_state_str = "STOPPED";
819                         break;
820                 case FlappingDisabled:
821                         flapping_output = "Flap detection has been disabled";
822                         flapping_state_str = "DISABLED";
823                         break;
824                 default:
825                         Log(LogCritical, "db_ido", "Unknown flapping state: " + Convert::ToString(flapping_state));
826                         return;
827         }
828
829         Host::Ptr host;
830         Service::Ptr service;
831         tie(host, service) = GetHostService(checkable);
832
833         std::ostringstream msgbuf;
834
835         if (service) {
836                 msgbuf << "SERVICE FLAPPING ALERT: "
837                         << host->GetName() << ";"
838                         << service->GetShortName() << ";"
839                         << flapping_state_str << "; "
840                         << flapping_output
841                         << "";
842         } else {
843                 msgbuf << "HOST FLAPPING ALERT: "
844                         << host->GetName() << ";"
845                         << flapping_state_str << "; "
846                         << flapping_output
847                         << "";
848         }
849
850         AddLogHistory(service, msgbuf.str(), LogEntryTypeInfoMessage);
851 }
852
853 void DbEvents::AddLogHistory(const Checkable::Ptr& checkable, String buffer, LogEntryType type)
854 {
855         Log(LogDebug, "db_ido", "add log entry history for '" + checkable->GetName() + "'");
856
857         double now = Utility::GetTime();
858         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
859
860         DbQuery query1;
861         query1.Table = "logentries";
862         query1.Type = DbQueryInsert;
863         query1.Category = DbCatLog;
864
865         Dictionary::Ptr fields1 = make_shared<Dictionary>();
866         fields1->Set("logentry_time", DbValue::FromTimestamp(time_bag.first));
867         fields1->Set("entry_time", DbValue::FromTimestamp(time_bag.first));
868         fields1->Set("entry_time_usec", time_bag.second);
869         fields1->Set("object_id", checkable); // added in 1.10 see #4754
870         fields1->Set("logentry_type", type);
871         fields1->Set("logentry_data", buffer);
872
873         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
874
875         String node = IcingaApplication::GetInstance()->GetNodeName();
876
877         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
878         if (endpoint)
879                 fields1->Set("endpoint_object_id", endpoint);
880
881         query1.Fields = fields1;
882         DbObject::OnQuery(query1);
883 }
884
885 /* flappinghistory */
886 void DbEvents::AddFlappingHistory(const Checkable::Ptr& checkable, FlappingState flapping_state)
887 {
888         Log(LogDebug, "db_ido", "add flapping history for '" + checkable->GetName() + "'");
889
890         double now = Utility::GetTime();
891         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
892
893         DbQuery query1;
894         query1.Table = "flappinghistory";
895         query1.Type = DbQueryInsert;
896         query1.Category = DbCatFlapping;
897
898         Dictionary::Ptr fields1 = make_shared<Dictionary>();
899
900         fields1->Set("event_time", DbValue::FromTimestamp(time_bag.first));
901         fields1->Set("event_time_usec", time_bag.second);
902
903         switch (flapping_state) {
904                 case FlappingStarted:
905                         fields1->Set("event_type", 1000);
906                         break;
907                 case FlappingStopped:
908                         fields1->Set("event_type", 1001);
909                         fields1->Set("reason_type", 1);
910                         break;
911                 case FlappingDisabled:
912                         fields1->Set("event_type", 1001);
913                         fields1->Set("reason_type", 2);
914                         break;
915                 default:
916                         Log(LogDebug, "db_ido", "Unhandled flapping state: " + Convert::ToString(flapping_state));
917                         return;
918         }
919
920         Host::Ptr host;
921         Service::Ptr service;
922         tie(host, service) = GetHostService(checkable);
923
924         fields1->Set("flapping_type", service ? 1 : 0);
925         fields1->Set("object_id", checkable);
926         fields1->Set("percent_state_change", checkable->GetFlappingCurrent());
927         fields1->Set("low_threshold", checkable->GetFlappingThreshold());
928         fields1->Set("high_threshold", checkable->GetFlappingThreshold());
929
930         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
931
932         String node = IcingaApplication::GetInstance()->GetNodeName();
933
934         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
935         if (endpoint)
936                 fields1->Set("endpoint_object_id", endpoint);
937
938         query1.Fields = fields1;
939         DbObject::OnQuery(query1);
940 }
941
942 /* servicechecks */
943 void DbEvents::AddServiceCheckHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
944 {
945         if (!cr)
946                 return;
947
948         Log(LogDebug, "db_ido", "add service check history for '" + checkable->GetName() + "'");
949
950         Host::Ptr host;
951         Service::Ptr service;
952         tie(host, service) = GetHostService(checkable);
953
954         std::ostringstream msgbuf;
955
956         DbQuery query1;
957         query1.Table = service ? "servicechecks" : "hostchecks";
958         query1.Type = DbQueryInsert;
959         query1.Category = DbCatCheck;
960
961         Dictionary::Ptr fields1 = make_shared<Dictionary>();
962         double execution_time = Service::CalculateExecutionTime(cr);
963
964         fields1->Set("check_type", CompatUtility::GetCheckableCheckType(checkable));
965         fields1->Set("current_check_attempt", checkable->GetCheckAttempt());
966         fields1->Set("max_check_attempts", checkable->GetMaxCheckAttempts());
967         fields1->Set("state_type", checkable->GetStateType());
968
969         double now = Utility::GetTime();
970         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
971
972         double end = now + execution_time;
973         std::pair<unsigned long, unsigned long> time_bag_end = CompatUtility::ConvertTimestamp(end);
974
975         fields1->Set("start_time", DbValue::FromTimestamp(time_bag.first));
976         fields1->Set("start_time_usec", time_bag.second);
977         fields1->Set("end_time", DbValue::FromTimestamp(time_bag_end.first));
978         fields1->Set("end_time_usec", time_bag_end.second);
979         fields1->Set("command_object_id", checkable->GetCheckCommand());
980         fields1->Set("command_args", Empty);
981         fields1->Set("command_line", cr->GetCommand());
982         fields1->Set("execution_time", Convert::ToString(execution_time));
983         fields1->Set("latency", Convert::ToString(Service::CalculateLatency(cr)));
984         fields1->Set("return_code", cr->GetExitStatus());
985         fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
986         fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
987         fields1->Set("perfdata", CompatUtility::GetCheckResultPerfdata(cr));
988
989         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
990
991         if (service) {
992                 fields1->Set("service_object_id", service);
993                 fields1->Set("state", service->GetState());
994         } else {
995                 fields1->Set("host_object_id", host);
996                 fields1->Set("state", host->GetState());
997         }
998
999         String node = IcingaApplication::GetInstance()->GetNodeName();
1000
1001         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1002         if (endpoint)
1003                 fields1->Set("endpoint_object_id", endpoint);
1004
1005         query1.Fields = fields1;
1006         DbObject::OnQuery(query1);
1007 }
1008
1009 /* eventhandlers */
1010 void DbEvents::AddEventHandlerHistory(const Checkable::Ptr& checkable)
1011 {
1012         Log(LogDebug, "db_ido", "add eventhandler history for '" + checkable->GetName() + "'");
1013
1014         double now = Utility::GetTime();
1015         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
1016
1017         DbQuery query1;
1018         query1.Table = "eventhandlers";
1019         query1.Type = DbQueryInsert;
1020         query1.Category = DbCatEventHandler;
1021
1022         Dictionary::Ptr fields1 = make_shared<Dictionary>();
1023
1024         Host::Ptr host;
1025         Service::Ptr service;
1026         tie(host, service) = GetHostService(checkable);
1027
1028         fields1->Set("eventhandler_type", service ? 1 : 0);
1029         fields1->Set("object_id", checkable);
1030         fields1->Set("state", service ? service->GetState() : host->GetState());
1031         fields1->Set("state_type", checkable->GetStateType());
1032
1033         fields1->Set("start_time", DbValue::FromTimestamp(time_bag.first));
1034         fields1->Set("start_time_usec", time_bag.second);
1035         fields1->Set("end_time", DbValue::FromTimestamp(time_bag.first));
1036         fields1->Set("end_time_usec", time_bag.second);
1037         fields1->Set("command_object_id", checkable->GetEventCommand());
1038
1039         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1040
1041         String node = IcingaApplication::GetInstance()->GetNodeName();
1042
1043         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1044         if (endpoint)
1045                 fields1->Set("endpoint_object_id", endpoint);
1046
1047         query1.Fields = fields1;
1048         DbObject::OnQuery(query1);
1049 }
1050
1051 /* externalcommands */
1052 void DbEvents::AddExternalCommandHistory(double time, const String& command, const std::vector<String>& arguments)
1053 {
1054         Log(LogDebug, "db_ido", "add external command history");
1055
1056         DbQuery query1;
1057         query1.Table = "externalcommands";
1058         query1.Type = DbQueryInsert;
1059         query1.Category = DbCatExternalCommand;
1060
1061         Dictionary::Ptr fields1 = make_shared<Dictionary>();
1062
1063         fields1->Set("entry_time", DbValue::FromTimestamp(static_cast<long>(time)));
1064         fields1->Set("command_type", CompatUtility::MapExternalCommandType(command));
1065         fields1->Set("command_name", command);
1066         fields1->Set("command_args", boost::algorithm::join(arguments, ";"));
1067
1068         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1069
1070         String node = IcingaApplication::GetInstance()->GetNodeName();
1071
1072         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1073         if (endpoint)
1074                 fields1->Set("endpoint_object_id", endpoint);
1075
1076         query1.Fields = fields1;
1077         DbObject::OnQuery(query1);
1078 }