]> granicus.if.org Git - icinga2/blob - lib/db_ido/dbconnection.cpp
Fix cookie with ActivateItems
[icinga2] / lib / db_ido / dbconnection.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "db_ido/dbconnection.hpp"
4 #include "db_ido/dbconnection-ti.cpp"
5 #include "db_ido/dbvalue.hpp"
6 #include "icinga/icingaapplication.hpp"
7 #include "icinga/host.hpp"
8 #include "icinga/service.hpp"
9 #include "base/configtype.hpp"
10 #include "base/convert.hpp"
11 #include "base/objectlock.hpp"
12 #include "base/utility.hpp"
13 #include "base/logger.hpp"
14 #include "base/exception.hpp"
15
16 using namespace icinga;
17
18 REGISTER_TYPE(DbConnection);
19
20 Timer::Ptr DbConnection::m_ProgramStatusTimer;
21 boost::once_flag DbConnection::m_OnceFlag = BOOST_ONCE_INIT;
22
23 void DbConnection::OnConfigLoaded()
24 {
25         ConfigObject::OnConfigLoaded();
26
27         Value categories = GetCategories();
28
29         SetCategoryFilter(FilterArrayToInt(categories, DbQuery::GetCategoryFilterMap(), DbCatEverything));
30
31         if (!GetEnableHa()) {
32                 Log(LogDebug, "DbConnection")
33                         << "HA functionality disabled. Won't pause IDO connection: " << GetName();
34
35                 SetHAMode(HARunEverywhere);
36         }
37
38         boost::call_once(m_OnceFlag, InitializeDbTimer);
39 }
40
41 void DbConnection::Start(bool runtimeCreated)
42 {
43         ObjectImpl<DbConnection>::Start(runtimeCreated);
44
45         Log(LogInformation, "DbConnection")
46                 << "'" << GetName() << "' started.";
47
48         DbObject::OnQuery.connect(std::bind(&DbConnection::ExecuteQuery, this, _1));
49         DbObject::OnMultipleQueries.connect(std::bind(&DbConnection::ExecuteMultipleQueries, this, _1));
50 }
51
52 void DbConnection::Stop(bool runtimeRemoved)
53 {
54         Log(LogInformation, "DbConnection")
55                 << "'" << GetName() << "' stopped.";
56
57         ObjectImpl<DbConnection>::Stop(runtimeRemoved);
58 }
59
60 void DbConnection::EnableActiveChangedHandler()
61 {
62         if (!m_ActiveChangedHandler) {
63                 ConfigObject::OnActiveChanged.connect(std::bind(&DbConnection::UpdateObject, this, _1));
64                 m_ActiveChangedHandler = true;
65         }
66 }
67
68 void DbConnection::Resume()
69 {
70         ConfigObject::Resume();
71
72         Log(LogInformation, "DbConnection")
73                 << "Resuming IDO connection: " << GetName();
74
75         m_CleanUpTimer = new Timer();
76         m_CleanUpTimer->SetInterval(60);
77         m_CleanUpTimer->OnTimerExpired.connect(std::bind(&DbConnection::CleanUpHandler, this));
78         m_CleanUpTimer->Start();
79 }
80
81 void DbConnection::Pause()
82 {
83         ConfigObject::Pause();
84
85         Log(LogInformation, "DbConnection")
86                 << "Pausing IDO connection: " << GetName();
87
88         m_CleanUpTimer.reset();
89
90         DbQuery query1;
91         query1.Table = "programstatus";
92         query1.IdColumn = "programstatus_id";
93         query1.Type = DbQueryUpdate;
94         query1.Category = DbCatProgramStatus;
95         query1.WhereCriteria = new Dictionary({
96                 { "instance_id", 0 }  /* DbConnection class fills in real ID */
97         });
98
99         query1.Fields = new Dictionary({
100                 { "instance_id", 0 }, /* DbConnection class fills in real ID */
101                 { "program_end_time", DbValue::FromTimestamp(Utility::GetTime()) }
102         });
103
104         query1.Priority = PriorityHigh;
105
106         ExecuteQuery(query1);
107
108         NewTransaction();
109 }
110
111 void DbConnection::InitializeDbTimer()
112 {
113         m_ProgramStatusTimer = new Timer();
114         m_ProgramStatusTimer->SetInterval(10);
115         m_ProgramStatusTimer->OnTimerExpired.connect(std::bind(&DbConnection::UpdateProgramStatus));
116         m_ProgramStatusTimer->Start();
117 }
118
119 void DbConnection::InsertRuntimeVariable(const String& key, const Value& value)
120 {
121         DbQuery query;
122         query.Table = "runtimevariables";
123         query.Type = DbQueryInsert;
124         query.Category = DbCatProgramStatus;
125         query.Fields = new Dictionary({
126                 { "instance_id", 0 }, /* DbConnection class fills in real ID */
127                 { "varname", key },
128                 { "varvalue", value }
129         });
130         DbObject::OnQuery(query);
131 }
132
133 void DbConnection::UpdateProgramStatus()
134 {
135         IcingaApplication::Ptr icingaApplication = IcingaApplication::GetInstance();
136
137         if (!icingaApplication)
138                 return;
139
140         Log(LogNotice, "DbConnection")
141                 << "Updating programstatus table.";
142
143         std::vector<DbQuery> queries;
144
145         DbQuery query1;
146         query1.Table = "programstatus";
147         query1.IdColumn = "programstatus_id";
148         query1.Type = DbQueryInsert | DbQueryUpdate;
149         query1.Category = DbCatProgramStatus;
150
151         query1.Fields = new Dictionary({
152                 { "instance_id", 0 }, /* DbConnection class fills in real ID */
153                 { "program_version", Application::GetAppVersion() },
154                 { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) },
155                 { "program_start_time", DbValue::FromTimestamp(Application::GetStartTime()) },
156                 { "is_currently_running", 1 },
157                 { "endpoint_name", icingaApplication->GetNodeName() },
158                 { "process_id", Utility::GetPid() },
159                 { "daemon_mode", 1 },
160                 { "last_command_check", DbValue::FromTimestamp(Utility::GetTime()) },
161                 { "notifications_enabled", (icingaApplication->GetEnableNotifications() ? 1 : 0) },
162                 { "active_host_checks_enabled", (icingaApplication->GetEnableHostChecks() ? 1 : 0) },
163                 { "passive_host_checks_enabled", 1 },
164                 { "active_service_checks_enabled", (icingaApplication->GetEnableServiceChecks() ? 1 : 0) },
165                 { "passive_service_checks_enabled", 1 },
166                 { "event_handlers_enabled", (icingaApplication->GetEnableEventHandlers() ? 1 : 0) },
167                 { "flap_detection_enabled", (icingaApplication->GetEnableFlapping() ? 1 : 0) },
168                 { "process_performance_data", (icingaApplication->GetEnablePerfdata() ? 1 : 0) }
169         });
170
171         query1.WhereCriteria = new Dictionary({
172                 { "instance_id", 0 }  /* DbConnection class fills in real ID */
173         });
174
175         query1.Priority = PriorityHigh;
176         queries.emplace_back(std::move(query1));
177
178         DbQuery query2;
179         query2.Type = DbQueryNewTransaction;
180         queries.emplace_back(std::move(query2));
181
182         DbObject::OnMultipleQueries(queries);
183
184         DbQuery query3;
185         query3.Table = "runtimevariables";
186         query3.Type = DbQueryDelete;
187         query3.Category = DbCatProgramStatus;
188         query3.WhereCriteria = new Dictionary({
189                 { "instance_id", 0 } /* DbConnection class fills in real ID */
190         });
191         DbObject::OnQuery(query3);
192
193         InsertRuntimeVariable("total_services", ConfigType::Get<Service>()->GetObjectCount());
194         InsertRuntimeVariable("total_scheduled_services", ConfigType::Get<Service>()->GetObjectCount());
195         InsertRuntimeVariable("total_hosts", ConfigType::Get<Host>()->GetObjectCount());
196         InsertRuntimeVariable("total_scheduled_hosts", ConfigType::Get<Host>()->GetObjectCount());
197 }
198
199 void DbConnection::CleanUpHandler()
200 {
201         auto now = static_cast<long>(Utility::GetTime());
202
203         struct {
204                 String name;
205                 String time_column;
206         } tables[] = {
207                 { "acknowledgements", "entry_time" },
208                 { "commenthistory", "entry_time" },
209                 { "contactnotifications", "start_time" },
210                 { "contactnotificationmethods", "start_time" },
211                 { "downtimehistory", "entry_time" },
212                 { "eventhandlers", "start_time" },
213                 { "externalcommands", "entry_time" },
214                 { "flappinghistory", "event_time" },
215                 { "hostchecks", "start_time" },
216                 { "logentries", "logentry_time" },
217                 { "notifications", "start_time" },
218                 { "processevents", "event_time" },
219                 { "statehistory", "state_time" },
220                 { "servicechecks", "start_time" },
221                 { "systemcommands", "start_time" }
222         };
223
224         for (auto& table : tables) {
225                 double max_age = GetCleanup()->Get(table.name + "_age");
226
227                 if (max_age == 0)
228                         continue;
229
230                 CleanUpExecuteQuery(table.name, table.time_column, now - max_age);
231                 Log(LogNotice, "DbConnection")
232                         << "Cleanup (" << table.name << "): " << max_age
233                         << " now: " << now
234                         << " old: " << now - max_age;
235         }
236
237 }
238
239 void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
240 {
241         /* Default handler does nothing. */
242 }
243
244 void DbConnection::SetConfigHash(const DbObject::Ptr& dbobj, const String& hash)
245 {
246         SetConfigHash(dbobj->GetType(), GetObjectID(dbobj), hash);
247 }
248
249 void DbConnection::SetConfigHash(const DbType::Ptr& type, const DbReference& objid, const String& hash)
250 {
251         if (!objid.IsValid())
252                 return;
253
254         if (!hash.IsEmpty())
255                 m_ConfigHashes[std::make_pair(type, objid)] = hash;
256         else
257                 m_ConfigHashes.erase(std::make_pair(type, objid));
258 }
259
260 String DbConnection::GetConfigHash(const DbObject::Ptr& dbobj) const
261 {
262         return GetConfigHash(dbobj->GetType(), GetObjectID(dbobj));
263 }
264
265 String DbConnection::GetConfigHash(const DbType::Ptr& type, const DbReference& objid) const
266 {
267         if (!objid.IsValid())
268                 return String();
269
270         auto it = m_ConfigHashes.find(std::make_pair(type, objid));
271
272         if (it == m_ConfigHashes.end())
273                 return String();
274
275         return it->second;
276 }
277
278 void DbConnection::SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref)
279 {
280         if (dbref.IsValid())
281                 m_ObjectIDs[dbobj] = dbref;
282         else
283                 m_ObjectIDs.erase(dbobj);
284 }
285
286 DbReference DbConnection::GetObjectID(const DbObject::Ptr& dbobj) const
287 {
288         auto it = m_ObjectIDs.find(dbobj);
289
290         if (it == m_ObjectIDs.end())
291                 return {};
292
293         return it->second;
294 }
295
296 void DbConnection::SetInsertID(const DbObject::Ptr& dbobj, const DbReference& dbref)
297 {
298         SetInsertID(dbobj->GetType(), GetObjectID(dbobj), dbref);
299 }
300
301 void DbConnection::SetInsertID(const DbType::Ptr& type, const DbReference& objid, const DbReference& dbref)
302 {
303         if (!objid.IsValid())
304                 return;
305
306         if (dbref.IsValid())
307                 m_InsertIDs[std::make_pair(type, objid)] = dbref;
308         else
309                 m_InsertIDs.erase(std::make_pair(type, objid));
310 }
311
312 DbReference DbConnection::GetInsertID(const DbObject::Ptr& dbobj) const
313 {
314         return GetInsertID(dbobj->GetType(), GetObjectID(dbobj));
315 }
316
317 DbReference DbConnection::GetInsertID(const DbType::Ptr& type, const DbReference& objid) const
318 {
319         if (!objid.IsValid())
320                 return {};
321
322         auto it = m_InsertIDs.find(std::make_pair(type, objid));
323
324         if (it == m_InsertIDs.end())
325                 return DbReference();
326
327         return it->second;
328 }
329
330 void DbConnection::SetObjectActive(const DbObject::Ptr& dbobj, bool active)
331 {
332         if (active)
333                 m_ActiveObjects.insert(dbobj);
334         else
335                 m_ActiveObjects.erase(dbobj);
336 }
337
338 bool DbConnection::GetObjectActive(const DbObject::Ptr& dbobj) const
339 {
340         return (m_ActiveObjects.find(dbobj) != m_ActiveObjects.end());
341 }
342
343 void DbConnection::ClearIDCache()
344 {
345         SetIDCacheValid(false);
346
347         m_ObjectIDs.clear();
348         m_InsertIDs.clear();
349         m_ActiveObjects.clear();
350         m_ConfigUpdates.clear();
351         m_StatusUpdates.clear();
352         m_ConfigHashes.clear();
353 }
354
355 void DbConnection::SetConfigUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
356 {
357         if (hasupdate)
358                 m_ConfigUpdates.insert(dbobj);
359         else
360                 m_ConfigUpdates.erase(dbobj);
361 }
362
363 bool DbConnection::GetConfigUpdate(const DbObject::Ptr& dbobj) const
364 {
365         return (m_ConfigUpdates.find(dbobj) != m_ConfigUpdates.end());
366 }
367
368 void DbConnection::SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
369 {
370         if (hasupdate)
371                 m_StatusUpdates.insert(dbobj);
372         else
373                 m_StatusUpdates.erase(dbobj);
374 }
375
376 bool DbConnection::GetStatusUpdate(const DbObject::Ptr& dbobj) const
377 {
378         return (m_StatusUpdates.find(dbobj) != m_StatusUpdates.end());
379 }
380
381 void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
382 {
383         bool isShuttingDown = Application::IsShuttingDown();
384         bool isRestarting = Application::IsRestarting();
385
386 #ifdef I2_DEBUG
387         if (isShuttingDown || isRestarting) {
388                 //Log(LogDebug, "DbConnection")
389                 //      << "Updating object '" << object->GetName() << "' \t\t active '" << Convert::ToLong(object->IsActive())
390                 //      << "' shutting down '" << Convert::ToLong(isShuttingDown) << "' restarting '" << Convert::ToLong(isRestarting) << "'.";
391         }
392 #endif /* I2_DEBUG */
393
394         /* Wait until a database connection is established on reconnect. */
395         if (!GetConnected())
396                 return;
397
398         /* Don't update inactive objects during shutdown/reload/restart.
399          * They would be marked as deleted. This gets triggered with ConfigObject::StopObjects().
400          * During startup/reconnect this is fine, the handler is not active there.
401          */
402         if (isShuttingDown || isRestarting)
403                 return;
404
405         DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
406
407         if (dbobj) {
408                 bool dbActive = GetObjectActive(dbobj);
409                 bool active = object->IsActive();
410
411                 if (active) {
412                         if (!dbActive)
413                                 ActivateObject(dbobj);
414
415                         Dictionary::Ptr configFields = dbobj->GetConfigFields();
416                         String configHash = dbobj->CalculateConfigHash(configFields);
417                         ASSERT(configHash.GetLength() <= 64);
418                         configFields->Set("config_hash", configHash);
419
420                         String cachedHash = GetConfigHash(dbobj);
421
422                         if (cachedHash != configHash) {
423                                 dbobj->SendConfigUpdateHeavy(configFields);
424                                 dbobj->SendStatusUpdate();
425                         } else {
426                                 dbobj->SendConfigUpdateLight();
427                         }
428                 } else if (!active) {
429                         /* This may happen on reload/restart actions too
430                          * and is blocked above already.
431                          *
432                          * Deactivate the deleted object no matter
433                          * which state it had in the database.
434                          */
435                         DeactivateObject(dbobj);
436                 }
437         }
438 }
439
440 void DbConnection::UpdateAllObjects()
441 {
442         for (const Type::Ptr& type : Type::GetAllTypes()) {
443                 auto *dtype = dynamic_cast<ConfigType *>(type.get());
444
445                 if (!dtype)
446                         continue;
447
448                 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
449                         UpdateObject(object);
450                 }
451         }
452 }
453
454 void DbConnection::PrepareDatabase()
455 {
456         for (const DbType::Ptr& type : DbType::GetAllTypes()) {
457                 FillIDCache(type);
458         }
459 }
460
461 void DbConnection::ValidateFailoverTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils)
462 {
463         ObjectImpl<DbConnection>::ValidateFailoverTimeout(lvalue, utils);
464
465         if (lvalue() < 30)
466                 BOOST_THROW_EXCEPTION(ValidationError(this, { "failover_timeout" }, "Failover timeout minimum is 30s."));
467 }
468
469 void DbConnection::ValidateCategories(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
470 {
471         ObjectImpl<DbConnection>::ValidateCategories(lvalue, utils);
472
473         int filter = FilterArrayToInt(lvalue(), DbQuery::GetCategoryFilterMap(), 0);
474
475         if (filter != DbCatEverything && (filter & ~(DbCatInvalid | DbCatEverything | DbCatConfig | DbCatState |
476                 DbCatAcknowledgement | DbCatComment | DbCatDowntime | DbCatEventHandler | DbCatExternalCommand |
477                 DbCatFlapping | DbCatLog | DbCatNotification | DbCatProgramStatus | DbCatRetention |
478                 DbCatStateHistory)) != 0)
479                 BOOST_THROW_EXCEPTION(ValidationError(this, { "categories" }, "categories filter is invalid."));
480 }
481
482 void DbConnection::IncreaseQueryCount()
483 {
484         double now = Utility::GetTime();
485
486         boost::mutex::scoped_lock lock(m_StatsMutex);
487         m_QueryStats.InsertValue(now, 1);
488 }
489
490 int DbConnection::GetQueryCount(RingBuffer::SizeType span)
491 {
492         boost::mutex::scoped_lock lock(m_StatsMutex);
493         return m_QueryStats.UpdateAndGetValues(Utility::GetTime(), span);
494 }
495
496 bool DbConnection::IsIDCacheValid() const
497 {
498         return m_IDCacheValid;
499 }
500
501 void DbConnection::SetIDCacheValid(bool valid)
502 {
503         m_IDCacheValid = valid;
504 }
505
506 int DbConnection::GetSessionToken()
507 {
508         return Application::GetStartTime();
509 }