]> granicus.if.org Git - icinga2/blob - lib/db_ido/dbevents.cpp
Fix commenthistory
[icinga2] / lib / db_ido / dbevents.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/)  *
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.hpp"
21 #include "db_ido/dbtype.hpp"
22 #include "db_ido/dbvalue.hpp"
23 #include "base/convert.hpp"
24 #include "base/objectlock.hpp"
25 #include "base/initialize.hpp"
26 #include "base/configtype.hpp"
27 #include "base/utility.hpp"
28 #include "base/logger.hpp"
29 #include "remote/endpoint.hpp"
30 #include "icinga/notification.hpp"
31 #include "icinga/checkcommand.hpp"
32 #include "icinga/eventcommand.hpp"
33 #include "icinga/externalcommandprocessor.hpp"
34 #include "icinga/compatutility.hpp"
35 #include "icinga/icingaapplication.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         Comment::OnCommentAdded.connect(boost::bind(&DbEvents::AddComment, _1));
46         Comment::OnCommentRemoved.connect(boost::bind(&DbEvents::RemoveComment, _1));
47         Downtime::OnDowntimeAdded.connect(boost::bind(&DbEvents::AddDowntime, _1));
48         Downtime::OnDowntimeRemoved.connect(boost::bind(&DbEvents::RemoveDowntime, _1));
49         Downtime::OnDowntimeTriggered.connect(boost::bind(&DbEvents::TriggerDowntime, _1));
50         Checkable::OnAcknowledgementSet.connect(boost::bind(&DbEvents::AddAcknowledgement, _1, _4));
51         Checkable::OnAcknowledgementCleared.connect(boost::bind(&DbEvents::RemoveAcknowledgement, _1));
52
53         Checkable::OnNextCheckUpdated.connect(boost::bind(&DbEvents::NextCheckUpdatedHandler, _1));
54         Checkable::OnFlappingChanged.connect(boost::bind(&DbEvents::FlappingChangedHandler, _1));
55         Checkable::OnNotificationSentToAllUsers.connect(boost::bind(&DbEvents::LastNotificationChangedHandler, _1, _2));
56
57         Checkable::OnEnableActiveChecksChanged.connect(boost::bind(&DbEvents::EnableActiveChecksChangedHandler, _1));
58         Checkable::OnEnablePassiveChecksChanged.connect(boost::bind(&DbEvents::EnablePassiveChecksChangedHandler, _1));
59         Checkable::OnEnableNotificationsChanged.connect(boost::bind(&DbEvents::EnableNotificationsChangedHandler, _1));
60         Checkable::OnEnablePerfdataChanged.connect(boost::bind(&DbEvents::EnablePerfdataChangedHandler, _1));
61         Checkable::OnEnableFlappingChanged.connect(boost::bind(&DbEvents::EnableFlappingChangedHandler, _1));
62
63         Checkable::OnReachabilityChanged.connect(boost::bind(&DbEvents::ReachabilityChangedHandler, _1, _2, _3));
64
65         /* History */
66         Comment::OnCommentAdded.connect(boost::bind(&DbEvents::AddCommentHistory, _1));
67         Downtime::OnDowntimeAdded.connect(boost::bind(&DbEvents::AddDowntimeHistory, _1));
68         Checkable::OnAcknowledgementSet.connect(boost::bind(&DbEvents::AddAcknowledgementHistory, _1, _2, _3, _4, _5, _6));
69
70         Checkable::OnNotificationSentToAllUsers.connect(boost::bind(&DbEvents::AddNotificationHistory, _1, _2, _3, _4, _5, _6, _7));
71
72         Checkable::OnStateChange.connect(boost::bind(&DbEvents::AddStateChangeHistory, _1, _2, _3));
73
74         Checkable::OnNewCheckResult.connect(boost::bind(&DbEvents::AddCheckResultLogHistory, _1, _2));
75         Checkable::OnNotificationSentToUser.connect(boost::bind(&DbEvents::AddNotificationSentLogHistory, _1, _2, _3, _4, _5, _6, _7));
76         Checkable::OnFlappingChanged.connect(boost::bind(&DbEvents::AddFlappingChangedLogHistory, _1));
77         Checkable::OnEnableFlappingChanged.connect(boost::bind(&DbEvents::AddEnableFlappingChangedLogHistory, _1));
78         Downtime::OnDowntimeTriggered.connect(boost::bind(&DbEvents::AddTriggerDowntimeLogHistory, _1));
79         Downtime::OnDowntimeRemoved.connect(boost::bind(&DbEvents::AddRemoveDowntimeLogHistory, _1));
80
81         Checkable::OnFlappingChanged.connect(boost::bind(&DbEvents::AddFlappingChangedHistory, _1));
82         Checkable::OnEnableFlappingChanged.connect(boost::bind(&DbEvents::AddEnableFlappingChangedHistory, _1));
83         Checkable::OnNewCheckResult.connect(boost::bind(&DbEvents::AddCheckableCheckHistory, _1, _2));
84
85         Checkable::OnEventCommandExecuted.connect(boost::bind(&DbEvents::AddEventHandlerHistory, _1));
86
87         ExternalCommandProcessor::OnNewExternalCommand.connect(boost::bind(&DbEvents::AddExternalCommandHistory, _1, _2, _3));
88 }
89
90 /* check events */
91 void DbEvents::NextCheckUpdatedHandler(const Checkable::Ptr& checkable)
92 {
93         Host::Ptr host;
94         Service::Ptr service;
95         tie(host, service) = GetHostService(checkable);
96
97         DbQuery query1;
98         if (service)
99                 query1.Table = "servicestatus";
100         else
101                 query1.Table = "hoststatus";
102
103         query1.Type = DbQueryUpdate;
104         query1.Category = DbCatState;
105         query1.StatusUpdate = true;
106         query1.Object = DbObject::GetOrCreateByObject(checkable);
107
108         Dictionary::Ptr fields1 = new Dictionary();
109         fields1->Set("next_check", DbValue::FromTimestamp(checkable->GetNextCheck()));
110
111         query1.Fields = fields1;
112
113         query1.WhereCriteria = new Dictionary();
114         if (service)
115                 query1.WhereCriteria->Set("service_object_id", service);
116         else
117                 query1.WhereCriteria->Set("host_object_id", host);
118
119         DbObject::OnQuery(query1);
120 }
121
122 void DbEvents::FlappingChangedHandler(const Checkable::Ptr& checkable)
123 {
124         Host::Ptr host;
125         Service::Ptr service;
126         tie(host, service) = GetHostService(checkable);
127
128         DbQuery query1;
129         if (service)
130                 query1.Table = "servicestatus";
131         else
132                 query1.Table = "hoststatus";
133
134         query1.Type = DbQueryUpdate;
135         query1.Category = DbCatState;
136         query1.StatusUpdate = true;
137         query1.Object = DbObject::GetOrCreateByObject(checkable);
138
139         Dictionary::Ptr fields1 = new Dictionary();
140         fields1->Set("is_flapping", CompatUtility::GetCheckableIsFlapping(checkable));
141         fields1->Set("percent_state_change", CompatUtility::GetCheckablePercentStateChange(checkable));
142
143         query1.Fields = fields1;
144
145         query1.WhereCriteria = new Dictionary();
146         if (service)
147                 query1.WhereCriteria->Set("service_object_id", service);
148         else
149                 query1.WhereCriteria->Set("host_object_id", host);
150
151         query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
152
153         DbObject::OnQuery(query1);
154 }
155
156 void DbEvents::LastNotificationChangedHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable)
157 {
158         double now = Utility::GetTime();
159         std::pair<unsigned long, unsigned long> now_bag = CompatUtility::ConvertTimestamp(now);
160         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(notification->GetNextNotification());
161
162         Host::Ptr host;
163         Service::Ptr service;
164         tie(host, service) = GetHostService(checkable);
165
166         DbQuery query1;
167         if (service)
168                 query1.Table = "servicestatus";
169         else
170                 query1.Table = "hoststatus";
171
172         query1.Type = DbQueryUpdate;
173         query1.Category = DbCatState;
174         query1.StatusUpdate = true;
175         query1.Object = DbObject::GetOrCreateByObject(checkable);
176
177         Dictionary::Ptr fields1 = new Dictionary();
178         fields1->Set("last_notification", DbValue::FromTimestamp(now_bag.first));
179         fields1->Set("next_notification", DbValue::FromTimestamp(time_bag.first));
180         fields1->Set("current_notification_number", notification->GetNotificationNumber());
181
182         query1.Fields = fields1;
183
184         query1.WhereCriteria = new Dictionary();
185         if (service)
186                 query1.WhereCriteria->Set("service_object_id", service);
187         else
188                 query1.WhereCriteria->Set("host_object_id", host);
189
190         query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
191
192         DbObject::OnQuery(query1);
193 }
194
195 void DbEvents::ReachabilityChangedHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, std::set<Checkable::Ptr> children)
196 {
197         int is_reachable = 0;
198
199         if (cr->GetState() == ServiceOK)
200                 is_reachable = 1;
201
202         Log(LogDebug, "DbEvents")
203             << "Updating reachability for checkable '" << checkable->GetName() << "': " << (is_reachable ? "" : "not" ) << " reachable for " << children.size() << " children.";
204
205         for (const Checkable::Ptr& child : children) {
206                 Log(LogDebug, "DbEvents")
207                     << "Updating reachability for checkable '" << child->GetName() << "': " << (is_reachable ? "" : "not" ) << " reachable.";
208
209                 Host::Ptr host;
210                 Service::Ptr service;
211                 tie(host, service) = GetHostService(child);
212
213                 DbQuery query1;
214                 if (service)
215                         query1.Table = "servicestatus";
216                 else
217                         query1.Table = "hoststatus";
218
219                 query1.Type = DbQueryUpdate;
220                 query1.Category = DbCatState;
221                 query1.StatusUpdate = true;
222                 query1.Object = DbObject::GetOrCreateByObject(child);
223
224                 Dictionary::Ptr fields1 = new Dictionary();
225                 fields1->Set("is_reachable", is_reachable);
226
227                 query1.Fields = fields1;
228
229                 query1.WhereCriteria = new Dictionary();
230                 if (service)
231                         query1.WhereCriteria->Set("service_object_id", service);
232                 else
233                         query1.WhereCriteria->Set("host_object_id", host);
234
235                 query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
236
237                 DbObject::OnQuery(query1);
238         }
239 }
240
241 /* enable changed events */
242 void DbEvents::EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable)
243 {
244         EnableChangedHandlerInternal(checkable, "active_checks_enabled", checkable->GetEnableActiveChecks());
245 }
246
247 void DbEvents::EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable)
248 {
249         EnableChangedHandlerInternal(checkable, "passive_checks_enabled", checkable->GetEnablePassiveChecks());
250 }
251
252 void DbEvents::EnableNotificationsChangedHandler(const Checkable::Ptr& checkable)
253 {
254         EnableChangedHandlerInternal(checkable, "notifications_enabled", checkable->GetEnableNotifications());
255 }
256
257 void DbEvents::EnablePerfdataChangedHandler(const Checkable::Ptr& checkable)
258 {
259         EnableChangedHandlerInternal(checkable, "process_performance_data", checkable->GetEnablePerfdata());
260 }
261
262 void DbEvents::EnableFlappingChangedHandler(const Checkable::Ptr& checkable)
263 {
264         EnableChangedHandlerInternal(checkable, "flap_detection_enabled", checkable->GetEnableFlapping());
265 }
266
267 void DbEvents::EnableChangedHandlerInternal(const Checkable::Ptr& checkable, const String& fieldName, bool enabled)
268 {
269         Host::Ptr host;
270         Service::Ptr service;
271         tie(host, service) = GetHostService(checkable);
272
273         DbQuery query1;
274         if (service)
275                 query1.Table = "servicestatus";
276         else
277                 query1.Table = "hoststatus";
278
279         query1.Type = DbQueryUpdate;
280         query1.Category = DbCatState;
281         query1.StatusUpdate = true;
282         query1.Object = DbObject::GetOrCreateByObject(checkable);
283
284         Dictionary::Ptr fields1 = new Dictionary();
285         fields1->Set(fieldName, enabled);
286         query1.Fields = fields1;
287
288         query1.WhereCriteria = new Dictionary();
289         if (service)
290                 query1.WhereCriteria->Set("service_object_id", service);
291         else
292                 query1.WhereCriteria->Set("host_object_id", host);
293
294         query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
295
296         DbObject::OnQuery(query1);
297 }
298
299
300 /* comments */
301 void DbEvents::AddComments(const Checkable::Ptr& checkable)
302 {
303         std::set<Comment::Ptr> comments = checkable->GetComments();
304
305         std::vector<DbQuery> queries;
306
307         for (const Comment::Ptr& comment : comments) {
308                 AddCommentInternal(queries, comment, false);
309         }
310
311         DbObject::OnMultipleQueries(queries);
312 }
313
314 void DbEvents::AddComment(const Comment::Ptr& comment)
315 {
316         std::vector<DbQuery> queries;
317         AddCommentInternal(queries, comment, false);
318         DbObject::OnMultipleQueries(queries);
319 }
320
321 void DbEvents::AddCommentHistory(const Comment::Ptr& comment)
322 {
323         std::vector<DbQuery> queries;
324         AddCommentInternal(queries, comment, true);
325         DbObject::OnMultipleQueries(queries);
326 }
327
328 void DbEvents::AddCommentInternal(std::vector<DbQuery>& queries, const Comment::Ptr& comment, bool historical)
329 {
330         Checkable::Ptr checkable = comment->GetCheckable();
331
332         unsigned long entry_time = static_cast<long>(comment->GetEntryTime());
333         unsigned long entry_time_usec = (comment->GetEntryTime() - entry_time) * 1000 * 1000;
334
335         Dictionary::Ptr fields1 = new Dictionary();
336         fields1->Set("entry_time", DbValue::FromTimestamp(entry_time));
337         fields1->Set("entry_time_usec", entry_time_usec);
338         fields1->Set("entry_type", comment->GetEntryType());
339         fields1->Set("object_id", checkable);
340
341         if (checkable->GetReflectionType() == Host::TypeInstance) {
342                 fields1->Set("comment_type", 2);
343                 /* requires idoutils 1.10 schema fix */
344                 fields1->Set("internal_comment_id", comment->GetLegacyId());
345         } else if (checkable->GetReflectionType() == Service::TypeInstance) {
346                 fields1->Set("comment_type", 1);
347                 fields1->Set("internal_comment_id", comment->GetLegacyId());
348         } else {
349                 Log(LogDebug, "DbEvents", "unknown object type for adding comment.");
350                 return;
351         }
352
353         fields1->Set("name", comment->GetName());
354         fields1->Set("comment_time", DbValue::FromTimestamp(entry_time)); /* same as entry_time */
355         fields1->Set("author_name", comment->GetAuthor());
356         fields1->Set("comment_data", comment->GetText());
357         fields1->Set("is_persistent", comment->GetPersistent() ? 1 : 0);
358         fields1->Set("comment_source", 1); /* external */
359         fields1->Set("expires", (comment->GetExpireTime() > 0) ? 1 : 0);
360         fields1->Set("expiration_time", DbValue::FromTimestamp(comment->GetExpireTime()));
361         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
362
363         String node = IcingaApplication::GetInstance()->GetNodeName();
364
365         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
366         if (endpoint)
367                 fields1->Set("endpoint_object_id", endpoint);
368
369         DbQuery query1;
370         if (!historical) {
371                 query1.Table = "comments";
372                 query1.Type = DbQueryInsert | DbQueryUpdate;
373
374                 fields1->Set("session_token", 0); /* DbConnection class fills in real ID */
375
376                 query1.WhereCriteria = new Dictionary();
377                 query1.WhereCriteria->Set("object_id", checkable);
378                 query1.WhereCriteria->Set("comment_time", DbValue::FromTimestamp(entry_time));
379                 query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
380                 /* Match the unique constraint. */
381                 query1.WhereCriteria->Set("internal_comment_id", comment->GetLegacyId());
382         } else {
383                 query1.Table = "commenthistory";
384                 query1.Type = DbQueryInsert;
385         }
386         query1.Category = DbCatComment;
387         query1.Fields = fields1;
388
389         queries.push_back(query1);
390 }
391
392 void DbEvents::RemoveComment(const Comment::Ptr& comment)
393 {
394         std::vector<DbQuery> queries;
395         RemoveCommentInternal(queries, comment);
396         DbObject::OnMultipleQueries(queries);
397 }
398
399 void DbEvents::RemoveCommentInternal(std::vector<DbQuery>& queries, const Comment::Ptr& comment)
400 {
401         Checkable::Ptr checkable = comment->GetCheckable();
402
403         unsigned long entry_time = static_cast<long>(comment->GetEntryTime());
404
405         /* Status */
406         DbQuery query1;
407         query1.Table = "comments";
408         query1.Type = DbQueryDelete;
409         query1.Category = DbCatComment;
410         query1.WhereCriteria = new Dictionary();
411         query1.WhereCriteria->Set("object_id", checkable);
412         query1.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(entry_time));
413         query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
414         query1.WhereCriteria->Set("name", comment->GetName());
415         queries.push_back(query1);
416
417         /* History - update deletion time for service/host */
418         double now = Utility::GetTime();
419         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
420
421         DbQuery query2;
422         query2.Table = "commenthistory";
423         query2.Type = DbQueryUpdate;
424         query2.Category = DbCatComment;
425
426         Dictionary::Ptr fields2 = new Dictionary();
427         fields2->Set("deletion_time", DbValue::FromTimestamp(time_bag.first));
428         fields2->Set("deletion_time_usec", time_bag.second);
429         query2.Fields = fields2;
430
431         query2.WhereCriteria = new Dictionary();
432         query2.WhereCriteria->Set("object_id", checkable);
433         query2.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(entry_time));
434         query2.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
435         query2.WhereCriteria->Set("name", comment->GetName());
436         queries.push_back(query2);
437 }
438
439 /* downtimes */
440 void DbEvents::AddDowntimes(const Checkable::Ptr& checkable)
441 {
442         std::set<Downtime::Ptr> downtimes = checkable->GetDowntimes();
443
444         std::vector<DbQuery> queries;
445
446         for (const Downtime::Ptr& downtime : downtimes) {
447                 AddDowntimeInternal(queries, downtime, false);
448         }
449
450         DbObject::OnMultipleQueries(queries);
451 }
452
453 void DbEvents::AddDowntime(const Downtime::Ptr& downtime)
454 {
455         std::vector<DbQuery> queries;
456         AddDowntimeInternal(queries, downtime, false);
457         DbObject::OnMultipleQueries(queries);
458 }
459
460 void DbEvents::AddDowntimeHistory(const Downtime::Ptr& downtime)
461 {
462         std::vector<DbQuery> queries;
463         AddDowntimeInternal(queries, downtime, true);
464         DbObject::OnMultipleQueries(queries);
465 }
466
467 void DbEvents::AddDowntimeInternal(std::vector<DbQuery>& queries, const Downtime::Ptr& downtime, bool historical)
468 {
469         Checkable::Ptr checkable = downtime->GetCheckable();
470
471         Dictionary::Ptr fields1 = new Dictionary();
472         fields1->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
473         fields1->Set("object_id", checkable);
474
475         if (checkable->GetReflectionType() == Host::TypeInstance) {
476                 fields1->Set("downtime_type", 2);
477                 /* requires idoutils 1.10 schema fix */
478                 fields1->Set("internal_downtime_id", downtime->GetLegacyId());
479         } else if (checkable->GetReflectionType() == Service::TypeInstance) {
480                 fields1->Set("downtime_type", 1);
481                 fields1->Set("internal_downtime_id", downtime->GetLegacyId());
482         } else {
483                 Log(LogDebug, "DbEvents", "unknown object type for adding downtime.");
484                 return;
485         }
486
487         fields1->Set("author_name", downtime->GetAuthor());
488         fields1->Set("comment_data", downtime->GetComment());
489         fields1->Set("triggered_by_id", Downtime::GetByName(downtime->GetTriggeredBy()));
490         fields1->Set("is_fixed", downtime->GetFixed() ? 1 : 0);
491         fields1->Set("duration", downtime->GetDuration());
492         fields1->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
493         fields1->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
494         fields1->Set("name", downtime->GetName());
495
496         /* flexible downtimes are started at trigger time */
497         if (downtime->GetFixed()) {
498                 std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(downtime->GetStartTime());
499                 fields1->Set("actual_start_time", DbValue::FromTimestamp(time_bag.first));
500                 fields1->Set("actual_start_time_usec", time_bag.second);
501                 fields1->Set("was_started", ((downtime->GetStartTime() <= Utility::GetTime()) ? 1 : 0));
502         }
503
504         fields1->Set("is_in_effect", (downtime->IsInEffect() ? 1 : 0));
505         fields1->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()));
506         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
507
508         String node = IcingaApplication::GetInstance()->GetNodeName();
509
510         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
511         if (endpoint)
512                 fields1->Set("endpoint_object_id", endpoint);
513
514         DbQuery query1;
515
516         if (!historical) {
517                 query1.Table = "scheduleddowntime";
518                 query1.Type = DbQueryInsert | DbQueryUpdate;
519
520                 fields1->Set("session_token", 0); /* DbConnection class fills in real ID */
521
522                 query1.WhereCriteria = new Dictionary();
523                 query1.WhereCriteria->Set("object_id", checkable);
524                 query1.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
525                 query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
526                 /* Match the unique constraint. */
527                 query1.WhereCriteria->Set("internal_downtime_id", downtime->GetLegacyId());
528         } else {
529                 query1.Table = "downtimehistory";
530                 query1.Type = DbQueryInsert;
531         }
532
533         query1.Category = DbCatDowntime;
534         query1.Fields = fields1;
535
536         queries.push_back(query1);
537
538         /* host/service status */
539         if (!historical) {
540                 Host::Ptr host;
541                 Service::Ptr service;
542                 tie(host, service) = GetHostService(checkable);
543
544                 DbQuery query2;
545                 if (service)
546                         query2.Table = "servicestatus";
547                 else
548                         query2.Table = "hoststatus";
549
550                 query2.Type = DbQueryUpdate;
551                 query2.Category = DbCatState;
552                 query2.StatusUpdate = true;
553                 query2.Object = DbObject::GetOrCreateByObject(checkable);
554
555                 Dictionary::Ptr fields2 = new Dictionary();
556                 fields2->Set("scheduled_downtime_depth", checkable->GetDowntimeDepth());
557
558                 query2.Fields = fields2;
559
560                 query2.WhereCriteria = new Dictionary();
561                 if (service)
562                         query2.WhereCriteria->Set("service_object_id", service);
563                 else
564                         query2.WhereCriteria->Set("host_object_id", host);
565
566                 query2.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
567
568                 queries.push_back(query2);
569         }
570 }
571
572 void DbEvents::RemoveDowntime(const Downtime::Ptr& downtime)
573 {
574         std::vector<DbQuery> queries;
575         RemoveDowntimeInternal(queries, downtime);
576         DbObject::OnMultipleQueries(queries);
577 }
578
579 void DbEvents::RemoveDowntimeInternal(std::vector<DbQuery>& queries, const Downtime::Ptr& downtime)
580 {
581         Checkable::Ptr checkable = downtime->GetCheckable();
582
583         /* Status */
584         DbQuery query1;
585         query1.Table = "scheduleddowntime";
586         query1.Type = DbQueryDelete;
587         query1.Category = DbCatDowntime;
588         query1.WhereCriteria = new Dictionary();
589
590         query1.WhereCriteria->Set("object_id", checkable);
591         query1.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
592         query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
593         query1.WhereCriteria->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
594         query1.WhereCriteria->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
595         query1.WhereCriteria->Set("name", downtime->GetName());
596         queries.push_back(query1);
597
598         /* History - update actual_end_time, was_cancelled for service (and host in case) */
599         double now = Utility::GetTime();
600         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
601
602         DbQuery query3;
603         query3.Table = "downtimehistory";
604         query3.Type = DbQueryUpdate;
605         query3.Category = DbCatDowntime;
606
607         Dictionary::Ptr fields3 = new Dictionary();
608         fields3->Set("was_cancelled", downtime->GetWasCancelled() ? 1 : 0);
609
610         if (downtime->GetFixed() || (!downtime->GetFixed() && downtime->GetTriggerTime() > 0)) {
611                 fields3->Set("actual_end_time", DbValue::FromTimestamp(time_bag.first));
612                 fields3->Set("actual_end_time_usec", time_bag.second);
613         }
614
615         fields3->Set("is_in_effect", 0);
616         query3.Fields = fields3;
617
618         query3.WhereCriteria = new Dictionary();
619         query3.WhereCriteria->Set("object_id", checkable);
620         query3.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
621         query3.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
622         query3.WhereCriteria->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
623         query3.WhereCriteria->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
624         query3.WhereCriteria->Set("name", downtime->GetName());
625
626         queries.push_back(query3);
627
628         /* host/service status */
629         Host::Ptr host;
630         Service::Ptr service;
631         tie(host, service) = GetHostService(checkable);
632
633         DbQuery query4;
634         if (service)
635                 query4.Table = "servicestatus";
636         else
637                 query4.Table = "hoststatus";
638
639         query4.Type = DbQueryUpdate;
640         query4.Category = DbCatState;
641         query4.StatusUpdate = true;
642         query4.Object = DbObject::GetOrCreateByObject(checkable);
643
644         Dictionary::Ptr fields4 = new Dictionary();
645         fields4->Set("scheduled_downtime_depth", checkable->GetDowntimeDepth());
646
647         query4.Fields = fields4;
648
649         query4.WhereCriteria = new Dictionary();
650         if (service)
651                 query4.WhereCriteria->Set("service_object_id", service);
652         else
653                 query4.WhereCriteria->Set("host_object_id", host);
654
655         query4.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
656
657         queries.push_back(query4);
658 }
659
660 void DbEvents::TriggerDowntime(const Downtime::Ptr& downtime)
661 {
662         Checkable::Ptr checkable = downtime->GetCheckable();
663
664         double now = Utility::GetTime();
665         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
666
667         /* Status */
668         DbQuery query1;
669         query1.Table = "scheduleddowntime";
670         query1.Type = DbQueryUpdate;
671         query1.Category = DbCatDowntime;
672
673         Dictionary::Ptr fields1 = new Dictionary();
674         fields1->Set("was_started", 1);
675         fields1->Set("actual_start_time", DbValue::FromTimestamp(time_bag.first));
676         fields1->Set("actual_start_time_usec", time_bag.second);
677         fields1->Set("is_in_effect", (downtime->IsInEffect() ? 1 : 0));
678         fields1->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()));
679         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
680
681         query1.WhereCriteria = new Dictionary();
682         query1.WhereCriteria->Set("object_id", checkable);
683         query1.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
684         query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
685         query1.WhereCriteria->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
686         query1.WhereCriteria->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
687         query1.WhereCriteria->Set("name", downtime->GetName());
688
689         query1.Fields = fields1;
690         DbObject::OnQuery(query1);
691
692         /* History - downtime was started for service (and host in case) */
693         DbQuery query3;
694         query3.Table = "downtimehistory";
695         query3.Type = DbQueryUpdate;
696         query3.Category = DbCatDowntime;
697
698         Dictionary::Ptr fields3 = new Dictionary();
699         fields3->Set("was_started", 1);
700         fields3->Set("is_in_effect", 1);
701         fields3->Set("actual_start_time", DbValue::FromTimestamp(time_bag.first));
702         fields3->Set("actual_start_time_usec", time_bag.second);
703         fields3->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()));
704         query3.Fields = fields3;
705
706         query3.WhereCriteria = query1.WhereCriteria;
707
708         DbObject::OnQuery(query3);
709
710         /* host/service status */
711         Host::Ptr host;
712         Service::Ptr service;
713         tie(host, service) = GetHostService(checkable);
714
715         DbQuery query4;
716         if (service)
717                 query4.Table = "servicestatus";
718         else
719                 query4.Table = "hoststatus";
720
721         query4.Type = DbQueryUpdate;
722         query4.Category = DbCatState;
723         query4.StatusUpdate = true;
724         query4.Object = DbObject::GetOrCreateByObject(checkable);
725
726         Dictionary::Ptr fields4 = new Dictionary();
727         fields4->Set("scheduled_downtime_depth", checkable->GetDowntimeDepth());
728
729         query4.Fields = fields4;
730
731         query4.WhereCriteria = new Dictionary();
732         if (service)
733                 query4.WhereCriteria->Set("service_object_id", service);
734         else
735                 query4.WhereCriteria->Set("host_object_id", host);
736
737         query4.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
738
739         DbObject::OnQuery(query4);
740 }
741
742 /* acknowledgements */
743 void DbEvents::AddAcknowledgementHistory(const Checkable::Ptr& checkable, const String& author, const String& comment,
744     AcknowledgementType type, bool notify, double expiry)
745 {
746         Log(LogDebug, "DbEvents")
747             << "add acknowledgement history for '" << checkable->GetName() << "'";
748
749         double now = Utility::GetTime();
750         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
751
752         unsigned long end_time = static_cast<long>(expiry);
753
754         DbQuery query1;
755         query1.Table = "acknowledgements";
756         query1.Type = DbQueryInsert;
757         query1.Category = DbCatAcknowledgement;
758
759         Host::Ptr host;
760         Service::Ptr service;
761         tie(host, service) = GetHostService(checkable);
762
763         Dictionary::Ptr fields1 = new Dictionary();
764         fields1->Set("entry_time", DbValue::FromTimestamp(time_bag.first));
765         fields1->Set("entry_time_usec", time_bag.second);
766         fields1->Set("acknowledgement_type", type);
767         fields1->Set("object_id", checkable);
768         fields1->Set("author_name", author);
769         fields1->Set("comment_data", comment);
770         fields1->Set("persistent_comment", 1); //always persistent
771         fields1->Set("notify_contacts", notify ? 1 : 0);
772         fields1->Set("is_sticky", type == AcknowledgementSticky ? 1 : 0);
773         fields1->Set("end_time", DbValue::FromTimestamp(end_time));
774         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
775
776         if (service) {
777                 fields1->Set("state", service->GetState());
778         } else {
779                 fields1->Set("state", CompatUtility::GetHostCurrentState(host));
780         }
781
782         String node = IcingaApplication::GetInstance()->GetNodeName();
783
784         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
785         if (endpoint)
786                 fields1->Set("endpoint_object_id", endpoint);
787
788         query1.Fields = fields1;
789         DbObject::OnQuery(query1);
790 }
791
792 void DbEvents::AddAcknowledgement(const Checkable::Ptr& checkable, AcknowledgementType type)
793 {
794         Log(LogDebug, "DbEvents")
795             << "add acknowledgement for '" << checkable->GetName() << "'";
796
797         AddAcknowledgementInternal(checkable, type, true);
798 }
799
800 void DbEvents::RemoveAcknowledgement(const Checkable::Ptr& checkable)
801 {
802         Log(LogDebug, "DbEvents")
803             << "remove acknowledgement for '" << checkable->GetName() << "'";
804
805         AddAcknowledgementInternal(checkable, AcknowledgementNone, false);
806 }
807
808 void DbEvents::AddAcknowledgementInternal(const Checkable::Ptr& checkable, AcknowledgementType type, bool add)
809 {
810         Host::Ptr host;
811         Service::Ptr service;
812         tie(host, service) = GetHostService(checkable);
813
814         DbQuery query1;
815         if (service)
816                 query1.Table = "servicestatus";
817         else
818                 query1.Table = "hoststatus";
819
820         query1.Type = DbQueryUpdate;
821         query1.Category = DbCatState;
822         query1.StatusUpdate = true;
823         query1.Object = DbObject::GetOrCreateByObject(checkable);
824
825         Dictionary::Ptr fields1 = new Dictionary();
826         fields1->Set("acknowledgement_type", type);
827         fields1->Set("problem_has_been_acknowledged", add ? 1 : 0);
828         query1.Fields = fields1;
829
830         query1.WhereCriteria = new Dictionary();
831         if (service)
832                 query1.WhereCriteria->Set("service_object_id", service);
833         else
834                 query1.WhereCriteria->Set("host_object_id", host);
835
836         query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
837
838         DbObject::OnQuery(query1);
839 }
840
841 /* notifications */
842 void DbEvents::AddNotificationHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set<User::Ptr>& users, NotificationType type,
843     const CheckResult::Ptr& cr, const String& author, const String& text)
844 {
845         Log(LogDebug, "DbEvents")
846             << "add notification history for '" << checkable->GetName() << "'";
847
848         /* start and end happen at the same time */
849         double now = Utility::GetTime();
850         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
851
852         DbQuery query1;
853         query1.Table = "notifications";
854         query1.Type = DbQueryInsert;
855         query1.Category = DbCatNotification;
856         query1.NotificationInsertID = new DbValue(DbValueObjectInsertID, -1);
857
858         Host::Ptr host;
859         Service::Ptr service;
860         tie(host, service) = GetHostService(checkable);
861
862         Dictionary::Ptr fields1 = new Dictionary();
863         fields1->Set("notification_type", 1); /* service */
864         fields1->Set("notification_reason", CompatUtility::MapNotificationReasonType(type));
865         fields1->Set("object_id", checkable);
866         fields1->Set("start_time", DbValue::FromTimestamp(time_bag.first));
867         fields1->Set("start_time_usec", time_bag.second);
868         fields1->Set("end_time", DbValue::FromTimestamp(time_bag.first));
869         fields1->Set("end_time_usec", time_bag.second);
870
871         if (service) {
872                 fields1->Set("state", service->GetState());
873         } else {
874                 fields1->Set("state", CompatUtility::GetHostCurrentState(host));
875         }
876
877         if (cr) {
878                 fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
879                 fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
880         }
881
882         fields1->Set("escalated", 0);
883         fields1->Set("contacts_notified", static_cast<long>(users.size()));
884         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
885
886         String node = IcingaApplication::GetInstance()->GetNodeName();
887
888         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
889         if (endpoint)
890                 fields1->Set("endpoint_object_id", endpoint);
891
892         query1.Fields = fields1;
893         DbObject::OnQuery(query1);
894
895         std::vector<DbQuery> queries;
896
897         for (const User::Ptr& user : users) {
898                 Log(LogDebug, "DbEvents")
899                     << "add contact notification history for service '" << checkable->GetName() << "' and user '" << user->GetName() << "'.";
900
901                 DbQuery query2;
902                 query2.Table = "contactnotifications";
903                 query2.Type = DbQueryInsert;
904                 query2.Category = DbCatNotification;
905
906                 Dictionary::Ptr fields2 = new Dictionary();
907                 fields2->Set("contact_object_id", user);
908                 fields2->Set("start_time", DbValue::FromTimestamp(time_bag.first));
909                 fields2->Set("start_time_usec", time_bag.second);
910                 fields2->Set("end_time", DbValue::FromTimestamp(time_bag.first));
911                 fields2->Set("end_time_usec", time_bag.second);
912
913                 fields2->Set("notification_id", query1.NotificationInsertID);
914                 fields2->Set("instance_id", 0); /* DbConnection class fills in real ID */
915
916                 query2.Fields = fields2;
917                 queries.push_back(query2);
918         }
919
920         DbObject::OnMultipleQueries(queries);
921 }
922
923 /* statehistory */
924 void DbEvents::AddStateChangeHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type)
925 {
926         Log(LogDebug, "DbEvents")
927             << "add state change history for '" << checkable->GetName() << "'";
928
929         double ts = cr->GetExecutionEnd();
930         std::pair<unsigned long, unsigned long> state_time_bag = CompatUtility::ConvertTimestamp(ts);
931
932         DbQuery query1;
933         query1.Table = "statehistory";
934         query1.Type = DbQueryInsert;
935         query1.Category = DbCatStateHistory;
936
937         Host::Ptr host;
938         Service::Ptr service;
939         tie(host, service) = GetHostService(checkable);
940
941         Dictionary::Ptr fields1 = new Dictionary();
942         fields1->Set("state_time", DbValue::FromTimestamp(state_time_bag.first));
943         fields1->Set("state_time_usec", state_time_bag.second);
944         fields1->Set("object_id", checkable);
945         fields1->Set("state_change", 1); /* service */
946         fields1->Set("state_type", checkable->GetStateType());
947         fields1->Set("current_check_attempt", checkable->GetCheckAttempt());
948         fields1->Set("max_check_attempts", checkable->GetMaxCheckAttempts());
949
950         if (service) {
951                 fields1->Set("state", service->GetState());
952                 fields1->Set("last_state", service->GetLastState());
953                 fields1->Set("last_hard_state", service->GetLastHardState());
954         } else {
955                 fields1->Set("state", CompatUtility::GetHostCurrentState(host));
956                 fields1->Set("last_state", host->GetLastState());
957                 fields1->Set("last_hard_state", host->GetLastHardState());
958         }
959
960         if (cr) {
961                 fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
962                 fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
963                 fields1->Set("check_source", cr->GetCheckSource());
964         }
965
966         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
967
968         String node = IcingaApplication::GetInstance()->GetNodeName();
969
970         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
971         if (endpoint)
972                 fields1->Set("endpoint_object_id", endpoint);
973
974         query1.Fields = fields1;
975         DbObject::OnQuery(query1);
976 }
977
978 /* logentries */
979 void DbEvents::AddCheckResultLogHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
980 {
981         Dictionary::Ptr vars_after = cr->GetVarsAfter();
982
983         long state_after = vars_after->Get("state");
984         long stateType_after = vars_after->Get("state_type");
985         long attempt_after = vars_after->Get("attempt");
986         bool reachable_after = vars_after->Get("reachable");
987
988         Dictionary::Ptr vars_before = cr->GetVarsBefore();
989
990         if (vars_before) {
991                 long state_before = vars_before->Get("state");
992                 long stateType_before = vars_before->Get("state_type");
993                 long attempt_before = vars_before->Get("attempt");
994                 bool reachable_before = vars_before->Get("reachable");
995
996                 if (state_before == state_after && stateType_before == stateType_after &&
997                     attempt_before == attempt_after && reachable_before == reachable_after)
998                         return; /* Nothing changed, ignore this checkresult. */
999         }
1000
1001         LogEntryType type;
1002         String output;
1003
1004         if (cr)
1005                 output = CompatUtility::GetCheckResultOutput(cr);
1006
1007         Host::Ptr host;
1008         Service::Ptr service;
1009         tie(host, service) = GetHostService(checkable);
1010
1011         std::ostringstream msgbuf;
1012
1013         if (service) {
1014                 msgbuf << "SERVICE ALERT: "
1015                        << host->GetName() << ";"
1016                        << service->GetShortName() << ";"
1017                        << Service::StateToString(service->GetState()) << ";"
1018                        << Service::StateTypeToString(service->GetStateType()) << ";"
1019                        << attempt_after << ";"
1020                        << output << ""
1021                        << "";
1022
1023                 switch (service->GetState()) {
1024                         case ServiceOK:
1025                                 type = LogEntryTypeServiceOk;
1026                                 break;
1027                         case ServiceUnknown:
1028                                 type = LogEntryTypeServiceUnknown;
1029                                 break;
1030                         case ServiceWarning:
1031                                 type = LogEntryTypeServiceWarning;
1032                                 break;
1033                         case ServiceCritical:
1034                                 type = LogEntryTypeServiceCritical;
1035                                 break;
1036                         default:
1037                                 Log(LogCritical, "DbEvents")
1038                                     << "Unknown service state: " << state_after;
1039                                 return;
1040                 }
1041         } else {
1042                 msgbuf << "HOST ALERT: "
1043                        << host->GetName() << ";"
1044                        << CompatUtility::GetHostStateString(host) << ";"
1045                        << Host::StateTypeToString(host->GetStateType()) << ";"
1046                        << attempt_after << ";"
1047                        << output << ""
1048                        << "";
1049
1050                 switch (host->GetState()) {
1051                         case HostUp:
1052                                 type = LogEntryTypeHostUp;
1053                                 break;
1054                         case HostDown:
1055                                 type = LogEntryTypeHostDown;
1056                                 break;
1057                         default:
1058                                 Log(LogCritical, "DbEvents")
1059                                     << "Unknown host state: " << state_after;
1060                                 return;
1061                 }
1062
1063                 if (!reachable_after)
1064                         type = LogEntryTypeHostUnreachable;
1065         }
1066
1067         AddLogHistory(checkable, msgbuf.str(), type);
1068 }
1069
1070 void DbEvents::AddTriggerDowntimeLogHistory(const Downtime::Ptr& downtime)
1071 {
1072         Checkable::Ptr checkable = downtime->GetCheckable();
1073
1074         Host::Ptr host;
1075         Service::Ptr service;
1076         tie(host, service) = GetHostService(checkable);
1077
1078         std::ostringstream msgbuf;
1079
1080         if (service) {
1081                 msgbuf << "SERVICE DOWNTIME ALERT: "
1082                         << host->GetName() << ";"
1083                         << service->GetShortName() << ";"
1084                         << "STARTED" << "; "
1085                         << "Service has entered a period of scheduled downtime."
1086                         << "";
1087         } else {
1088                 msgbuf << "HOST DOWNTIME ALERT: "
1089                         << host->GetName() << ";"
1090                         << "STARTED" << "; "
1091                         << "Service has entered a period of scheduled downtime."
1092                         << "";
1093         }
1094
1095         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
1096 }
1097
1098 void DbEvents::AddRemoveDowntimeLogHistory(const Downtime::Ptr& downtime)
1099 {
1100         Checkable::Ptr checkable = downtime->GetCheckable();
1101
1102         String downtime_output;
1103         String downtime_state_str;
1104
1105         if (downtime->GetWasCancelled()) {
1106                 downtime_output = "Scheduled downtime for service has been cancelled.";
1107                 downtime_state_str = "CANCELLED";
1108         } else {
1109                 downtime_output = "Service has exited from a period of scheduled downtime.";
1110                 downtime_state_str = "STOPPED";
1111         }
1112
1113         Host::Ptr host;
1114         Service::Ptr service;
1115         tie(host, service) = GetHostService(checkable);
1116
1117         std::ostringstream msgbuf;
1118
1119         if (service) {
1120                 msgbuf << "SERVICE DOWNTIME ALERT: "
1121                         << host->GetName() << ";"
1122                         << service->GetShortName() << ";"
1123                         << downtime_state_str << "; "
1124                         << downtime_output
1125                         << "";
1126         } else {
1127                 msgbuf << "HOST DOWNTIME ALERT: "
1128                         << host->GetName() << ";"
1129                         << downtime_state_str << "; "
1130                         << downtime_output
1131                         << "";
1132         }
1133
1134         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
1135 }
1136
1137 void DbEvents::AddNotificationSentLogHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user,
1138     NotificationType notification_type, const CheckResult::Ptr& cr,
1139     const String& author, const String& comment_text)
1140 {
1141         CheckCommand::Ptr commandObj = checkable->GetCheckCommand();
1142
1143         String check_command = "";
1144         if (commandObj)
1145                 check_command = commandObj->GetName();
1146
1147         String notification_type_str = Notification::NotificationTypeToString(notification_type);
1148
1149         String author_comment = "";
1150         if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) {
1151                 author_comment = ";" + author + ";" + comment_text;
1152         }
1153
1154         if (!cr)
1155                 return;
1156
1157         String output;
1158
1159         if (cr)
1160                 output = CompatUtility::GetCheckResultOutput(cr);
1161
1162         Host::Ptr host;
1163         Service::Ptr service;
1164         tie(host, service) = GetHostService(checkable);
1165
1166         std::ostringstream msgbuf;
1167
1168         if (service) {
1169                 msgbuf << "SERVICE NOTIFICATION: "
1170                        << user->GetName() << ";"
1171                        << host->GetName() << ";"
1172                        << service->GetShortName() << ";"
1173                        << notification_type_str << " "
1174                        << "(" << Service::StateToString(service->GetState()) << ");"
1175                        << check_command << ";"
1176                        << output << author_comment
1177                        << "";
1178         } else {
1179                 msgbuf << "HOST NOTIFICATION: "
1180                        << user->GetName() << ";"
1181                        << host->GetName() << ";"
1182                        << notification_type_str << " "
1183                        << "(" << Host::StateToString(host->GetState()) << ");"
1184                        << check_command << ";"
1185                        << output << author_comment
1186                        << "";
1187         }
1188
1189         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeHostNotification);
1190 }
1191
1192 void DbEvents::AddFlappingChangedLogHistory(const Checkable::Ptr& checkable)
1193 {
1194         String flapping_state_str;
1195         String flapping_output;
1196         
1197         if (checkable->IsFlapping()) {
1198                 flapping_output = "Service appears to have started flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change >= " + Convert::ToString(checkable->GetFlappingThresholdHigh()) + "% threshold)";
1199                 flapping_state_str = "STARTED";
1200         } else {
1201                 flapping_output = "Service appears to have stopped flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change < " + Convert::ToString(checkable->GetFlappingThresholdLow()) + "% threshold)";
1202                 flapping_state_str = "STOPPED";
1203         }
1204
1205         Host::Ptr host;
1206         Service::Ptr service;
1207         tie(host, service) = GetHostService(checkable);
1208
1209         std::ostringstream msgbuf;
1210
1211         if (service) {
1212                 msgbuf << "SERVICE FLAPPING ALERT: "
1213                        << host->GetName() << ";"
1214                        << service->GetShortName() << ";"
1215                        << flapping_state_str << "; "
1216                        << flapping_output
1217                        << "";
1218         } else {
1219                 msgbuf << "HOST FLAPPING ALERT: "
1220                        << host->GetName() << ";"
1221                        << flapping_state_str << "; "
1222                        << flapping_output
1223                        << "";
1224         }
1225
1226         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
1227 }
1228
1229 void DbEvents::AddEnableFlappingChangedLogHistory(const Checkable::Ptr& checkable)
1230 {
1231         if (!checkable->GetEnableFlapping())
1232                 return;
1233                 
1234         String flapping_output = "Flap detection has been disabled";
1235         String flapping_state_str = "DISABLED";
1236
1237         Host::Ptr host;
1238         Service::Ptr service;
1239         tie(host, service) = GetHostService(checkable);
1240
1241         std::ostringstream msgbuf;
1242
1243         if (service) {
1244                 msgbuf << "SERVICE FLAPPING ALERT: "
1245                        << host->GetName() << ";"
1246                        << service->GetShortName() << ";"
1247                        << flapping_state_str << "; "
1248                        << flapping_output
1249                        << "";
1250         } else {
1251                 msgbuf << "HOST FLAPPING ALERT: "
1252                        << host->GetName() << ";"
1253                        << flapping_state_str << "; "
1254                        << flapping_output
1255                        << "";
1256         }
1257
1258         AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
1259 }
1260
1261 void DbEvents::AddLogHistory(const Checkable::Ptr& checkable, String buffer, LogEntryType type)
1262 {
1263         Log(LogDebug, "DbEvents")
1264             << "add log entry history for '" << checkable->GetName() << "'";
1265
1266         double now = Utility::GetTime();
1267         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
1268
1269         DbQuery query1;
1270         query1.Table = "logentries";
1271         query1.Type = DbQueryInsert;
1272         query1.Category = DbCatLog;
1273
1274         Dictionary::Ptr fields1 = new Dictionary();
1275         fields1->Set("logentry_time", DbValue::FromTimestamp(time_bag.first));
1276         fields1->Set("entry_time", DbValue::FromTimestamp(time_bag.first));
1277         fields1->Set("entry_time_usec", time_bag.second);
1278         fields1->Set("object_id", checkable); // added in 1.10 see #4754
1279         fields1->Set("logentry_type", type);
1280         fields1->Set("logentry_data", buffer);
1281
1282         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1283
1284         String node = IcingaApplication::GetInstance()->GetNodeName();
1285
1286         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1287         if (endpoint)
1288                 fields1->Set("endpoint_object_id", endpoint);
1289
1290         query1.Fields = fields1;
1291         DbObject::OnQuery(query1);
1292 }
1293
1294 /* flappinghistory */
1295 void DbEvents::AddFlappingChangedHistory(const Checkable::Ptr& checkable)
1296 {
1297         Log(LogDebug, "DbEvents")
1298             << "add flapping history for '" << checkable->GetName() << "'";
1299
1300         double now = Utility::GetTime();
1301         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
1302
1303         DbQuery query1;
1304         query1.Table = "flappinghistory";
1305         query1.Type = DbQueryInsert;
1306         query1.Category = DbCatFlapping;
1307
1308         Dictionary::Ptr fields1 = new Dictionary();
1309
1310         fields1->Set("event_time", DbValue::FromTimestamp(time_bag.first));
1311         fields1->Set("event_time_usec", time_bag.second);
1312
1313         if (checkable->IsFlapping())
1314                 fields1->Set("event_type", 1000);
1315         else {
1316                 fields1->Set("event_type", 1001);
1317                 fields1->Set("reason_type", 1);
1318         }
1319
1320         Host::Ptr host;
1321         Service::Ptr service;
1322         tie(host, service) = GetHostService(checkable);
1323
1324         fields1->Set("flapping_type", service ? 1 : 0);
1325         fields1->Set("object_id", checkable);
1326         fields1->Set("percent_state_change", checkable->GetFlappingCurrent());
1327         fields1->Set("low_threshold", checkable->GetFlappingThresholdLow());
1328         fields1->Set("high_threshold", checkable->GetFlappingThresholdHigh());
1329
1330         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1331
1332         String node = IcingaApplication::GetInstance()->GetNodeName();
1333
1334         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1335         if (endpoint)
1336                 fields1->Set("endpoint_object_id", endpoint);
1337
1338         query1.Fields = fields1;
1339         DbObject::OnQuery(query1);
1340 }
1341
1342 void DbEvents::AddEnableFlappingChangedHistory(const Checkable::Ptr& checkable)
1343 {
1344         Log(LogDebug, "DbEvents")
1345             << "add flapping history for '" << checkable->GetName() << "'";
1346
1347         double now = Utility::GetTime();
1348         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
1349
1350         DbQuery query1;
1351         query1.Table = "flappinghistory";
1352         query1.Type = DbQueryInsert;
1353         query1.Category = DbCatFlapping;
1354
1355         Dictionary::Ptr fields1 = new Dictionary();
1356
1357         fields1->Set("event_time", DbValue::FromTimestamp(time_bag.first));
1358         fields1->Set("event_time_usec", time_bag.second);
1359
1360         if (!checkable->GetEnableFlapping())
1361                 return;
1362                 
1363         fields1->Set("event_type", 1001);
1364         fields1->Set("reason_type", 2);
1365
1366         Host::Ptr host;
1367         Service::Ptr service;
1368         tie(host, service) = GetHostService(checkable);
1369
1370         fields1->Set("flapping_type", service ? 1 : 0);
1371         fields1->Set("object_id", checkable);
1372         fields1->Set("percent_state_change", checkable->GetFlappingCurrent());
1373         fields1->Set("low_threshold", checkable->GetFlappingThresholdLow());
1374         fields1->Set("high_threshold", checkable->GetFlappingThresholdHigh());
1375
1376         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1377
1378         String node = IcingaApplication::GetInstance()->GetNodeName();
1379
1380         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1381         if (endpoint)
1382                 fields1->Set("endpoint_object_id", endpoint);
1383
1384         query1.Fields = fields1;
1385         DbObject::OnQuery(query1);
1386 }
1387
1388 /* servicechecks */
1389 void DbEvents::AddCheckableCheckHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
1390 {
1391         if (!cr)
1392                 return;
1393
1394         Log(LogDebug, "DbEvents")
1395             << "add checkable check history for '" << checkable->GetName() << "'";
1396
1397         Host::Ptr host;
1398         Service::Ptr service;
1399         tie(host, service) = GetHostService(checkable);
1400
1401         std::ostringstream msgbuf;
1402
1403         DbQuery query1;
1404         query1.Table = service ? "servicechecks" : "hostchecks";
1405         query1.Type = DbQueryInsert;
1406         query1.Category = DbCatCheck;
1407
1408         Dictionary::Ptr fields1 = new Dictionary();
1409         fields1->Set("check_type", CompatUtility::GetCheckableCheckType(checkable));
1410         fields1->Set("current_check_attempt", checkable->GetCheckAttempt());
1411         fields1->Set("max_check_attempts", checkable->GetMaxCheckAttempts());
1412         fields1->Set("state_type", checkable->GetStateType());
1413
1414         double start = cr->GetExecutionStart();
1415         std::pair<unsigned long, unsigned long> time_bag_start = CompatUtility::ConvertTimestamp(start);
1416
1417         double end = cr->GetExecutionEnd();
1418         std::pair<unsigned long, unsigned long> time_bag_end = CompatUtility::ConvertTimestamp(end);
1419
1420         double execution_time = cr->CalculateExecutionTime();
1421
1422         fields1->Set("start_time", DbValue::FromTimestamp(time_bag_start.first));
1423         fields1->Set("start_time_usec", time_bag_start.second);
1424         fields1->Set("end_time", DbValue::FromTimestamp(time_bag_end.first));
1425         fields1->Set("end_time_usec", time_bag_end.second);
1426         fields1->Set("command_object_id", checkable->GetCheckCommand());
1427         fields1->Set("command_args", Empty);
1428         fields1->Set("command_line", CompatUtility::GetCommandLine(checkable->GetCheckCommand()));
1429         fields1->Set("execution_time", Convert::ToString(execution_time));
1430         fields1->Set("latency", Convert::ToString(cr->CalculateLatency()));
1431         fields1->Set("return_code", cr->GetExitStatus());
1432         fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
1433         fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
1434         fields1->Set("perfdata", CompatUtility::GetCheckResultPerfdata(cr));
1435
1436         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1437
1438         if (service) {
1439                 fields1->Set("service_object_id", service);
1440                 fields1->Set("state", service->GetState());
1441         } else {
1442                 fields1->Set("host_object_id", host);
1443                 fields1->Set("state", CompatUtility::GetHostCurrentState(host));
1444         }
1445
1446         String node = IcingaApplication::GetInstance()->GetNodeName();
1447
1448         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1449         if (endpoint)
1450                 fields1->Set("endpoint_object_id", endpoint);
1451
1452         query1.Fields = fields1;
1453         DbObject::OnQuery(query1);
1454 }
1455
1456 /* eventhandlers */
1457 void DbEvents::AddEventHandlerHistory(const Checkable::Ptr& checkable)
1458 {
1459         Log(LogDebug, "DbEvents")
1460             << "add eventhandler history for '" << checkable->GetName() << "'";
1461
1462         double now = Utility::GetTime();
1463         std::pair<unsigned long, unsigned long> time_bag = CompatUtility::ConvertTimestamp(now);
1464
1465         DbQuery query1;
1466         query1.Table = "eventhandlers";
1467         query1.Type = DbQueryInsert;
1468         query1.Category = DbCatEventHandler;
1469
1470         Dictionary::Ptr fields1 = new Dictionary();
1471
1472         Host::Ptr host;
1473         Service::Ptr service;
1474         tie(host, service) = GetHostService(checkable);
1475
1476         fields1->Set("object_id", checkable);
1477
1478         if (service) {
1479                 fields1->Set("state", service->GetState());
1480                 fields1->Set("eventhandler_type", 1);
1481         } else {
1482                 fields1->Set("state", CompatUtility::GetHostCurrentState(host));
1483                 fields1->Set("eventhandler_type", 0);
1484         }
1485
1486         fields1->Set("state_type", checkable->GetStateType());
1487
1488         fields1->Set("start_time", DbValue::FromTimestamp(time_bag.first));
1489         fields1->Set("start_time_usec", time_bag.second);
1490         fields1->Set("end_time", DbValue::FromTimestamp(time_bag.first));
1491         fields1->Set("end_time_usec", time_bag.second);
1492         fields1->Set("command_object_id", checkable->GetEventCommand());
1493
1494         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1495
1496         String node = IcingaApplication::GetInstance()->GetNodeName();
1497
1498         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1499         if (endpoint)
1500                 fields1->Set("endpoint_object_id", endpoint);
1501
1502         query1.Fields = fields1;
1503         DbObject::OnQuery(query1);
1504 }
1505
1506 /* externalcommands */
1507 void DbEvents::AddExternalCommandHistory(double time, const String& command, const std::vector<String>& arguments)
1508 {
1509         Log(LogDebug, "DbEvents", "add external command history");
1510
1511         DbQuery query1;
1512         query1.Table = "externalcommands";
1513         query1.Type = DbQueryInsert;
1514         query1.Category = DbCatExternalCommand;
1515
1516         Dictionary::Ptr fields1 = new Dictionary();
1517
1518         fields1->Set("entry_time", DbValue::FromTimestamp(static_cast<long>(time)));
1519         fields1->Set("command_type", CompatUtility::MapExternalCommandType(command));
1520         fields1->Set("command_name", command);
1521         fields1->Set("command_args", boost::algorithm::join(arguments, ";"));
1522
1523         fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
1524
1525         String node = IcingaApplication::GetInstance()->GetNodeName();
1526
1527         Endpoint::Ptr endpoint = Endpoint::GetByName(node);
1528         if (endpoint)
1529                 fields1->Set("endpoint_object_id", endpoint);
1530
1531         query1.Fields = fields1;
1532         DbObject::OnQuery(query1);
1533 }