1 /******************************************************************************
3 * Copyright (C) 2012-2017 Icinga Development Team (https://www.icinga.com/) *
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. *
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. *
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 ******************************************************************************/
20 #include "db_ido/dbconnection.hpp"
21 #include "db_ido/dbconnection.tcpp"
22 #include "db_ido/dbvalue.hpp"
23 #include "icinga/icingaapplication.hpp"
24 #include "icinga/host.hpp"
25 #include "icinga/service.hpp"
26 #include "base/configtype.hpp"
27 #include "base/convert.hpp"
28 #include "base/objectlock.hpp"
29 #include "base/utility.hpp"
30 #include "base/logger.hpp"
31 #include "base/exception.hpp"
33 using namespace icinga;
35 REGISTER_TYPE(DbConnection);
37 Timer::Ptr DbConnection::m_ProgramStatusTimer;
38 boost::once_flag DbConnection::m_OnceFlag = BOOST_ONCE_INIT;
40 DbConnection::DbConnection(void)
41 : m_IDCacheValid(false), m_QueryStats(15 * 60), m_ActiveChangedHandler(false)
44 void DbConnection::OnConfigLoaded(void)
46 ConfigObject::OnConfigLoaded();
48 Value categories = GetCategories();
50 SetCategoryFilter(FilterArrayToInt(categories, DbQuery::GetCategoryFilterMap(), DbCatEverything));
53 Log(LogDebug, "DbConnection")
54 << "HA functionality disabled. Won't pause IDO connection: " << GetName();
56 SetHAMode(HARunEverywhere);
59 boost::call_once(m_OnceFlag, InitializeDbTimer);
62 void DbConnection::Start(bool runtimeCreated)
64 ObjectImpl<DbConnection>::Start(runtimeCreated);
66 Log(LogInformation, "DbConnection")
67 << "'" << GetName() << "' started.";
69 DbObject::OnQuery.connect(boost::bind(&DbConnection::ExecuteQuery, this, _1));
70 DbObject::OnMultipleQueries.connect(boost::bind(&DbConnection::ExecuteMultipleQueries, this, _1));
73 void DbConnection::Stop(bool runtimeRemoved)
75 Log(LogInformation, "DbConnection")
76 << "'" << GetName() << "' stopped.";
78 ObjectImpl<DbConnection>::Stop(runtimeRemoved);
81 void DbConnection::EnableActiveChangedHandler(void)
83 if (!m_ActiveChangedHandler) {
84 ConfigObject::OnActiveChanged.connect(boost::bind(&DbConnection::UpdateObject, this, _1));
85 m_ActiveChangedHandler = true;
89 void DbConnection::Resume(void)
91 ConfigObject::Resume();
93 Log(LogInformation, "DbConnection")
94 << "Resuming IDO connection: " << GetName();
96 m_CleanUpTimer = new Timer();
97 m_CleanUpTimer->SetInterval(60);
98 m_CleanUpTimer->OnTimerExpired.connect(boost::bind(&DbConnection::CleanUpHandler, this));
99 m_CleanUpTimer->Start();
102 void DbConnection::Pause(void)
104 ConfigObject::Pause();
106 Log(LogInformation, "DbConnection")
107 << "Pausing IDO connection: " << GetName();
109 m_CleanUpTimer.reset();
112 query1.Table = "programstatus";
113 query1.IdColumn = "programstatus_id";
114 query1.Type = DbQueryUpdate;
115 query1.Category = DbCatProgramStatus;
116 query1.WhereCriteria = new Dictionary();
117 query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
119 query1.Fields = new Dictionary();
120 query1.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
121 query1.Fields->Set("program_end_time", DbValue::FromTimestamp(Utility::GetTime()));
123 query1.Priority = PriorityHigh;
125 ExecuteQuery(query1);
130 void DbConnection::InitializeDbTimer(void)
132 m_ProgramStatusTimer = new Timer();
133 m_ProgramStatusTimer->SetInterval(10);
134 m_ProgramStatusTimer->OnTimerExpired.connect(boost::bind(&DbConnection::UpdateProgramStatus));
135 m_ProgramStatusTimer->Start();
138 void DbConnection::InsertRuntimeVariable(const String& key, const Value& value)
141 query.Table = "runtimevariables";
142 query.Type = DbQueryInsert;
143 query.Category = DbCatProgramStatus;
144 query.Fields = new Dictionary();
145 query.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
146 query.Fields->Set("varname", key);
147 query.Fields->Set("varvalue", value);
148 DbObject::OnQuery(query);
151 void DbConnection::UpdateProgramStatus(void)
153 Log(LogNotice, "DbConnection")
154 << "Updating programstatus table.";
156 std::vector<DbQuery> queries;
159 query1.Table = "programstatus";
160 query1.IdColumn = "programstatus_id";
161 query1.Type = DbQueryInsert | DbQueryUpdate;
162 query1.Category = DbCatProgramStatus;
164 query1.Fields = new Dictionary();
165 query1.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
166 query1.Fields->Set("program_version", Application::GetAppVersion());
167 query1.Fields->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
168 query1.Fields->Set("program_start_time", DbValue::FromTimestamp(Application::GetStartTime()));
169 query1.Fields->Set("is_currently_running", 1);
170 query1.Fields->Set("endpoint_name", IcingaApplication::GetInstance()->GetNodeName());
171 query1.Fields->Set("process_id", Utility::GetPid());
172 query1.Fields->Set("daemon_mode", 1);
173 query1.Fields->Set("last_command_check", DbValue::FromTimestamp(Utility::GetTime()));
174 query1.Fields->Set("notifications_enabled", (IcingaApplication::GetInstance()->GetEnableNotifications() ? 1 : 0));
175 query1.Fields->Set("active_host_checks_enabled", (IcingaApplication::GetInstance()->GetEnableHostChecks() ? 1 : 0));
176 query1.Fields->Set("passive_host_checks_enabled", 1);
177 query1.Fields->Set("active_service_checks_enabled", (IcingaApplication::GetInstance()->GetEnableServiceChecks() ? 1 : 0));
178 query1.Fields->Set("passive_service_checks_enabled", 1);
179 query1.Fields->Set("event_handlers_enabled", (IcingaApplication::GetInstance()->GetEnableEventHandlers() ? 1 : 0));
180 query1.Fields->Set("flap_detection_enabled", (IcingaApplication::GetInstance()->GetEnableFlapping() ? 1 : 0));
181 query1.Fields->Set("process_performance_data", (IcingaApplication::GetInstance()->GetEnablePerfdata() ? 1 : 0));
182 query1.WhereCriteria = new Dictionary();
183 query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
185 query1.Priority = PriorityHigh;
186 queries.push_back(query1);
189 query2.Type = DbQueryNewTransaction;
190 queries.push_back(query2);
192 DbObject::OnMultipleQueries(queries);
195 query3.Table = "runtimevariables";
196 query3.Type = DbQueryDelete;
197 query3.Category = DbCatProgramStatus;
198 query3.WhereCriteria = new Dictionary();
199 query3.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
200 DbObject::OnQuery(query3);
202 InsertRuntimeVariable("total_services", ConfigType::Get<Service>()->GetObjectCount());
203 InsertRuntimeVariable("total_scheduled_services", ConfigType::Get<Service>()->GetObjectCount());
204 InsertRuntimeVariable("total_hosts", ConfigType::Get<Host>()->GetObjectCount());
205 InsertRuntimeVariable("total_scheduled_hosts", ConfigType::Get<Host>()->GetObjectCount());
208 void DbConnection::CleanUpHandler(void)
210 long now = static_cast<long>(Utility::GetTime());
216 { "acknowledgements", "entry_time" },
217 { "commenthistory", "entry_time" },
218 { "contactnotifications", "start_time" },
219 { "contactnotificationmethods", "start_time" },
220 { "downtimehistory", "entry_time" },
221 { "eventhandlers", "start_time" },
222 { "externalcommands", "entry_time" },
223 { "flappinghistory", "event_time" },
224 { "hostchecks", "start_time" },
225 { "logentries", "logentry_time" },
226 { "notifications", "start_time" },
227 { "processevents", "event_time" },
228 { "statehistory", "state_time" },
229 { "servicechecks", "start_time" },
230 { "systemcommands", "start_time" }
233 for (size_t i = 0; i < sizeof(tables) / sizeof(tables[0]); i++) {
234 double max_age = GetCleanup()->Get(tables[i].name + "_age");
239 CleanUpExecuteQuery(tables[i].name, tables[i].time_column, now - max_age);
240 Log(LogNotice, "DbConnection")
241 << "Cleanup (" << tables[i].name << "): " << max_age
243 << " old: " << now - max_age;
248 void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
250 /* Default handler does nothing. */
253 void DbConnection::SetConfigHash(const DbObject::Ptr& dbobj, const String& hash)
255 SetConfigHash(dbobj->GetType(), GetObjectID(dbobj), hash);
258 void DbConnection::SetConfigHash(const DbType::Ptr& type, const DbReference& objid, const String& hash)
260 if (!objid.IsValid())
264 m_ConfigHashes[std::make_pair(type, objid)] = hash;
266 m_ConfigHashes.erase(std::make_pair(type, objid));
269 String DbConnection::GetConfigHash(const DbObject::Ptr& dbobj) const
271 return GetConfigHash(dbobj->GetType(), GetObjectID(dbobj));
274 String DbConnection::GetConfigHash(const DbType::Ptr& type, const DbReference& objid) const
276 if (!objid.IsValid())
279 auto it = m_ConfigHashes.find(std::make_pair(type, objid));
281 if (it == m_ConfigHashes.end())
287 void DbConnection::SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref)
290 m_ObjectIDs[dbobj] = dbref;
292 m_ObjectIDs.erase(dbobj);
295 DbReference DbConnection::GetObjectID(const DbObject::Ptr& dbobj) const
297 auto it = m_ObjectIDs.find(dbobj);
299 if (it == m_ObjectIDs.end())
300 return DbReference();
305 void DbConnection::SetInsertID(const DbObject::Ptr& dbobj, const DbReference& dbref)
307 SetInsertID(dbobj->GetType(), GetObjectID(dbobj), dbref);
310 void DbConnection::SetInsertID(const DbType::Ptr& type, const DbReference& objid, const DbReference& dbref)
312 if (!objid.IsValid())
316 m_InsertIDs[std::make_pair(type, objid)] = dbref;
318 m_InsertIDs.erase(std::make_pair(type, objid));
321 DbReference DbConnection::GetInsertID(const DbObject::Ptr& dbobj) const
323 return GetInsertID(dbobj->GetType(), GetObjectID(dbobj));
326 DbReference DbConnection::GetInsertID(const DbType::Ptr& type, const DbReference& objid) const
328 if (!objid.IsValid())
329 return DbReference();
331 auto it = m_InsertIDs.find(std::make_pair(type, objid));
333 if (it == m_InsertIDs.end())
334 return DbReference();
339 void DbConnection::SetObjectActive(const DbObject::Ptr& dbobj, bool active)
342 m_ActiveObjects.insert(dbobj);
344 m_ActiveObjects.erase(dbobj);
347 bool DbConnection::GetObjectActive(const DbObject::Ptr& dbobj) const
349 return (m_ActiveObjects.find(dbobj) != m_ActiveObjects.end());
352 void DbConnection::ClearIDCache(void)
354 SetIDCacheValid(false);
358 m_ActiveObjects.clear();
359 m_ConfigUpdates.clear();
360 m_StatusUpdates.clear();
361 m_ConfigHashes.clear();
364 void DbConnection::SetConfigUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
367 m_ConfigUpdates.insert(dbobj);
369 m_ConfigUpdates.erase(dbobj);
372 bool DbConnection::GetConfigUpdate(const DbObject::Ptr& dbobj) const
374 return (m_ConfigUpdates.find(dbobj) != m_ConfigUpdates.end());
377 void DbConnection::SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
380 m_StatusUpdates.insert(dbobj);
382 m_StatusUpdates.erase(dbobj);
385 bool DbConnection::GetStatusUpdate(const DbObject::Ptr& dbobj) const
387 return (m_StatusUpdates.find(dbobj) != m_StatusUpdates.end());
390 void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
392 if (!GetConnected() || Application::IsShuttingDown())
395 DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
398 bool dbActive = GetObjectActive(dbobj);
399 bool active = object->IsActive();
403 ActivateObject(dbobj);
405 Dictionary::Ptr configFields = dbobj->GetConfigFields();
406 String configHash = dbobj->CalculateConfigHash(configFields);
407 ASSERT(configHash.GetLength() <= 64);
408 configFields->Set("config_hash", configHash);
410 String cachedHash = GetConfigHash(dbobj);
412 if (cachedHash != configHash) {
413 dbobj->SendConfigUpdateHeavy(configFields);
414 dbobj->SendStatusUpdate();
416 dbobj->SendConfigUpdateLight();
418 } else if (!active) {
419 /* Deactivate the deleted object no matter
420 * which state it had in the database.
422 DeactivateObject(dbobj);
427 void DbConnection::UpdateAllObjects(void)
429 for (const Type::Ptr& type : Type::GetAllTypes()) {
430 ConfigType *dtype = dynamic_cast<ConfigType *>(type.get());
435 for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
436 UpdateObject(object);
441 void DbConnection::PrepareDatabase(void)
443 for (const DbType::Ptr& type : DbType::GetAllTypes()) {
448 void DbConnection::ValidateFailoverTimeout(double value, const ValidationUtils& utils)
450 ObjectImpl<DbConnection>::ValidateFailoverTimeout(value, utils);
453 BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("failover_timeout"), "Failover timeout minimum is 60s."));
456 void DbConnection::ValidateCategories(const Array::Ptr& value, const ValidationUtils& utils)
458 ObjectImpl<DbConnection>::ValidateCategories(value, utils);
460 int filter = FilterArrayToInt(value, DbQuery::GetCategoryFilterMap(), 0);
462 if (filter != DbCatEverything && (filter & ~(DbCatInvalid | DbCatEverything | DbCatConfig | DbCatState |
463 DbCatAcknowledgement | DbCatComment | DbCatDowntime | DbCatEventHandler | DbCatExternalCommand |
464 DbCatFlapping | DbCatLog | DbCatNotification | DbCatProgramStatus | DbCatRetention |
465 DbCatStateHistory)) != 0)
466 BOOST_THROW_EXCEPTION(ValidationError(this, boost::assign::list_of("categories"), "categories filter is invalid."));
469 void DbConnection::IncreaseQueryCount(void)
471 double now = Utility::GetTime();
473 boost::mutex::scoped_lock lock(m_StatsMutex);
474 m_QueryStats.InsertValue(now, 1);
477 int DbConnection::GetQueryCount(RingBuffer::SizeType span) const
479 boost::mutex::scoped_lock lock(m_StatsMutex);
480 return m_QueryStats.GetValues(span);
483 bool DbConnection::IsIDCacheValid(void) const
485 return m_IDCacheValid;
488 void DbConnection::SetIDCacheValid(bool valid)
490 m_IDCacheValid = valid;
493 int DbConnection::GetSessionToken(void)
495 return Application::GetStartTime();