1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
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"
16 using namespace icinga;
18 REGISTER_TYPE(DbConnection);
20 Timer::Ptr DbConnection::m_ProgramStatusTimer;
21 boost::once_flag DbConnection::m_OnceFlag = BOOST_ONCE_INIT;
23 void DbConnection::OnConfigLoaded()
25 ConfigObject::OnConfigLoaded();
27 Value categories = GetCategories();
29 SetCategoryFilter(FilterArrayToInt(categories, DbQuery::GetCategoryFilterMap(), DbCatEverything));
32 Log(LogDebug, "DbConnection")
33 << "HA functionality disabled. Won't pause IDO connection: " << GetName();
35 SetHAMode(HARunEverywhere);
38 boost::call_once(m_OnceFlag, InitializeDbTimer);
41 void DbConnection::Start(bool runtimeCreated)
43 ObjectImpl<DbConnection>::Start(runtimeCreated);
45 Log(LogInformation, "DbConnection")
46 << "'" << GetName() << "' started.";
48 DbObject::OnQuery.connect(std::bind(&DbConnection::ExecuteQuery, this, _1));
49 DbObject::OnMultipleQueries.connect(std::bind(&DbConnection::ExecuteMultipleQueries, this, _1));
52 void DbConnection::Stop(bool runtimeRemoved)
54 Log(LogInformation, "DbConnection")
55 << "'" << GetName() << "' stopped.";
57 ObjectImpl<DbConnection>::Stop(runtimeRemoved);
60 void DbConnection::EnableActiveChangedHandler()
62 if (!m_ActiveChangedHandler) {
63 ConfigObject::OnActiveChanged.connect(std::bind(&DbConnection::UpdateObject, this, _1));
64 m_ActiveChangedHandler = true;
68 void DbConnection::Resume()
70 ConfigObject::Resume();
72 Log(LogInformation, "DbConnection")
73 << "Resuming IDO connection: " << GetName();
75 m_CleanUpTimer = new Timer();
76 m_CleanUpTimer->SetInterval(60);
77 m_CleanUpTimer->OnTimerExpired.connect(std::bind(&DbConnection::CleanUpHandler, this));
78 m_CleanUpTimer->Start();
81 void DbConnection::Pause()
83 ConfigObject::Pause();
85 Log(LogInformation, "DbConnection")
86 << "Pausing IDO connection: " << GetName();
88 m_CleanUpTimer.reset();
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 */
99 query1.Fields = new Dictionary({
100 { "instance_id", 0 }, /* DbConnection class fills in real ID */
101 { "program_end_time", DbValue::FromTimestamp(Utility::GetTime()) }
104 query1.Priority = PriorityHigh;
106 ExecuteQuery(query1);
111 void DbConnection::InitializeDbTimer()
113 m_ProgramStatusTimer = new Timer();
114 m_ProgramStatusTimer->SetInterval(10);
115 m_ProgramStatusTimer->OnTimerExpired.connect(std::bind(&DbConnection::UpdateProgramStatus));
116 m_ProgramStatusTimer->Start();
119 void DbConnection::InsertRuntimeVariable(const String& key, const Value& value)
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 */
128 { "varvalue", value }
130 DbObject::OnQuery(query);
133 void DbConnection::UpdateProgramStatus()
135 IcingaApplication::Ptr icingaApplication = IcingaApplication::GetInstance();
137 if (!icingaApplication)
140 Log(LogNotice, "DbConnection")
141 << "Updating programstatus table.";
143 std::vector<DbQuery> queries;
146 query1.Table = "programstatus";
147 query1.IdColumn = "programstatus_id";
148 query1.Type = DbQueryInsert | DbQueryUpdate;
149 query1.Category = DbCatProgramStatus;
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) }
171 query1.WhereCriteria = new Dictionary({
172 { "instance_id", 0 } /* DbConnection class fills in real ID */
175 query1.Priority = PriorityHigh;
176 queries.emplace_back(std::move(query1));
179 query2.Type = DbQueryNewTransaction;
180 queries.emplace_back(std::move(query2));
182 DbObject::OnMultipleQueries(queries);
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 */
191 DbObject::OnQuery(query3);
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());
199 void DbConnection::CleanUpHandler()
201 auto now = static_cast<long>(Utility::GetTime());
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" }
224 for (auto& table : tables) {
225 double max_age = GetCleanup()->Get(table.name + "_age");
230 CleanUpExecuteQuery(table.name, table.time_column, now - max_age);
231 Log(LogNotice, "DbConnection")
232 << "Cleanup (" << table.name << "): " << max_age
234 << " old: " << now - max_age;
239 void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
241 /* Default handler does nothing. */
244 void DbConnection::SetConfigHash(const DbObject::Ptr& dbobj, const String& hash)
246 SetConfigHash(dbobj->GetType(), GetObjectID(dbobj), hash);
249 void DbConnection::SetConfigHash(const DbType::Ptr& type, const DbReference& objid, const String& hash)
251 if (!objid.IsValid())
255 m_ConfigHashes[std::make_pair(type, objid)] = hash;
257 m_ConfigHashes.erase(std::make_pair(type, objid));
260 String DbConnection::GetConfigHash(const DbObject::Ptr& dbobj) const
262 return GetConfigHash(dbobj->GetType(), GetObjectID(dbobj));
265 String DbConnection::GetConfigHash(const DbType::Ptr& type, const DbReference& objid) const
267 if (!objid.IsValid())
270 auto it = m_ConfigHashes.find(std::make_pair(type, objid));
272 if (it == m_ConfigHashes.end())
278 void DbConnection::SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref)
281 m_ObjectIDs[dbobj] = dbref;
283 m_ObjectIDs.erase(dbobj);
286 DbReference DbConnection::GetObjectID(const DbObject::Ptr& dbobj) const
288 auto it = m_ObjectIDs.find(dbobj);
290 if (it == m_ObjectIDs.end())
296 void DbConnection::SetInsertID(const DbObject::Ptr& dbobj, const DbReference& dbref)
298 SetInsertID(dbobj->GetType(), GetObjectID(dbobj), dbref);
301 void DbConnection::SetInsertID(const DbType::Ptr& type, const DbReference& objid, const DbReference& dbref)
303 if (!objid.IsValid())
307 m_InsertIDs[std::make_pair(type, objid)] = dbref;
309 m_InsertIDs.erase(std::make_pair(type, objid));
312 DbReference DbConnection::GetInsertID(const DbObject::Ptr& dbobj) const
314 return GetInsertID(dbobj->GetType(), GetObjectID(dbobj));
317 DbReference DbConnection::GetInsertID(const DbType::Ptr& type, const DbReference& objid) const
319 if (!objid.IsValid())
322 auto it = m_InsertIDs.find(std::make_pair(type, objid));
324 if (it == m_InsertIDs.end())
325 return DbReference();
330 void DbConnection::SetObjectActive(const DbObject::Ptr& dbobj, bool active)
333 m_ActiveObjects.insert(dbobj);
335 m_ActiveObjects.erase(dbobj);
338 bool DbConnection::GetObjectActive(const DbObject::Ptr& dbobj) const
340 return (m_ActiveObjects.find(dbobj) != m_ActiveObjects.end());
343 void DbConnection::ClearIDCache()
345 SetIDCacheValid(false);
349 m_ActiveObjects.clear();
350 m_ConfigUpdates.clear();
351 m_StatusUpdates.clear();
352 m_ConfigHashes.clear();
355 void DbConnection::SetConfigUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
358 m_ConfigUpdates.insert(dbobj);
360 m_ConfigUpdates.erase(dbobj);
363 bool DbConnection::GetConfigUpdate(const DbObject::Ptr& dbobj) const
365 return (m_ConfigUpdates.find(dbobj) != m_ConfigUpdates.end());
368 void DbConnection::SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
371 m_StatusUpdates.insert(dbobj);
373 m_StatusUpdates.erase(dbobj);
376 bool DbConnection::GetStatusUpdate(const DbObject::Ptr& dbobj) const
378 return (m_StatusUpdates.find(dbobj) != m_StatusUpdates.end());
381 void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
383 bool isShuttingDown = Application::IsShuttingDown();
384 bool isRestarting = Application::IsRestarting();
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) << "'.";
392 #endif /* I2_DEBUG */
394 /* Wait until a database connection is established on reconnect. */
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.
402 if (isShuttingDown || isRestarting)
405 DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
408 bool dbActive = GetObjectActive(dbobj);
409 bool active = object->IsActive();
413 ActivateObject(dbobj);
415 Dictionary::Ptr configFields = dbobj->GetConfigFields();
416 String configHash = dbobj->CalculateConfigHash(configFields);
417 ASSERT(configHash.GetLength() <= 64);
418 configFields->Set("config_hash", configHash);
420 String cachedHash = GetConfigHash(dbobj);
422 if (cachedHash != configHash) {
423 dbobj->SendConfigUpdateHeavy(configFields);
424 dbobj->SendStatusUpdate();
426 dbobj->SendConfigUpdateLight();
428 } else if (!active) {
429 /* This may happen on reload/restart actions too
430 * and is blocked above already.
432 * Deactivate the deleted object no matter
433 * which state it had in the database.
435 DeactivateObject(dbobj);
440 void DbConnection::UpdateAllObjects()
442 for (const Type::Ptr& type : Type::GetAllTypes()) {
443 auto *dtype = dynamic_cast<ConfigType *>(type.get());
448 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
449 UpdateObject(object);
454 void DbConnection::PrepareDatabase()
456 for (const DbType::Ptr& type : DbType::GetAllTypes()) {
461 void DbConnection::ValidateFailoverTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils)
463 ObjectImpl<DbConnection>::ValidateFailoverTimeout(lvalue, utils);
466 BOOST_THROW_EXCEPTION(ValidationError(this, { "failover_timeout" }, "Failover timeout minimum is 30s."));
469 void DbConnection::ValidateCategories(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
471 ObjectImpl<DbConnection>::ValidateCategories(lvalue, utils);
473 int filter = FilterArrayToInt(lvalue(), DbQuery::GetCategoryFilterMap(), 0);
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."));
482 void DbConnection::IncreaseQueryCount()
484 double now = Utility::GetTime();
486 boost::mutex::scoped_lock lock(m_StatsMutex);
487 m_QueryStats.InsertValue(now, 1);
490 int DbConnection::GetQueryCount(RingBuffer::SizeType span)
492 boost::mutex::scoped_lock lock(m_StatsMutex);
493 return m_QueryStats.UpdateAndGetValues(Utility::GetTime(), span);
496 bool DbConnection::IsIDCacheValid() const
498 return m_IDCacheValid;
501 void DbConnection::SetIDCacheValid(bool valid)
503 m_IDCacheValid = valid;
506 int DbConnection::GetSessionToken()
508 return Application::GetStartTime();