]> granicus.if.org Git - icinga2/blob - lib/db_ido/dbevents.cpp
Remove the HostUnreachable state.
[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
600         Dictionary::Ptr vars_before = cr->GetVarsBefore();
601
602         if (vars_before) {
603                 long state_before = vars_before->Get("state");
604                 long stateType_before = vars_before->Get("state_type");
605                 long attempt_before = vars_before->Get("attempt");
606                 bool reachable_before = vars_before->Get("reachable");
607
608                 if (state_before == state_after && stateType_before == stateType_after &&
609                     attempt_before == attempt_after && reachable_before == reachable_after)
610                         return; /* Nothing changed, ignore this checkresult. */
611         }
612
613         LogEntryType type;
614         String output;
615
616         if (cr)
617                 output = CompatUtility::GetCheckResultOutput(cr);
618
619         Host::Ptr host;
620         Service::Ptr service;
621         tie(host, service) = GetHostService(checkable);
622
623         std::ostringstream msgbuf;
624
625         if (service) {
626                 msgbuf << "SERVICE ALERT: "
627                        << host->GetName() << ";"
628                        << service->GetShortName() << ";"
629                        << Service::StateToString(static_cast<ServiceState>(state_after)) << ";"
630                        << Service::StateTypeToString(static_cast<StateType>(stateType_after)) << ";"
631                        << attempt_after << ";"
632                        << output << ""
633                        << "";
634
635                 switch (service->GetState()) {
636                         case StateOK:
637                                 type = LogEntryTypeServiceOk;
638                                 break;
639                         case StateUnknown:
640                                 type = LogEntryTypeServiceUnknown;
641                                 break;
642                         case StateWarning:
643                                 type = LogEntryTypeServiceWarning;
644                                 break;
645                         case StateCritical:
646                                 type = LogEntryTypeServiceCritical;
647                                 break;
648                         default:
649                                 Log(LogCritical, "db_ido", "Unknown service state: " + Convert::ToString(state_after));
650                                 return;
651                 }
652         } else {
653                 String state = Host::StateToString(Host::CalculateState(static_cast<ServiceState>(state_after)));
654
655                 if (!reachable_after)
656                         state = "UNREACHABLE";
657
658                 msgbuf << "HOST ALERT: "
659                        << host->GetName() << ";"
660                        << state << ";"
661                        << Service::StateTypeToString(static_cast<StateType>(stateType_after)) << ";"
662                        << attempt_after << ";"
663                        << output << ""
664                        << "";
665
666                 switch (host->GetState()) {
667                         case HostUp:
668                                 type = LogEntryTypeHostUp;
669                                 break;
670                         case HostDown:
671                                 type = LogEntryTypeHostDown;
672                                 break;
673                         default:
674                                 Log(LogCritical, "db_ido", "Unknown host state: " + Convert::ToString(state_after));
675                                 return;
676                 }
677
678                 if (!reachable_after)
679                         type = LogEntryTypeHostUnreachable;
680         }
681
682         AddLogHistory(checkable, msgbuf.str(), type);
683 }
684
685 void DbEvents::AddTriggerDowntimeLogHistory(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
686 {
687         if (!downtime)
688                 return;
689
690         Host::Ptr host;
691         Service::Ptr service;
692         tie(host, service) = GetHostService(checkable);
693
694         std::ostringstream msgbuf;
695
696         if (service) {
697                 msgbuf << "SERVICE DOWNTIME ALERT: "
698                         << host->GetName() << ";"
699                         << service->GetShortName() << ";"
700                         << "STARTED" << "; "
701                         << "Service has entered a period of scheduled downtime."
702                         << "";
703         } else {
704                 msgbuf << "HOST DOWNTIME ALERT: "
705                         << host->GetName() << ";"
706                         << "STARTED" << "; "
707                         << "Service has entered a period of scheduled downtime."
708                         << "";
709         }
710
711         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
712 }
713
714 void DbEvents::AddRemoveDowntimeLogHistory(const Checkable::Ptr& checkable, const Downtime::Ptr& downtime)
715 {
716         if (!downtime)
717                 return;
718
719         String downtime_output;
720         String downtime_state_str;
721
722         if (downtime->GetWasCancelled()) {
723                 downtime_output = "Scheduled downtime for service has been cancelled.";
724                 downtime_state_str = "CANCELLED";
725         } else {
726                 downtime_output = "Service has exited from a period of scheduled downtime.";
727                 downtime_state_str = "STOPPED";
728         }
729
730         Host::Ptr host;
731         Service::Ptr service;
732         tie(host, service) = GetHostService(checkable);
733
734         std::ostringstream msgbuf;
735
736         if (service) {
737                 msgbuf << "SERVICE DOWNTIME ALERT: "
738                         << host->GetName() << ";"
739                         << service->GetShortName() << ";"
740                         << downtime_state_str << "; "
741                         << downtime_output
742                         << "";
743         } else {
744                 msgbuf << "HOST DOWNTIME ALERT: "
745                         << host->GetName() << ";"
746                         << downtime_state_str << "; "
747                         << downtime_output
748                         << "";
749         }
750
751         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
752 }
753
754 void DbEvents::AddNotificationSentLogHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user,
755     NotificationType notification_type, const CheckResult::Ptr& cr,
756     const String& author, const String& comment_text)
757 {
758         CheckCommand::Ptr commandObj = checkable->GetCheckCommand();
759
760         String check_command = "";
761         if (commandObj)
762                 check_command = commandObj->GetName();
763
764         String notification_type_str = Notification::NotificationTypeToString(notification_type);
765
766         String author_comment = "";
767         if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) {
768                 author_comment = ";" + author + ";" + comment_text;
769         }
770
771         if (!cr)
772                 return;
773
774         String output;
775
776         if (cr)
777                 output = CompatUtility::GetCheckResultOutput(cr);
778
779         Host::Ptr host;
780         Service::Ptr service;
781         tie(host, service) = GetHostService(checkable);
782
783         std::ostringstream msgbuf;
784
785         if (service) {
786                 msgbuf << "SERVICE NOTIFICATION: "
787                        << user->GetName() << ";"
788                        << host->GetName() << ";"
789                        << service->GetShortName() << ";"
790                        << notification_type_str << " "
791                        << "(" << Service::StateToString(service->GetState()) << ");"
792                        << check_command << ";"
793                        << output << author_comment
794                        << "";
795         } else {
796                 msgbuf << "HOST NOTIFICATION: "
797                        << user->GetName() << ";"
798                        << host->GetName() << ";"
799                        << notification_type_str << " "
800                        << "(" << Host::StateToString(host->GetState()) << ");"
801                        << check_command << ";"
802                        << output << author_comment
803                        << "";
804         }
805
806         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeHostNotification);
807 }
808
809 void DbEvents::AddFlappingLogHistory(const Checkable::Ptr& checkable, FlappingState flapping_state)
810 {
811         String flapping_state_str;
812         String flapping_output;
813
814         switch (flapping_state) {
815                 case FlappingStarted:
816                         flapping_output = "Service appears to have started flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change >= " + Convert::ToString(checkable->GetFlappingThreshold()) + "% threshold)";
817                         flapping_state_str = "STARTED";
818                         break;
819                 case FlappingStopped:
820                         flapping_output = "Service appears to have stopped flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change < " + Convert::ToString(checkable->GetFlappingThreshold()) + "% threshold)";
821                         flapping_state_str = "STOPPED";
822                         break;
823                 case FlappingDisabled:
824                         flapping_output = "Flap detection has been disabled";
825                         flapping_state_str = "DISABLED";
826                         break;
827                 default:
828                         Log(LogCritical, "db_ido", "Unknown flapping state: " + Convert::ToString(flapping_state));
829                         return;
830         }
831
832         Host::Ptr host;
833         Service::Ptr service;
834         tie(host, service) = GetHostService(checkable);
835
836         std::ostringstream msgbuf;
837
838         if (service) {
839                 msgbuf << "SERVICE FLAPPING ALERT: "
840                         << host->GetName() << ";"
841                         << service->GetShortName() << ";"
842                         << flapping_state_str << "; "
843                         << flapping_output
844                         << "";
845         } else {
846                 msgbuf << "HOST FLAPPING ALERT: "
847                         << host->GetName() << ";"
848                         << flapping_state_str << "; "
849                         << flapping_output
850                         << "";
851         }
852
853         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
854 }
855
856 void DbEvents::AddLogHistory(const Checkable::Ptr& checkable, String buffer, LogEntryType type)
857 {
858         Log(LogDebug, "db_ido", "add log entry history for '" + checkable->GetName() + "'");
859
860         double now = Utility::GetTime();
861         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
862
863         DbQuery query1;
864         query1.Table = "logentries";
865         query1.Type = DbQueryInsert;
866         query1.Category = DbCatLog;
867
868         Dictionary::Ptr fields1 = make_shared<Dictionary>();
869         fields1->Set("logentry_time", DbValue::FromTimestamp(time_bag.first));
870         fields1->Set("entry_time", DbValue::FromTimestamp(time_bag.first));
871         fields1->Set("entry_time_usec", time_bag.second);
872         fields1->Set("object_id", checkable); // added in 1.10 see #4754
873         fields1->Set("logentry_type", type);
874         fields1->Set("logentry_data", buffer);
875
876         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
877
878         String node = IcingaApplication::GetInstance()->GetNodeName();
879
880         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
881         if (endpoint)
882                 fields1->Set("endpoint_object_id", endpoint);
883
884         query1.Fields = fields1;
885         DbObject::OnQuery(query1);
886 }
887
888 /* flappinghistory */
889 void DbEvents::AddFlappingHistory(const Checkable::Ptr& checkable, FlappingState flapping_state)
890 {
891         Log(LogDebug, "db_ido", "add flapping history for '" + checkable->GetName() + "'");
892
893         double now = Utility::GetTime();
894         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
895
896         DbQuery query1;
897         query1.Table = "flappinghistory";
898         query1.Type = DbQueryInsert;
899         query1.Category = DbCatFlapping;
900
901         Dictionary::Ptr fields1 = make_shared<Dictionary>();
902
903         fields1->Set("event_time", DbValue::FromTimestamp(time_bag.first));
904         fields1->Set("event_time_usec", time_bag.second);
905
906         switch (flapping_state) {
907                 case FlappingStarted:
908                         fields1->Set("event_type", 1000);
909                         break;
910                 case FlappingStopped:
911                         fields1->Set("event_type", 1001);
912                         fields1->Set("reason_type", 1);
913                         break;
914                 case FlappingDisabled:
915                         fields1->Set("event_type", 1001);
916                         fields1->Set("reason_type", 2);
917                         break;
918                 default:
919                         Log(LogDebug, "db_ido", "Unhandled flapping state: " + Convert::ToString(flapping_state));
920                         return;
921         }
922
923         Host::Ptr host;
924         Service::Ptr service;
925         tie(host, service) = GetHostService(checkable);
926
927         fields1->Set("flapping_type", service ? 1 : 0);
928         fields1->Set("object_id", checkable);
929         fields1->Set("percent_state_change", checkable->GetFlappingCurrent());
930         fields1->Set("low_threshold", checkable->GetFlappingThreshold());
931         fields1->Set("high_threshold", checkable->GetFlappingThreshold());
932
933         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
934
935         String node = IcingaApplication::GetInstance()->GetNodeName();
936
937         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
938         if (endpoint)
939                 fields1->Set("endpoint_object_id", endpoint);
940
941         query1.Fields = fields1;
942         DbObject::OnQuery(query1);
943 }
944
945 /* servicechecks */
946 void DbEvents::AddServiceCheckHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
947 {
948         if (!cr)
949                 return;
950
951         Log(LogDebug, "db_ido", "add service check history for '" + checkable->GetName() + "'");
952
953         Host::Ptr host;
954         Service::Ptr service;
955         tie(host, service) = GetHostService(checkable);
956
957         std::ostringstream msgbuf;
958
959         DbQuery query1;
960         query1.Table = service ? "servicechecks" : "hostchecks";
961         query1.Type = DbQueryInsert;
962         query1.Category = DbCatCheck;
963
964         Dictionary::Ptr fields1 = make_shared<Dictionary>();
965         double execution_time = Service::CalculateExecutionTime(cr);
966
967         fields1->Set("check_type", CompatUtility::GetCheckableCheckType(checkable));
968         fields1->Set("current_check_attempt", checkable->GetCheckAttempt());
969         fields1->Set("max_check_attempts", checkable->GetMaxCheckAttempts());
970         fields1->Set("state_type", checkable->GetStateType());
971
972         double now = Utility::GetTime();
973         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
974
975         double end = now + execution_time;
976         std::pair<unsigned long, unsigned long> time_bag_end = CompatUtility::ConvertTimestamp(end);
977
978         fields1->Set("start_time", DbValue::FromTimestamp(time_bag.first));
979         fields1->Set("start_time_usec", time_bag.second);
980         fields1->Set("end_time", DbValue::FromTimestamp(time_bag_end.first));
981         fields1->Set("end_time_usec", time_bag_end.second);
982         fields1->Set("command_object_id", checkable->GetCheckCommand());
983         fields1->Set("command_args", Empty);
984         fields1->Set("command_line", cr->GetCommand());
985         fields1->Set("execution_time", Convert::ToString(execution_time));
986         fields1->Set("latency", Convert::ToString(Service::CalculateLatency(cr)));
987         fields1->Set("return_code", cr->GetExitStatus());
988         fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
989         fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
990         fields1->Set("perfdata", CompatUtility::GetCheckResultPerfdata(cr));
991
992         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
993
994         if (service) {
995                 fields1->Set("service_object_id", service);
996                 fields1->Set("state", service->GetState());
997         } else {
998                 fields1->Set("host_object_id", host);
999                 fields1->Set("state", host->GetState());
1000         }
1001
1002         String node = IcingaApplication::GetInstance()->GetNodeName();
1003
1004         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1005         if (endpoint)
1006                 fields1->Set("endpoint_object_id", endpoint);
1007
1008         query1.Fields = fields1;
1009         DbObject::OnQuery(query1);
1010 }
1011
1012 /* eventhandlers */
1013 void DbEvents::AddEventHandlerHistory(const Checkable::Ptr& checkable)
1014 {
1015         Log(LogDebug, "db_ido", "add eventhandler history for '" + checkable->GetName() + "'");
1016
1017         double now = Utility::GetTime();
1018         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
1019
1020         DbQuery query1;
1021         query1.Table = "eventhandlers";
1022         query1.Type = DbQueryInsert;
1023         query1.Category = DbCatEventHandler;
1024
1025         Dictionary::Ptr fields1 = make_shared<Dictionary>();
1026
1027         Host::Ptr host;
1028         Service::Ptr service;
1029         tie(host, service) = GetHostService(checkable);
1030
1031         fields1->Set("eventhandler_type", service ? 1 : 0);
1032         fields1->Set("object_id", checkable);
1033         fields1->Set("state", service ? service->GetState() : host->GetState());
1034         fields1->Set("state_type", checkable->GetStateType());
1035
1036         fields1->Set("start_time", DbValue::FromTimestamp(time_bag.first));
1037         fields1->Set("start_time_usec", time_bag.second);
1038         fields1->Set("end_time", DbValue::FromTimestamp(time_bag.first));
1039         fields1->Set("end_time_usec", time_bag.second);
1040         fields1->Set("command_object_id", checkable->GetEventCommand());
1041
1042         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1043
1044         String node = IcingaApplication::GetInstance()->GetNodeName();
1045
1046         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1047         if (endpoint)
1048                 fields1->Set("endpoint_object_id", endpoint);
1049
1050         query1.Fields = fields1;
1051         DbObject::OnQuery(query1);
1052 }
1053
1054 /* externalcommands */
1055 void DbEvents::AddExternalCommandHistory(double time, const String& command, const std::vector<String>& arguments)
1056 {
1057         Log(LogDebug, "db_ido", "add external command history");
1058
1059         DbQuery query1;
1060         query1.Table = "externalcommands";
1061         query1.Type = DbQueryInsert;
1062         query1.Category = DbCatExternalCommand;
1063
1064         Dictionary::Ptr fields1 = make_shared<Dictionary>();
1065
1066         fields1->Set("entry_time", DbValue::FromTimestamp(static_cast<long>(time)));
1067         fields1->Set("command_type", CompatUtility::MapExternalCommandType(command));
1068         fields1->Set("command_name", command);
1069         fields1->Set("command_args", boost::algorithm::join(arguments, ";"));
1070
1071         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1072
1073         String node = IcingaApplication::GetInstance()->GetNodeName();
1074
1075         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1076         if (endpoint)
1077                 fields1->Set("endpoint_object_id", endpoint);
1078
1079         query1.Fields = fields1;
1080         DbObject::OnQuery(query1);
1081 }