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 Log(LogNotice, "DbConnection")
136 << "Updating programstatus table.";
138 std::vector<DbQuery> queries;
141 query1.Table = "programstatus";
142 query1.IdColumn = "programstatus_id";
143 query1.Type = DbQueryInsert | DbQueryUpdate;
144 query1.Category = DbCatProgramStatus;
146 query1.Fields = new Dictionary({
147 { "instance_id", 0 }, /* DbConnection class fills in real ID */
148 { "program_version", Application::GetAppVersion() },
149 { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) },
150 { "program_start_time", DbValue::FromTimestamp(Application::GetStartTime()) },
151 { "is_currently_running", 1 },
152 { "endpoint_name", IcingaApplication::GetInstance()->GetNodeName() },
153 { "process_id", Utility::GetPid() },
154 { "daemon_mode", 1 },
155 { "last_command_check", DbValue::FromTimestamp(Utility::GetTime()) },
156 { "notifications_enabled", (IcingaApplication::GetInstance()->GetEnableNotifications() ? 1 : 0) },
157 { "active_host_checks_enabled", (IcingaApplication::GetInstance()->GetEnableHostChecks() ? 1 : 0) },
158 { "passive_host_checks_enabled", 1 },
159 { "active_service_checks_enabled", (IcingaApplication::GetInstance()->GetEnableServiceChecks() ? 1 : 0) },
160 { "passive_service_checks_enabled", 1 },
161 { "event_handlers_enabled", (IcingaApplication::GetInstance()->GetEnableEventHandlers() ? 1 : 0) },
162 { "flap_detection_enabled", (IcingaApplication::GetInstance()->GetEnableFlapping() ? 1 : 0) },
163 { "process_performance_data", (IcingaApplication::GetInstance()->GetEnablePerfdata() ? 1 : 0) }
166 query1.WhereCriteria = new Dictionary({
167 { "instance_id", 0 } /* DbConnection class fills in real ID */
170 query1.Priority = PriorityHigh;
171 queries.emplace_back(std::move(query1));
174 query2.Type = DbQueryNewTransaction;
175 queries.emplace_back(std::move(query2));
177 DbObject::OnMultipleQueries(queries);
180 query3.Table = "runtimevariables";
181 query3.Type = DbQueryDelete;
182 query3.Category = DbCatProgramStatus;
183 query3.WhereCriteria = new Dictionary({
184 { "instance_id", 0 } /* DbConnection class fills in real ID */
186 DbObject::OnQuery(query3);
188 InsertRuntimeVariable("total_services", ConfigType::Get<Service>()->GetObjectCount());
189 InsertRuntimeVariable("total_scheduled_services", ConfigType::Get<Service>()->GetObjectCount());
190 InsertRuntimeVariable("total_hosts", ConfigType::Get<Host>()->GetObjectCount());
191 InsertRuntimeVariable("total_scheduled_hosts", ConfigType::Get<Host>()->GetObjectCount());
194 void DbConnection::CleanUpHandler()
196 auto now = static_cast<long>(Utility::GetTime());
202 { "acknowledgements", "entry_time" },
203 { "commenthistory", "entry_time" },
204 { "contactnotifications", "start_time" },
205 { "contactnotificationmethods", "start_time" },
206 { "downtimehistory", "entry_time" },
207 { "eventhandlers", "start_time" },
208 { "externalcommands", "entry_time" },
209 { "flappinghistory", "event_time" },
210 { "hostchecks", "start_time" },
211 { "logentries", "logentry_time" },
212 { "notifications", "start_time" },
213 { "processevents", "event_time" },
214 { "statehistory", "state_time" },
215 { "servicechecks", "start_time" },
216 { "systemcommands", "start_time" }
219 for (auto& table : tables) {
220 double max_age = GetCleanup()->Get(table.name + "_age");
225 CleanUpExecuteQuery(table.name, table.time_column, now - max_age);
226 Log(LogNotice, "DbConnection")
227 << "Cleanup (" << table.name << "): " << max_age
229 << " old: " << now - max_age;
234 void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
236 /* Default handler does nothing. */
239 void DbConnection::SetConfigHash(const DbObject::Ptr& dbobj, const String& hash)
241 SetConfigHash(dbobj->GetType(), GetObjectID(dbobj), hash);
244 void DbConnection::SetConfigHash(const DbType::Ptr& type, const DbReference& objid, const String& hash)
246 if (!objid.IsValid())
250 m_ConfigHashes[std::make_pair(type, objid)] = hash;
252 m_ConfigHashes.erase(std::make_pair(type, objid));
255 String DbConnection::GetConfigHash(const DbObject::Ptr& dbobj) const
257 return GetConfigHash(dbobj->GetType(), GetObjectID(dbobj));
260 String DbConnection::GetConfigHash(const DbType::Ptr& type, const DbReference& objid) const
262 if (!objid.IsValid())
265 auto it = m_ConfigHashes.find(std::make_pair(type, objid));
267 if (it == m_ConfigHashes.end())
273 void DbConnection::SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref)
276 m_ObjectIDs[dbobj] = dbref;
278 m_ObjectIDs.erase(dbobj);
281 DbReference DbConnection::GetObjectID(const DbObject::Ptr& dbobj) const
283 auto it = m_ObjectIDs.find(dbobj);
285 if (it == m_ObjectIDs.end())
291 void DbConnection::SetInsertID(const DbObject::Ptr& dbobj, const DbReference& dbref)
293 SetInsertID(dbobj->GetType(), GetObjectID(dbobj), dbref);
296 void DbConnection::SetInsertID(const DbType::Ptr& type, const DbReference& objid, const DbReference& dbref)
298 if (!objid.IsValid())
302 m_InsertIDs[std::make_pair(type, objid)] = dbref;
304 m_InsertIDs.erase(std::make_pair(type, objid));
307 DbReference DbConnection::GetInsertID(const DbObject::Ptr& dbobj) const
309 return GetInsertID(dbobj->GetType(), GetObjectID(dbobj));
312 DbReference DbConnection::GetInsertID(const DbType::Ptr& type, const DbReference& objid) const
314 if (!objid.IsValid())
317 auto it = m_InsertIDs.find(std::make_pair(type, objid));
319 if (it == m_InsertIDs.end())
320 return DbReference();
325 void DbConnection::SetObjectActive(const DbObject::Ptr& dbobj, bool active)
328 m_ActiveObjects.insert(dbobj);
330 m_ActiveObjects.erase(dbobj);
333 bool DbConnection::GetObjectActive(const DbObject::Ptr& dbobj) const
335 return (m_ActiveObjects.find(dbobj) != m_ActiveObjects.end());
338 void DbConnection::ClearIDCache()
340 SetIDCacheValid(false);
344 m_ActiveObjects.clear();
345 m_ConfigUpdates.clear();
346 m_StatusUpdates.clear();
347 m_ConfigHashes.clear();
350 void DbConnection::SetConfigUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
353 m_ConfigUpdates.insert(dbobj);
355 m_ConfigUpdates.erase(dbobj);
358 bool DbConnection::GetConfigUpdate(const DbObject::Ptr& dbobj) const
360 return (m_ConfigUpdates.find(dbobj) != m_ConfigUpdates.end());
363 void DbConnection::SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
366 m_StatusUpdates.insert(dbobj);
368 m_StatusUpdates.erase(dbobj);
371 bool DbConnection::GetStatusUpdate(const DbObject::Ptr& dbobj) const
373 return (m_StatusUpdates.find(dbobj) != m_StatusUpdates.end());
376 void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
378 bool isShuttingDown = Application::IsShuttingDown();
379 bool isRestarting = Application::IsRestarting();
382 if (isShuttingDown || isRestarting) {
383 //Log(LogDebug, "DbConnection")
384 // << "Updating object '" << object->GetName() << "' \t\t active '" << Convert::ToLong(object->IsActive())
385 // << "' shutting down '" << Convert::ToLong(isShuttingDown) << "' restarting '" << Convert::ToLong(isRestarting) << "'.";
387 #endif /* I2_DEBUG */
389 /* Wait until a database connection is established on reconnect. */
393 /* Don't update inactive objects during shutdown/reload/restart.
394 * They would be marked as deleted. This gets triggered with ConfigObject::StopObjects().
395 * During startup/reconnect this is fine, the handler is not active there.
397 if (isShuttingDown || isRestarting)
400 DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
403 bool dbActive = GetObjectActive(dbobj);
404 bool active = object->IsActive();
408 ActivateObject(dbobj);
410 Dictionary::Ptr configFields = dbobj->GetConfigFields();
411 String configHash = dbobj->CalculateConfigHash(configFields);
412 ASSERT(configHash.GetLength() <= 64);
413 configFields->Set("config_hash", configHash);
415 String cachedHash = GetConfigHash(dbobj);
417 if (cachedHash != configHash) {
418 dbobj->SendConfigUpdateHeavy(configFields);
419 dbobj->SendStatusUpdate();
421 dbobj->SendConfigUpdateLight();
423 } else if (!active) {
424 /* This may happen on reload/restart actions too
425 * and is blocked above already.
427 * Deactivate the deleted object no matter
428 * which state it had in the database.
430 DeactivateObject(dbobj);
435 void DbConnection::UpdateAllObjects()
437 for (const Type::Ptr& type : Type::GetAllTypes()) {
438 auto *dtype = dynamic_cast<ConfigType *>(type.get());
443 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
444 UpdateObject(object);
449 void DbConnection::PrepareDatabase()
451 for (const DbType::Ptr& type : DbType::GetAllTypes()) {
456 void DbConnection::ValidateFailoverTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils)
458 ObjectImpl<DbConnection>::ValidateFailoverTimeout(lvalue, utils);
461 BOOST_THROW_EXCEPTION(ValidationError(this, { "failover_timeout" }, "Failover timeout minimum is 30s."));
464 void DbConnection::ValidateCategories(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
466 ObjectImpl<DbConnection>::ValidateCategories(lvalue, utils);
468 int filter = FilterArrayToInt(lvalue(), DbQuery::GetCategoryFilterMap(), 0);
470 if (filter != DbCatEverything && (filter & ~(DbCatInvalid | DbCatEverything | DbCatConfig | DbCatState |
471 DbCatAcknowledgement | DbCatComment | DbCatDowntime | DbCatEventHandler | DbCatExternalCommand |
472 DbCatFlapping | DbCatLog | DbCatNotification | DbCatProgramStatus | DbCatRetention |
473 DbCatStateHistory)) != 0)
474 BOOST_THROW_EXCEPTION(ValidationError(this, { "categories" }, "categories filter is invalid."));
477 void DbConnection::IncreaseQueryCount()
479 double now = Utility::GetTime();
481 boost::mutex::scoped_lock lock(m_StatsMutex);
482 m_QueryStats.InsertValue(now, 1);
485 int DbConnection::GetQueryCount(RingBuffer::SizeType span)
487 boost::mutex::scoped_lock lock(m_StatsMutex);
488 return m_QueryStats.UpdateAndGetValues(Utility::GetTime(), span);
491 bool DbConnection::IsIDCacheValid() const
493 return m_IDCacheValid;
496 void DbConnection::SetIDCacheValid(bool valid)
498 m_IDCacheValid = valid;
501 int DbConnection::GetSessionToken()
503 return Application::GetStartTime();