summaryrefslogtreecommitdiffstats
path: root/lib/db_ido
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/db_ido/CMakeLists.txt40
-rw-r--r--lib/db_ido/commanddbobject.cpp31
-rw-r--r--lib/db_ido/commanddbobject.hpp30
-rw-r--r--lib/db_ido/db_ido-itl.conf19
-rw-r--r--lib/db_ido/dbconnection.cpp582
-rw-r--r--lib/db_ido/dbconnection.hpp138
-rw-r--r--lib/db_ido/dbconnection.ti82
-rw-r--r--lib/db_ido/dbevents.cpp1884
-rw-r--r--lib/db_ido/dbevents.hpp128
-rw-r--r--lib/db_ido/dbobject.cpp430
-rw-r--r--lib/db_ido/dbobject.hpp112
-rw-r--r--lib/db_ido/dbquery.cpp52
-rw-r--r--lib/db_ido/dbquery.hpp72
-rw-r--r--lib/db_ido/dbreference.cpp19
-rw-r--r--lib/db_ido/dbreference.hpp30
-rw-r--r--lib/db_ido/dbtype.cpp141
-rw-r--r--lib/db_ido/dbtype.hpp90
-rw-r--r--lib/db_ido/dbvalue.cpp69
-rw-r--r--lib/db_ido/dbvalue.hpp52
-rw-r--r--lib/db_ido/endpointdbobject.cpp91
-rw-r--r--lib/db_ido/endpointdbobject.hpp37
-rw-r--r--lib/db_ido/hostdbobject.cpp423
-rw-r--r--lib/db_ido/hostdbobject.hpp38
-rw-r--r--lib/db_ido/hostgroupdbobject.cpp33
-rw-r--r--lib/db_ido/hostgroupdbobject.hpp34
-rw-r--r--lib/db_ido/i2-db_ido.hpp14
-rw-r--r--lib/db_ido/idochecktask.cpp198
-rw-r--r--lib/db_ido/idochecktask.hpp29
-rw-r--r--lib/db_ido/servicedbobject.cpp359
-rw-r--r--lib/db_ido/servicedbobject.hpp41
-rw-r--r--lib/db_ido/servicegroupdbobject.cpp32
-rw-r--r--lib/db_ido/servicegroupdbobject.hpp31
-rw-r--r--lib/db_ido/timeperioddbobject.cpp85
-rw-r--r--lib/db_ido/timeperioddbobject.hpp33
-rw-r--r--lib/db_ido/userdbobject.cpp161
-rw-r--r--lib/db_ido/userdbobject.hpp35
-rw-r--r--lib/db_ido/usergroupdbobject.cpp30
-rw-r--r--lib/db_ido/usergroupdbobject.hpp31
-rw-r--r--lib/db_ido/zonedbobject.cpp38
-rw-r--r--lib/db_ido/zonedbobject.hpp31
-rw-r--r--lib/db_ido_mysql/CMakeLists.txt41
-rw-r--r--lib/db_ido_mysql/idomysqlconnection.cpp1268
-rw-r--r--lib/db_ido_mysql/idomysqlconnection.hpp114
-rw-r--r--lib/db_ido_mysql/idomysqlconnection.ti42
-rw-r--r--lib/db_ido_mysql/schema/mysql.sql1666
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.0.2.sql20
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.1.0.sql17
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.11.0.sql89
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.12.7.sql15
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.13.0.sql23
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.13.3.sql15
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.2.0.sql23
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.3.0.sql26
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.4.0.sql75
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.5.0.sql103
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.6.0.sql151
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.8.0.sql81
-rw-r--r--lib/db_ido_mysql/schema/upgrade/2.8.1.sql67
-rw-r--r--lib/db_ido_pgsql/CMakeLists.txt41
-rw-r--r--lib/db_ido_pgsql/idopgsqlconnection.cpp1028
-rw-r--r--lib/db_ido_pgsql/idopgsqlconnection.hpp99
-rw-r--r--lib/db_ido_pgsql/idopgsqlconnection.ti39
-rw-r--r--lib/db_ido_pgsql/schema/pgsql.sql1733
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.0.2.sql17
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.1.0.sql17
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.2.0.sql21
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.3.0.sql26
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.4.0.sql185
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.5.0.sql85
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.6.0.sql161
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.8.0.sql32
-rw-r--r--lib/db_ido_pgsql/schema/upgrade/2.8.1.sql19
72 files changed, 13144 insertions, 0 deletions
diff --git a/lib/db_ido/CMakeLists.txt b/lib/db_ido/CMakeLists.txt
new file mode 100644
index 0000000..7a97d27
--- /dev/null
+++ b/lib/db_ido/CMakeLists.txt
@@ -0,0 +1,40 @@
+# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+
+mkclass_target(dbconnection.ti dbconnection-ti.cpp dbconnection-ti.hpp)
+
+mkembedconfig_target(db_ido-itl.conf db_ido-itl.cpp)
+
+set(db_ido_SOURCES
+ i2-db_ido.hpp db_ido-itl.cpp
+ commanddbobject.cpp commanddbobject.hpp
+ dbconnection.cpp dbconnection.hpp dbconnection-ti.hpp
+ dbevents.cpp dbevents.hpp
+ dbobject.cpp dbobject.hpp
+ dbquery.cpp dbquery.hpp
+ dbreference.cpp dbreference.hpp
+ dbtype.cpp dbtype.hpp
+ dbvalue.cpp dbvalue.hpp
+ endpointdbobject.cpp endpointdbobject.hpp
+ hostdbobject.cpp hostdbobject.hpp
+ hostgroupdbobject.cpp hostgroupdbobject.hpp
+ idochecktask.cpp idochecktask.hpp
+ servicedbobject.cpp servicedbobject.hpp
+ servicegroupdbobject.cpp servicegroupdbobject.hpp
+ timeperioddbobject.cpp timeperioddbobject.hpp
+ userdbobject.cpp userdbobject.hpp
+ usergroupdbobject.cpp usergroupdbobject.hpp
+ zonedbobject.cpp zonedbobject.hpp
+)
+
+if(ICINGA2_UNITY_BUILD)
+ mkunity_target(db_ido db_ido db_ido_SOURCES)
+endif()
+
+add_library(db_ido OBJECT ${db_ido_SOURCES})
+
+add_dependencies(db_ido base config icinga remote)
+
+set_target_properties (
+ db_ido PROPERTIES
+ FOLDER Lib
+)
diff --git a/lib/db_ido/commanddbobject.cpp b/lib/db_ido/commanddbobject.cpp
new file mode 100644
index 0000000..2ac167a
--- /dev/null
+++ b/lib/db_ido/commanddbobject.cpp
@@ -0,0 +1,31 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/commanddbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "icinga/command.hpp"
+#include "icinga/compatutility.hpp"
+#include "base/objectlock.hpp"
+#include "base/convert.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(Command, "command", DbObjectTypeCommand, "object_id", CommandDbObject);
+
+CommandDbObject::CommandDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr CommandDbObject::GetConfigFields() const
+{
+ Command::Ptr command = static_pointer_cast<Command>(GetObject());
+
+ return new Dictionary({
+ { "command_line", CompatUtility::GetCommandLine(command) }
+ });
+}
+
+Dictionary::Ptr CommandDbObject::GetStatusFields() const
+{
+ return nullptr;
+}
diff --git a/lib/db_ido/commanddbobject.hpp b/lib/db_ido/commanddbobject.hpp
new file mode 100644
index 0000000..6d22747
--- /dev/null
+++ b/lib/db_ido/commanddbobject.hpp
@@ -0,0 +1,30 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef COMMANDDBOBJECT_H
+#define COMMANDDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+/**
+ * A Command database object.
+ *
+ * @ingroup ido
+ */
+class CommandDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(CommandDbObject);
+
+ CommandDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+};
+
+}
+
+#endif /* COMMANDDBOBJECT_H */
diff --git a/lib/db_ido/db_ido-itl.conf b/lib/db_ido/db_ido-itl.conf
new file mode 100644
index 0000000..e2c42c3
--- /dev/null
+++ b/lib/db_ido/db_ido-itl.conf
@@ -0,0 +1,19 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+System.assert(Internal.run_with_activation_context(function() {
+ template CheckCommand "ido-check-command" use (checkFunc = Internal.IdoCheck) {
+ execute = checkFunc
+ }
+
+ object CheckCommand "ido" {
+ import "ido-check-command"
+ }
+}))
+
+var methods = [
+ "IdoCheck"
+]
+
+for (method in methods) {
+ Internal.remove(method)
+}
diff --git a/lib/db_ido/dbconnection.cpp b/lib/db_ido/dbconnection.cpp
new file mode 100644
index 0000000..62160d8
--- /dev/null
+++ b/lib/db_ido/dbconnection.cpp
@@ -0,0 +1,582 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbconnection.hpp"
+#include "db_ido/dbconnection-ti.cpp"
+#include "db_ido/dbvalue.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "icinga/host.hpp"
+#include "icinga/service.hpp"
+#include "base/configtype.hpp"
+#include "base/convert.hpp"
+#include "base/objectlock.hpp"
+#include "base/utility.hpp"
+#include "base/logger.hpp"
+#include "base/exception.hpp"
+
+using namespace icinga;
+
+REGISTER_TYPE(DbConnection);
+
+Timer::Ptr DbConnection::m_ProgramStatusTimer;
+boost::once_flag DbConnection::m_OnceFlag = BOOST_ONCE_INIT;
+
+void DbConnection::OnConfigLoaded()
+{
+ ConfigObject::OnConfigLoaded();
+
+ Value categories = GetCategories();
+
+ SetCategoryFilter(FilterArrayToInt(categories, DbQuery::GetCategoryFilterMap(), DbCatEverything));
+
+ if (!GetEnableHa()) {
+ Log(LogDebug, "DbConnection")
+ << "HA functionality disabled. Won't pause IDO connection: " << GetName();
+
+ SetHAMode(HARunEverywhere);
+ }
+
+ boost::call_once(m_OnceFlag, InitializeDbTimer);
+}
+
+void DbConnection::Start(bool runtimeCreated)
+{
+ ObjectImpl<DbConnection>::Start(runtimeCreated);
+
+ Log(LogInformation, "DbConnection")
+ << "'" << GetName() << "' started.";
+
+ auto onQuery = [this](const DbQuery& query) { ExecuteQuery(query); };
+ DbObject::OnQuery.connect(onQuery);
+
+ auto onMultipleQueries = [this](const std::vector<DbQuery>& multiQueries) { ExecuteMultipleQueries(multiQueries); };
+ DbObject::OnMultipleQueries.connect(onMultipleQueries);
+
+ DbObject::QueryCallbacks queryCallbacks;
+ queryCallbacks.Query = onQuery;
+ queryCallbacks.MultipleQueries = onMultipleQueries;
+
+ DbObject::OnMakeQueries.connect([queryCallbacks](const std::function<void (const DbObject::QueryCallbacks&)>& queryFunc) {
+ queryFunc(queryCallbacks);
+ });
+}
+
+void DbConnection::Stop(bool runtimeRemoved)
+{
+ Log(LogInformation, "DbConnection")
+ << "'" << GetName() << "' stopped.";
+
+ ObjectImpl<DbConnection>::Stop(runtimeRemoved);
+}
+
+void DbConnection::EnableActiveChangedHandler()
+{
+ if (!m_ActiveChangedHandler) {
+ ConfigObject::OnActiveChanged.connect([this](const ConfigObject::Ptr& object, const Value&) { UpdateObject(object); });
+ m_ActiveChangedHandler = true;
+ }
+}
+
+void DbConnection::Resume()
+{
+ ConfigObject::Resume();
+
+ Log(LogInformation, "DbConnection")
+ << "Resuming IDO connection: " << GetName();
+
+ m_CleanUpTimer = new Timer();
+ m_CleanUpTimer->SetInterval(60);
+ m_CleanUpTimer->OnTimerExpired.connect([this](const Timer * const&) { CleanUpHandler(); });
+ m_CleanUpTimer->Start();
+
+ m_LogStatsTimeout = 0;
+
+ m_LogStatsTimer = new Timer();
+ m_LogStatsTimer->SetInterval(10);
+ m_LogStatsTimer->OnTimerExpired.connect([this](const Timer * const&) { LogStatsHandler(); });
+ m_LogStatsTimer->Start();
+}
+
+void DbConnection::Pause()
+{
+ Log(LogInformation, "DbConnection")
+ << "Pausing IDO connection: " << GetName();
+
+ m_CleanUpTimer.reset();
+
+ DbQuery query1;
+ query1.Table = "programstatus";
+ query1.IdColumn = "programstatus_id";
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatProgramStatus;
+ query1.WhereCriteria = new Dictionary({
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+
+ query1.Fields = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "program_end_time", DbValue::FromTimestamp(Utility::GetTime()) },
+ { "is_currently_running", 0 },
+ { "process_id", Empty }
+ });
+
+ query1.Priority = PriorityHigh;
+
+ ExecuteQuery(query1);
+
+ NewTransaction();
+
+ m_QueryQueue.Enqueue([this]() { Disconnect(); }, PriorityLow);
+
+ /* Work on remaining tasks but never delete the threads, for HA resuming later. */
+ m_QueryQueue.Join();
+
+ ConfigObject::Pause();
+}
+
+void DbConnection::InitializeDbTimer()
+{
+ m_ProgramStatusTimer = new Timer();
+ m_ProgramStatusTimer->SetInterval(10);
+ m_ProgramStatusTimer->OnTimerExpired.connect([](const Timer * const&) { UpdateProgramStatus(); });
+ m_ProgramStatusTimer->Start();
+}
+
+void DbConnection::InsertRuntimeVariable(const String& key, const Value& value)
+{
+ DbQuery query;
+ query.Table = "runtimevariables";
+ query.Type = DbQueryInsert;
+ query.Category = DbCatProgramStatus;
+ query.Fields = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "varname", key },
+ { "varvalue", value }
+ });
+ DbObject::OnQuery(query);
+}
+
+void DbConnection::UpdateProgramStatus()
+{
+ IcingaApplication::Ptr icingaApplication = IcingaApplication::GetInstance();
+
+ if (!icingaApplication)
+ return;
+
+ Log(LogNotice, "DbConnection")
+ << "Updating programstatus table.";
+
+ std::vector<DbQuery> queries;
+
+ DbQuery query1;
+ query1.Type = DbQueryNewTransaction;
+ query1.Priority = PriorityImmediate;
+ queries.emplace_back(std::move(query1));
+
+ DbQuery query2;
+ query2.Table = "programstatus";
+ query2.IdColumn = "programstatus_id";
+ query2.Type = DbQueryInsert | DbQueryDelete;
+ query2.Category = DbCatProgramStatus;
+
+ query2.Fields = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "program_version", Application::GetAppVersion() },
+ { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) },
+ { "program_start_time", DbValue::FromTimestamp(Application::GetStartTime()) },
+ { "is_currently_running", 1 },
+ { "endpoint_name", icingaApplication->GetNodeName() },
+ { "process_id", Utility::GetPid() },
+ { "daemon_mode", 1 },
+ { "last_command_check", DbValue::FromTimestamp(Utility::GetTime()) },
+ { "notifications_enabled", (icingaApplication->GetEnableNotifications() ? 1 : 0) },
+ { "active_host_checks_enabled", (icingaApplication->GetEnableHostChecks() ? 1 : 0) },
+ { "passive_host_checks_enabled", 1 },
+ { "active_service_checks_enabled", (icingaApplication->GetEnableServiceChecks() ? 1 : 0) },
+ { "passive_service_checks_enabled", 1 },
+ { "event_handlers_enabled", (icingaApplication->GetEnableEventHandlers() ? 1 : 0) },
+ { "flap_detection_enabled", (icingaApplication->GetEnableFlapping() ? 1 : 0) },
+ { "process_performance_data", (icingaApplication->GetEnablePerfdata() ? 1 : 0) }
+ });
+
+ query2.WhereCriteria = new Dictionary({
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+
+ queries.emplace_back(std::move(query2));
+
+ DbQuery query3;
+ query3.Type = DbQueryNewTransaction;
+ queries.emplace_back(std::move(query3));
+
+ DbObject::OnMultipleQueries(queries);
+
+ DbQuery query4;
+ query4.Table = "runtimevariables";
+ query4.Type = DbQueryDelete;
+ query4.Category = DbCatProgramStatus;
+ query4.WhereCriteria = new Dictionary({
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ DbObject::OnQuery(query4);
+
+ InsertRuntimeVariable("total_services", ConfigType::Get<Service>()->GetObjectCount());
+ InsertRuntimeVariable("total_scheduled_services", ConfigType::Get<Service>()->GetObjectCount());
+ InsertRuntimeVariable("total_hosts", ConfigType::Get<Host>()->GetObjectCount());
+ InsertRuntimeVariable("total_scheduled_hosts", ConfigType::Get<Host>()->GetObjectCount());
+}
+
+void DbConnection::CleanUpHandler()
+{
+ auto now = static_cast<long>(Utility::GetTime());
+
+ struct {
+ String name;
+ String time_column;
+ } tables[] = {
+ { "acknowledgements", "entry_time" },
+ { "commenthistory", "entry_time" },
+ { "contactnotifications", "start_time" },
+ { "contactnotificationmethods", "start_time" },
+ { "downtimehistory", "entry_time" },
+ { "eventhandlers", "start_time" },
+ { "externalcommands", "entry_time" },
+ { "flappinghistory", "event_time" },
+ { "hostchecks", "start_time" },
+ { "logentries", "logentry_time" },
+ { "notifications", "start_time" },
+ { "processevents", "event_time" },
+ { "statehistory", "state_time" },
+ { "servicechecks", "start_time" },
+ { "systemcommands", "start_time" }
+ };
+
+ for (auto& table : tables) {
+ double max_age = GetCleanup()->Get(table.name + "_age");
+
+ if (max_age == 0)
+ continue;
+
+ CleanUpExecuteQuery(table.name, table.time_column, now - max_age);
+ Log(LogNotice, "DbConnection")
+ << "Cleanup (" << table.name << "): " << max_age
+ << " now: " << now
+ << " old: " << now - max_age;
+ }
+
+}
+
+void DbConnection::LogStatsHandler()
+{
+ if (!GetConnected() || IsPaused())
+ return;
+
+ auto pending = m_PendingQueries.load();
+
+ auto now = Utility::GetTime();
+ bool timeoutReached = m_LogStatsTimeout < now;
+
+ if (pending == 0u && !timeoutReached) {
+ return;
+ }
+
+ auto output = round(m_OutputQueries.CalculateRate(now, 10));
+
+ if (pending < output * 5 && !timeoutReached) {
+ return;
+ }
+
+ auto input = round(m_InputQueries.CalculateRate(now, 10));
+
+ Log(LogInformation, GetReflectionType()->GetName())
+ << "Pending queries: " << pending << " (Input: " << input
+ << "/s; Output: " << output << "/s)";
+
+ /* Reschedule next log entry in 5 minutes. */
+ if (timeoutReached) {
+ m_LogStatsTimeout = now + 60 * 5;
+ }
+}
+
+void DbConnection::CleanUpExecuteQuery(const String&, const String&, double)
+{
+ /* Default handler does nothing. */
+}
+
+void DbConnection::SetConfigHash(const DbObject::Ptr& dbobj, const String& hash)
+{
+ SetConfigHash(dbobj->GetType(), GetObjectID(dbobj), hash);
+}
+
+void DbConnection::SetConfigHash(const DbType::Ptr& type, const DbReference& objid, const String& hash)
+{
+ if (!objid.IsValid())
+ return;
+
+ if (!hash.IsEmpty())
+ m_ConfigHashes[std::make_pair(type, objid)] = hash;
+ else
+ m_ConfigHashes.erase(std::make_pair(type, objid));
+}
+
+String DbConnection::GetConfigHash(const DbObject::Ptr& dbobj) const
+{
+ return GetConfigHash(dbobj->GetType(), GetObjectID(dbobj));
+}
+
+String DbConnection::GetConfigHash(const DbType::Ptr& type, const DbReference& objid) const
+{
+ if (!objid.IsValid())
+ return String();
+
+ auto it = m_ConfigHashes.find(std::make_pair(type, objid));
+
+ if (it == m_ConfigHashes.end())
+ return String();
+
+ return it->second;
+}
+
+void DbConnection::SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref)
+{
+ if (dbref.IsValid())
+ m_ObjectIDs[dbobj] = dbref;
+ else
+ m_ObjectIDs.erase(dbobj);
+}
+
+DbReference DbConnection::GetObjectID(const DbObject::Ptr& dbobj) const
+{
+ auto it = m_ObjectIDs.find(dbobj);
+
+ if (it == m_ObjectIDs.end())
+ return {};
+
+ return it->second;
+}
+
+void DbConnection::SetInsertID(const DbObject::Ptr& dbobj, const DbReference& dbref)
+{
+ SetInsertID(dbobj->GetType(), GetObjectID(dbobj), dbref);
+}
+
+void DbConnection::SetInsertID(const DbType::Ptr& type, const DbReference& objid, const DbReference& dbref)
+{
+ if (!objid.IsValid())
+ return;
+
+ if (dbref.IsValid())
+ m_InsertIDs[std::make_pair(type, objid)] = dbref;
+ else
+ m_InsertIDs.erase(std::make_pair(type, objid));
+}
+
+DbReference DbConnection::GetInsertID(const DbObject::Ptr& dbobj) const
+{
+ return GetInsertID(dbobj->GetType(), GetObjectID(dbobj));
+}
+
+DbReference DbConnection::GetInsertID(const DbType::Ptr& type, const DbReference& objid) const
+{
+ if (!objid.IsValid())
+ return {};
+
+ auto it = m_InsertIDs.find(std::make_pair(type, objid));
+
+ if (it == m_InsertIDs.end())
+ return DbReference();
+
+ return it->second;
+}
+
+void DbConnection::SetObjectActive(const DbObject::Ptr& dbobj, bool active)
+{
+ if (active)
+ m_ActiveObjects.insert(dbobj);
+ else
+ m_ActiveObjects.erase(dbobj);
+}
+
+bool DbConnection::GetObjectActive(const DbObject::Ptr& dbobj) const
+{
+ return (m_ActiveObjects.find(dbobj) != m_ActiveObjects.end());
+}
+
+void DbConnection::ClearIDCache()
+{
+ SetIDCacheValid(false);
+
+ m_ObjectIDs.clear();
+ m_InsertIDs.clear();
+ m_ActiveObjects.clear();
+ m_ConfigUpdates.clear();
+ m_StatusUpdates.clear();
+ m_ConfigHashes.clear();
+}
+
+void DbConnection::SetConfigUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
+{
+ if (hasupdate)
+ m_ConfigUpdates.insert(dbobj);
+ else
+ m_ConfigUpdates.erase(dbobj);
+}
+
+bool DbConnection::GetConfigUpdate(const DbObject::Ptr& dbobj) const
+{
+ return (m_ConfigUpdates.find(dbobj) != m_ConfigUpdates.end());
+}
+
+void DbConnection::SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate)
+{
+ if (hasupdate)
+ m_StatusUpdates.insert(dbobj);
+ else
+ m_StatusUpdates.erase(dbobj);
+}
+
+bool DbConnection::GetStatusUpdate(const DbObject::Ptr& dbobj) const
+{
+ return (m_StatusUpdates.find(dbobj) != m_StatusUpdates.end());
+}
+
+void DbConnection::UpdateObject(const ConfigObject::Ptr& object)
+{
+ bool isShuttingDown = Application::IsShuttingDown();
+ bool isRestarting = Application::IsRestarting();
+
+#ifdef I2_DEBUG
+ if (isShuttingDown || isRestarting) {
+ //Log(LogDebug, "DbConnection")
+ // << "Updating object '" << object->GetName() << "' \t\t active '" << Convert::ToLong(object->IsActive())
+ // << "' shutting down '" << Convert::ToLong(isShuttingDown) << "' restarting '" << Convert::ToLong(isRestarting) << "'.";
+ }
+#endif /* I2_DEBUG */
+
+ /* Wait until a database connection is established on reconnect. */
+ if (!GetConnected())
+ return;
+
+ /* Don't update inactive objects during shutdown/reload/restart.
+ * They would be marked as deleted. This gets triggered with ConfigObject::StopObjects().
+ * During startup/reconnect this is fine, the handler is not active there.
+ */
+ if (isShuttingDown || isRestarting)
+ return;
+
+ DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
+
+ if (dbobj) {
+ bool dbActive = GetObjectActive(dbobj);
+ bool active = object->IsActive();
+
+ if (active) {
+ if (!dbActive)
+ ActivateObject(dbobj);
+
+ Dictionary::Ptr configFields = dbobj->GetConfigFields();
+ String configHash = dbobj->CalculateConfigHash(configFields);
+ ASSERT(configHash.GetLength() <= 64);
+ configFields->Set("config_hash", configHash);
+
+ String cachedHash = GetConfigHash(dbobj);
+
+ if (cachedHash != configHash) {
+ dbobj->SendConfigUpdateHeavy(configFields);
+ dbobj->SendStatusUpdate();
+ } else {
+ dbobj->SendConfigUpdateLight();
+ }
+ } else if (!active) {
+ /* This may happen on reload/restart actions too
+ * and is blocked above already.
+ *
+ * Deactivate the deleted object no matter
+ * which state it had in the database.
+ */
+ DeactivateObject(dbobj);
+ }
+ }
+}
+
+void DbConnection::UpdateAllObjects()
+{
+ for (const Type::Ptr& type : Type::GetAllTypes()) {
+ auto *dtype = dynamic_cast<ConfigType *>(type.get());
+
+ if (!dtype)
+ continue;
+
+ for (const ConfigObject::Ptr& object : dtype->GetObjects()) {
+ m_QueryQueue.Enqueue([this, object](){ UpdateObject(object); }, PriorityHigh);
+ }
+ }
+}
+
+void DbConnection::PrepareDatabase()
+{
+ for (const DbType::Ptr& type : DbType::GetAllTypes()) {
+ FillIDCache(type);
+ }
+}
+
+void DbConnection::ValidateFailoverTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils)
+{
+ ObjectImpl<DbConnection>::ValidateFailoverTimeout(lvalue, utils);
+
+ if (lvalue() < 30)
+ BOOST_THROW_EXCEPTION(ValidationError(this, { "failover_timeout" }, "Failover timeout minimum is 30s."));
+}
+
+void DbConnection::ValidateCategories(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils)
+{
+ ObjectImpl<DbConnection>::ValidateCategories(lvalue, utils);
+
+ int filter = FilterArrayToInt(lvalue(), DbQuery::GetCategoryFilterMap(), 0);
+
+ if (filter != DbCatEverything && (filter & ~(DbCatInvalid | DbCatEverything | DbCatConfig | DbCatState |
+ DbCatAcknowledgement | DbCatComment | DbCatDowntime | DbCatEventHandler | DbCatExternalCommand |
+ DbCatFlapping | DbCatLog | DbCatNotification | DbCatProgramStatus | DbCatRetention |
+ DbCatStateHistory)) != 0)
+ BOOST_THROW_EXCEPTION(ValidationError(this, { "categories" }, "categories filter is invalid."));
+}
+
+void DbConnection::IncreaseQueryCount()
+{
+ double now = Utility::GetTime();
+
+ std::unique_lock<std::mutex> lock(m_StatsMutex);
+ m_QueryStats.InsertValue(now, 1);
+}
+
+int DbConnection::GetQueryCount(RingBuffer::SizeType span)
+{
+ std::unique_lock<std::mutex> lock(m_StatsMutex);
+ return m_QueryStats.UpdateAndGetValues(Utility::GetTime(), span);
+}
+
+bool DbConnection::IsIDCacheValid() const
+{
+ return m_IDCacheValid;
+}
+
+void DbConnection::SetIDCacheValid(bool valid)
+{
+ m_IDCacheValid = valid;
+}
+
+int DbConnection::GetSessionToken()
+{
+ return Application::GetStartTime();
+}
+
+void DbConnection::IncreasePendingQueries(int count)
+{
+ m_PendingQueries.fetch_add(count);
+ m_InputQueries.InsertValue(Utility::GetTime(), count);
+}
+
+void DbConnection::DecreasePendingQueries(int count)
+{
+ m_PendingQueries.fetch_sub(count);
+ m_OutputQueries.InsertValue(Utility::GetTime(), count);
+}
diff --git a/lib/db_ido/dbconnection.hpp b/lib/db_ido/dbconnection.hpp
new file mode 100644
index 0000000..517a8a4
--- /dev/null
+++ b/lib/db_ido/dbconnection.hpp
@@ -0,0 +1,138 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DBCONNECTION_H
+#define DBCONNECTION_H
+
+#include "db_ido/i2-db_ido.hpp"
+#include "db_ido/dbconnection-ti.hpp"
+#include "db_ido/dbobject.hpp"
+#include "db_ido/dbquery.hpp"
+#include "base/timer.hpp"
+#include "base/ringbuffer.hpp"
+#include <boost/thread/once.hpp>
+#include <mutex>
+
+namespace icinga
+{
+
+/**
+ * A database connection.
+ *
+ * @ingroup db_ido
+ */
+class DbConnection : public ObjectImpl<DbConnection>
+{
+public:
+ DECLARE_OBJECT(DbConnection);
+
+ static void InitializeDbTimer();
+
+ virtual const char * GetLatestSchemaVersion() const noexcept = 0;
+ virtual const char * GetCompatSchemaVersion() const noexcept = 0;
+
+ void SetConfigHash(const DbObject::Ptr& dbobj, const String& hash);
+ void SetConfigHash(const DbType::Ptr& type, const DbReference& objid, const String& hash);
+ String GetConfigHash(const DbObject::Ptr& dbobj) const;
+ String GetConfigHash(const DbType::Ptr& type, const DbReference& objid) const;
+
+ void SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref);
+ DbReference GetObjectID(const DbObject::Ptr& dbobj) const;
+
+ void SetInsertID(const DbObject::Ptr& dbobj, const DbReference& dbref);
+ void SetInsertID(const DbType::Ptr& type, const DbReference& objid, const DbReference& dbref);
+ DbReference GetInsertID(const DbObject::Ptr& dbobj) const;
+ DbReference GetInsertID(const DbType::Ptr& type, const DbReference& objid) const;
+
+ void SetObjectActive(const DbObject::Ptr& dbobj, bool active);
+ bool GetObjectActive(const DbObject::Ptr& dbobj) const;
+
+ void ClearIDCache();
+
+ void SetConfigUpdate(const DbObject::Ptr& dbobj, bool hasupdate);
+ bool GetConfigUpdate(const DbObject::Ptr& dbobj) const;
+
+ void SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate);
+ bool GetStatusUpdate(const DbObject::Ptr& dbobj) const;
+
+ int GetQueryCount(RingBuffer::SizeType span);
+ virtual int GetPendingQueryCount() const = 0;
+
+ void ValidateFailoverTimeout(const Lazy<double>& lvalue, const ValidationUtils& utils) final;
+ void ValidateCategories(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils) final;
+
+protected:
+ void OnConfigLoaded() override;
+ void Start(bool runtimeCreated) override;
+ void Stop(bool runtimeRemoved) override;
+ void Resume() override;
+ void Pause() override;
+
+ virtual void ExecuteQuery(const DbQuery& query) = 0;
+ virtual void ExecuteMultipleQueries(const std::vector<DbQuery>&) = 0;
+ virtual void ActivateObject(const DbObject::Ptr& dbobj) = 0;
+ virtual void DeactivateObject(const DbObject::Ptr& dbobj) = 0;
+
+ virtual void CleanUpExecuteQuery(const String& table, const String& time_column, double max_age);
+ virtual void FillIDCache(const DbType::Ptr& type) = 0;
+ virtual void NewTransaction() = 0;
+ virtual void Disconnect() = 0;
+
+ void UpdateObject(const ConfigObject::Ptr& object);
+ void UpdateAllObjects();
+
+ void PrepareDatabase();
+
+ void IncreaseQueryCount();
+
+ bool IsIDCacheValid() const;
+ void SetIDCacheValid(bool valid);
+
+ void EnableActiveChangedHandler();
+
+ static void UpdateProgramStatus();
+
+ static int GetSessionToken();
+
+ void IncreasePendingQueries(int count);
+ void DecreasePendingQueries(int count);
+
+ WorkQueue m_QueryQueue{10000000, 1, LogNotice};
+
+private:
+ bool m_IDCacheValid{false};
+ std::map<std::pair<DbType::Ptr, DbReference>, String> m_ConfigHashes;
+ std::map<DbObject::Ptr, DbReference> m_ObjectIDs;
+ std::map<std::pair<DbType::Ptr, DbReference>, DbReference> m_InsertIDs;
+ std::set<DbObject::Ptr> m_ActiveObjects;
+ std::set<DbObject::Ptr> m_ConfigUpdates;
+ std::set<DbObject::Ptr> m_StatusUpdates;
+ Timer::Ptr m_CleanUpTimer;
+ Timer::Ptr m_LogStatsTimer;
+
+ double m_LogStatsTimeout;
+
+ void CleanUpHandler();
+ void LogStatsHandler();
+
+ static Timer::Ptr m_ProgramStatusTimer;
+ static boost::once_flag m_OnceFlag;
+
+ static void InsertRuntimeVariable(const String& key, const Value& value);
+
+ mutable std::mutex m_StatsMutex;
+ RingBuffer m_QueryStats{15 * 60};
+ bool m_ActiveChangedHandler{false};
+
+ RingBuffer m_InputQueries{10};
+ RingBuffer m_OutputQueries{10};
+ Atomic<uint_fast64_t> m_PendingQueries{0};
+};
+
+struct database_error : virtual std::exception, virtual boost::exception { };
+
+struct errinfo_database_query_;
+typedef boost::error_info<struct errinfo_database_query_, std::string> errinfo_database_query;
+
+}
+
+#endif /* DBCONNECTION_H */
diff --git a/lib/db_ido/dbconnection.ti b/lib/db_ido/dbconnection.ti
new file mode 100644
index 0000000..ad02b40
--- /dev/null
+++ b/lib/db_ido/dbconnection.ti
@@ -0,0 +1,82 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbquery.hpp"
+#include "base/configobject.hpp"
+
+library db_ido;
+
+namespace icinga
+{
+
+abstract class DbConnection : ConfigObject
+{
+ [config] String table_prefix {
+ default {{{ return "icinga_"; }}}
+ };
+
+ [config, required] Dictionary::Ptr cleanup {
+ default {{{ return new Dictionary(); }}}
+ };
+
+ [config] Array::Ptr categories {
+ default {{{
+ return new Array({
+ "DbCatConfig",
+ "DbCatState",
+ "DbCatAcknowledgement",
+ "DbCatComment",
+ "DbCatDowntime",
+ "DbCatEventHandler",
+ "DbCatFlapping",
+ "DbCatNotification",
+ "DbCatProgramStatus",
+ "DbCatRetention",
+ "DbCatStateHistory"
+ });
+ }}}
+ };
+ [no_user_view, no_user_modify] int categories_filter_real (CategoryFilter);
+
+ [config] bool enable_ha {
+ default {{{ return true; }}}
+ };
+
+ [config] double failover_timeout {
+ default {{{ return 30; }}}
+ };
+
+ [state, no_user_modify] double last_failover;
+
+ [no_user_modify] String schema_version;
+ [no_user_modify] bool connected;
+ [no_user_modify] bool should_connect {
+ default {{{ return true; }}}
+ };
+};
+
+
+validator DbConnection {
+ Dictionary cleanup {
+ Number acknowledgements_age;
+ Number commenthistory_age;
+ Number contactnotifications_age;
+ Number contactnotificationmethods_age;
+ Number downtimehistory_age;
+ Number eventhandlers_age;
+ Number externalcommands_age;
+ Number flappinghistory_age;
+ Number hostchecks_age;
+ Number logentries_age;
+ Number notifications_age;
+ Number processevents_age;
+ Number statehistory_age;
+ Number servicechecks_age;
+ Number systemcommands_age;
+ };
+
+ Array categories {
+ String "*";
+ };
+};
+
+}
diff --git a/lib/db_ido/dbevents.cpp b/lib/db_ido/dbevents.cpp
new file mode 100644
index 0000000..8358824
--- /dev/null
+++ b/lib/db_ido/dbevents.cpp
@@ -0,0 +1,1884 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbevents.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "base/convert.hpp"
+#include "base/objectlock.hpp"
+#include "base/initialize.hpp"
+#include "base/configtype.hpp"
+#include "base/utility.hpp"
+#include "base/logger.hpp"
+#include "remote/endpoint.hpp"
+#include "icinga/notification.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/eventcommand.hpp"
+#include "icinga/externalcommandprocessor.hpp"
+#include "icinga/compatutility.hpp"
+#include "icinga/pluginutility.hpp"
+#include "icinga/icingaapplication.hpp"
+#include <boost/algorithm/string/join.hpp>
+#include <utility>
+
+using namespace icinga;
+
+INITIALIZE_ONCE(&DbEvents::StaticInitialize);
+
+void DbEvents::StaticInitialize()
+{
+ /* Status */
+ Comment::OnCommentAdded.connect([](const Comment::Ptr& comment) { DbEvents::AddComment(comment); });
+ Comment::OnCommentRemoved.connect([](const Comment::Ptr& comment) { DbEvents::RemoveComment(comment); });
+ Downtime::OnDowntimeAdded.connect([](const Downtime::Ptr& downtime) { DbEvents::AddDowntime(downtime); });
+ Downtime::OnDowntimeRemoved.connect([](const Downtime::Ptr& downtime) { DbEvents::RemoveDowntime(downtime); });
+ Downtime::OnDowntimeTriggered.connect([](const Downtime::Ptr& downtime) { DbEvents::TriggerDowntime(downtime); });
+ Checkable::OnAcknowledgementSet.connect([](const Checkable::Ptr& checkable, const String&, const String&,
+ AcknowledgementType type, bool, bool, double, double, const MessageOrigin::Ptr&) {
+ DbEvents::AddAcknowledgement(checkable, type);
+ });
+ Checkable::OnAcknowledgementCleared.connect([](const Checkable::Ptr& checkable, const String&, double, const MessageOrigin::Ptr&) {
+ DbEvents::RemoveAcknowledgement(checkable);
+ });
+
+ Checkable::OnNextCheckUpdated.connect([](const Checkable::Ptr& checkable) { NextCheckUpdatedHandler(checkable); });
+ Checkable::OnFlappingChanged.connect([](const Checkable::Ptr& checkable, const Value&) { FlappingChangedHandler(checkable); });
+ Checkable::OnNotificationSentToAllUsers.connect([](const Notification::Ptr& notification, const Checkable::Ptr& checkable,
+ const std::set<User::Ptr>&, const NotificationType&, const CheckResult::Ptr&, const String&, const String&,
+ const MessageOrigin::Ptr&) {
+ DbEvents::LastNotificationChangedHandler(notification, checkable);
+ });
+
+ Checkable::OnEnableActiveChecksChanged.connect([](const Checkable::Ptr& checkable, const Value&) {
+ DbEvents::EnableActiveChecksChangedHandler(checkable);
+ });
+ Checkable::OnEnablePassiveChecksChanged.connect([](const Checkable::Ptr& checkable, const Value&) {
+ DbEvents::EnablePassiveChecksChangedHandler(checkable);
+ });
+ Checkable::OnEnableNotificationsChanged.connect([](const Checkable::Ptr& checkable, const Value&) {
+ DbEvents::EnableNotificationsChangedHandler(checkable);
+ });
+ Checkable::OnEnablePerfdataChanged.connect([](const Checkable::Ptr& checkable, const Value&) {
+ DbEvents::EnablePerfdataChangedHandler(checkable);
+ });
+ Checkable::OnEnableFlappingChanged.connect([](const Checkable::Ptr& checkable, const Value&) {
+ DbEvents::EnableFlappingChangedHandler(checkable);
+ });
+
+ Checkable::OnReachabilityChanged.connect([](const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ std::set<Checkable::Ptr> children, const MessageOrigin::Ptr&) {
+ DbEvents::ReachabilityChangedHandler(checkable, cr, std::move(children));
+ });
+
+ /* History */
+ Comment::OnCommentAdded.connect([](const Comment::Ptr& comment) { AddCommentHistory(comment); });
+ Downtime::OnDowntimeAdded.connect([](const Downtime::Ptr& downtime) { AddDowntimeHistory(downtime); });
+ Checkable::OnAcknowledgementSet.connect([](const Checkable::Ptr& checkable, const String& author, const String& comment,
+ AcknowledgementType type, bool notify, bool, double expiry, double, const MessageOrigin::Ptr&) {
+ DbEvents::AddAcknowledgementHistory(checkable, author, comment, type, notify, expiry);
+ });
+
+ Checkable::OnNotificationSentToAllUsers.connect([](const Notification::Ptr& notification, const Checkable::Ptr& checkable,
+ const std::set<User::Ptr>& users, const NotificationType& type, const CheckResult::Ptr& cr, const String& author,
+ const String& text, const MessageOrigin::Ptr&) {
+ DbEvents::AddNotificationHistory(notification, checkable, users, type, cr, author, text);
+ });
+
+ Checkable::OnStateChange.connect([](const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type, const MessageOrigin::Ptr&) {
+ DbEvents::AddStateChangeHistory(checkable, cr, type);
+ });
+
+ Checkable::OnNewCheckResult.connect([](const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin::Ptr&) {
+ DbEvents::AddCheckResultLogHistory(checkable, cr);
+ });
+ Checkable::OnNotificationSentToUser.connect([](const Notification::Ptr& notification, const Checkable::Ptr& checkable,
+ const User::Ptr& users, const NotificationType& type, const CheckResult::Ptr& cr, const String& author, const String& text,
+ const String&, const MessageOrigin::Ptr&) {
+ DbEvents::AddNotificationSentLogHistory(notification, checkable, users, type, cr, author, text);
+ });
+ Checkable::OnFlappingChanged.connect([](const Checkable::Ptr& checkable, const Value&) {
+ DbEvents::AddFlappingChangedLogHistory(checkable);
+ });
+ Checkable::OnEnableFlappingChanged.connect([](const Checkable::Ptr& checkable, const Value&) {
+ DbEvents::AddEnableFlappingChangedLogHistory(checkable);
+ });
+ Downtime::OnDowntimeTriggered.connect([](const Downtime::Ptr& downtime) { DbEvents::AddTriggerDowntimeLogHistory(downtime); });
+ Downtime::OnDowntimeRemoved.connect([](const Downtime::Ptr& downtime) { DbEvents::AddRemoveDowntimeLogHistory(downtime); });
+
+ Checkable::OnFlappingChanged.connect([](const Checkable::Ptr& checkable, const Value&) { DbEvents::AddFlappingChangedHistory(checkable); });
+ Checkable::OnEnableFlappingChanged.connect([](const Checkable::Ptr& checkable, const Value&) { DbEvents::AddEnableFlappingChangedHistory(checkable); });
+ Checkable::OnNewCheckResult.connect([](const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, const MessageOrigin::Ptr&) {
+ DbEvents::AddCheckableCheckHistory(checkable, cr);
+ });
+
+ Checkable::OnEventCommandExecuted.connect([](const Checkable::Ptr& checkable) { DbEvents::AddEventHandlerHistory(checkable); });
+
+ ExternalCommandProcessor::OnNewExternalCommand.connect([](double time, const String& command, const std::vector<String>& arguments) {
+ DbEvents::AddExternalCommandHistory(time, command, arguments);
+ });
+}
+
+/* check events */
+void DbEvents::NextCheckUpdatedHandler(const Checkable::Ptr& checkable)
+{
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query1;
+ query1.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query1.Table = "servicestatus";
+ query1.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query1.Table = "hoststatus";
+ query1.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatState;
+ query1.StatusUpdate = true;
+ query1.Object = DbObject::GetOrCreateByObject(checkable);
+
+ query1.Fields = new Dictionary({
+ { "next_check", DbValue::FromTimestamp(checkable->GetNextCheck()) }
+ });
+
+ DbObject::OnQuery(query1);
+}
+
+void DbEvents::FlappingChangedHandler(const Checkable::Ptr& checkable)
+{
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query1;
+ query1.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query1.Table = "servicestatus";
+ query1.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query1.Table = "hoststatus";
+ query1.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatState;
+ query1.StatusUpdate = true;
+ query1.Object = DbObject::GetOrCreateByObject(checkable);
+
+ Dictionary::Ptr fields1 = new Dictionary();
+ fields1->Set("is_flapping", checkable->IsFlapping());
+ fields1->Set("percent_state_change", checkable->GetFlappingCurrent());
+
+ query1.Fields = new Dictionary({
+ { "is_flapping", checkable->IsFlapping() },
+ { "percent_state_change", checkable->GetFlappingCurrent() }
+ });
+
+ query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ DbObject::OnQuery(query1);
+}
+
+void DbEvents::LastNotificationChangedHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable)
+{
+ std::pair<unsigned long, unsigned long> now_bag = ConvertTimestamp(Utility::GetTime());
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(notification->GetNextNotification());
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query1;
+ query1.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query1.Table = "servicestatus";
+ query1.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query1.Table = "hoststatus";
+ query1.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatState;
+ query1.StatusUpdate = true;
+ query1.Object = DbObject::GetOrCreateByObject(checkable);
+
+ query1.Fields = new Dictionary({
+ { "last_notification", DbValue::FromTimestamp(now_bag.first) },
+ { "next_notification", DbValue::FromTimestamp(timeBag.first) },
+ { "current_notification_number", notification->GetNotificationNumber() }
+ });
+
+ query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ DbObject::OnQuery(query1);
+}
+
+void DbEvents::ReachabilityChangedHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, std::set<Checkable::Ptr> children)
+{
+ int is_reachable = 0;
+
+ if (cr->GetState() == ServiceOK)
+ is_reachable = 1;
+
+ for (const Checkable::Ptr& child : children) {
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(child);
+
+ DbQuery query1;
+ query1.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query1.Table = "servicestatus";
+ query1.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query1.Table = "hoststatus";
+ query1.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatState;
+ query1.StatusUpdate = true;
+ query1.Object = DbObject::GetOrCreateByObject(child);
+
+ query1.Fields = new Dictionary({
+ { "is_reachable", is_reachable }
+ });
+
+ query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ DbObject::OnQuery(query1);
+ }
+}
+
+/* enable changed events */
+void DbEvents::EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable)
+{
+ EnableChangedHandlerInternal(checkable, "active_checks_enabled", checkable->GetEnableActiveChecks());
+}
+
+void DbEvents::EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable)
+{
+ EnableChangedHandlerInternal(checkable, "passive_checks_enabled", checkable->GetEnablePassiveChecks());
+}
+
+void DbEvents::EnableNotificationsChangedHandler(const Checkable::Ptr& checkable)
+{
+ EnableChangedHandlerInternal(checkable, "notifications_enabled", checkable->GetEnableNotifications());
+}
+
+void DbEvents::EnablePerfdataChangedHandler(const Checkable::Ptr& checkable)
+{
+ EnableChangedHandlerInternal(checkable, "process_performance_data", checkable->GetEnablePerfdata());
+}
+
+void DbEvents::EnableFlappingChangedHandler(const Checkable::Ptr& checkable)
+{
+ EnableChangedHandlerInternal(checkable, "flap_detection_enabled", checkable->GetEnableFlapping());
+}
+
+void DbEvents::EnableChangedHandlerInternal(const Checkable::Ptr& checkable, const String& fieldName, bool enabled)
+{
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query1;
+ query1.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query1.Table = "servicestatus";
+ query1.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query1.Table = "hoststatus";
+ query1.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatState;
+ query1.StatusUpdate = true;
+ query1.Object = DbObject::GetOrCreateByObject(checkable);
+
+ query1.Fields = new Dictionary({
+ { fieldName, enabled }
+ });
+
+ query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ DbObject::OnQuery(query1);
+}
+
+
+/* comments */
+void DbEvents::AddComments(const Checkable::Ptr& checkable)
+{
+ std::set<Comment::Ptr> comments = checkable->GetComments();
+
+ std::vector<DbQuery> queries;
+
+ for (const Comment::Ptr& comment : comments) {
+ AddCommentInternal(queries, comment, false);
+ }
+
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::AddComment(const Comment::Ptr& comment)
+{
+ std::vector<DbQuery> queries;
+ AddCommentInternal(queries, comment, false);
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::AddCommentHistory(const Comment::Ptr& comment)
+{
+ std::vector<DbQuery> queries;
+ AddCommentInternal(queries, comment, true);
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::AddCommentInternal(std::vector<DbQuery>& queries, const Comment::Ptr& comment, bool historical)
+{
+ Checkable::Ptr checkable = comment->GetCheckable();
+
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(comment->GetEntryTime());
+
+ Dictionary::Ptr fields1 = new Dictionary();
+ fields1->Set("entry_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("entry_time_usec", timeBag.second);
+ fields1->Set("entry_type", comment->GetEntryType());
+ fields1->Set("object_id", checkable);
+
+ int commentType = 0;
+
+ if (checkable->GetReflectionType() == Host::TypeInstance)
+ commentType = 2;
+ else if (checkable->GetReflectionType() == Service::TypeInstance)
+ commentType = 1;
+ else {
+ return;
+ }
+
+ fields1->Set("comment_type", commentType);
+ fields1->Set("internal_comment_id", comment->GetLegacyId());
+ fields1->Set("name", comment->GetName());
+ fields1->Set("comment_time", DbValue::FromTimestamp(timeBag.first)); /* same as entry_time */
+ fields1->Set("author_name", comment->GetAuthor());
+ fields1->Set("comment_data", comment->GetText());
+ fields1->Set("is_persistent", comment->GetPersistent());
+ fields1->Set("comment_source", 1); /* external */
+ fields1->Set("expires", (comment->GetExpireTime() > 0));
+ fields1->Set("expiration_time", DbValue::FromTimestamp(comment->GetExpireTime()));
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ DbQuery query1;
+
+ if (!historical) {
+ query1.Table = "comments";
+ query1.Type = DbQueryInsert | DbQueryUpdate;
+
+ fields1->Set("session_token", 0); /* DbConnection class fills in real ID */
+
+ query1.WhereCriteria = new Dictionary({
+ { "object_id", checkable },
+ { "name", comment->GetName() },
+ { "entry_time", DbValue::FromTimestamp(timeBag.first) }
+ });
+ } else {
+ query1.Table = "commenthistory";
+ query1.Type = DbQueryInsert;
+ }
+
+ query1.Category = DbCatComment;
+ query1.Fields = fields1;
+ queries.emplace_back(std::move(query1));
+}
+
+void DbEvents::RemoveComment(const Comment::Ptr& comment)
+{
+ std::vector<DbQuery> queries;
+ RemoveCommentInternal(queries, comment);
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::RemoveCommentInternal(std::vector<DbQuery>& queries, const Comment::Ptr& comment)
+{
+ Checkable::Ptr checkable = comment->GetCheckable();
+
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(comment->GetEntryTime());
+
+ /* Status */
+ DbQuery query1;
+ query1.Table = "comments";
+ query1.Type = DbQueryDelete;
+ query1.Category = DbCatComment;
+
+ query1.WhereCriteria = new Dictionary({
+ { "object_id", checkable },
+ { "entry_time", DbValue::FromTimestamp(timeBag.first) },
+ { "name", comment->GetName() }
+ });
+
+ queries.emplace_back(std::move(query1));
+
+ /* History - update deletion time for service/host */
+ std::pair<unsigned long, unsigned long> timeBagNow = ConvertTimestamp(Utility::GetTime());
+
+ DbQuery query2;
+ query2.Table = "commenthistory";
+ query2.Type = DbQueryUpdate;
+ query2.Category = DbCatComment;
+
+ query2.Fields = new Dictionary({
+ { "deletion_time", DbValue::FromTimestamp(timeBagNow.first) },
+ { "deletion_time_usec", timeBagNow.second }
+ });
+
+ query2.WhereCriteria = new Dictionary({
+ { "object_id", checkable },
+ { "entry_time", DbValue::FromTimestamp(timeBag.first) },
+ { "name", comment->GetName() }
+ });
+
+ queries.emplace_back(std::move(query2));
+}
+
+/* downtimes */
+void DbEvents::AddDowntimes(const Checkable::Ptr& checkable)
+{
+ std::set<Downtime::Ptr> downtimes = checkable->GetDowntimes();
+
+ std::vector<DbQuery> queries;
+
+ for (const Downtime::Ptr& downtime : downtimes) {
+ AddDowntimeInternal(queries, downtime, false);
+ }
+
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::AddDowntime(const Downtime::Ptr& downtime)
+{
+ std::vector<DbQuery> queries;
+ AddDowntimeInternal(queries, downtime, false);
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::AddDowntimeHistory(const Downtime::Ptr& downtime)
+{
+ std::vector<DbQuery> queries;
+ AddDowntimeInternal(queries, downtime, true);
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::AddDowntimeInternal(std::vector<DbQuery>& queries, const Downtime::Ptr& downtime, bool historical)
+{
+ Checkable::Ptr checkable = downtime->GetCheckable();
+
+ Dictionary::Ptr fields1 = new Dictionary();
+ fields1->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
+ fields1->Set("object_id", checkable);
+
+ int downtimeType = 0;
+
+ if (checkable->GetReflectionType() == Host::TypeInstance)
+ downtimeType = 2;
+ else if (checkable->GetReflectionType() == Service::TypeInstance)
+ downtimeType = 1;
+ else {
+ return;
+ }
+
+ fields1->Set("downtime_type", downtimeType);
+ fields1->Set("internal_downtime_id", downtime->GetLegacyId());
+ fields1->Set("author_name", downtime->GetAuthor());
+ fields1->Set("comment_data", downtime->GetComment());
+ fields1->Set("triggered_by_id", Downtime::GetByName(downtime->GetTriggeredBy()));
+ fields1->Set("is_fixed", downtime->GetFixed());
+ fields1->Set("duration", downtime->GetDuration());
+ fields1->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
+ fields1->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
+ fields1->Set("name", downtime->GetName());
+
+ /* flexible downtimes are started at trigger time */
+ if (downtime->GetFixed()) {
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(downtime->GetStartTime());
+
+ fields1->Set("actual_start_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("actual_start_time_usec", timeBag.second);
+ fields1->Set("was_started", ((downtime->GetStartTime() <= Utility::GetTime()) ? 1 : 0));
+ }
+
+ fields1->Set("is_in_effect", downtime->IsInEffect());
+ fields1->Set("trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()));
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ DbQuery query1;
+
+ if (!historical) {
+ query1.Table = "scheduleddowntime";
+ query1.Type = DbQueryInsert | DbQueryUpdate;
+
+ fields1->Set("session_token", 0); /* DbConnection class fills in real ID */
+
+ query1.WhereCriteria = new Dictionary({
+ { "object_id", checkable },
+ { "name", downtime->GetName() },
+ { "entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()) }
+ });
+ } else {
+ query1.Table = "downtimehistory";
+ query1.Type = DbQueryInsert;
+ }
+
+ query1.Category = DbCatDowntime;
+ query1.Fields = fields1;
+ queries.emplace_back(std::move(query1));
+
+ /* host/service status */
+ if (!historical) {
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query2;
+ query2.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query2.Table = "servicestatus";
+ query2.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query2.Table = "hoststatus";
+ query2.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query2.Type = DbQueryUpdate;
+ query2.Category = DbCatState;
+ query2.StatusUpdate = true;
+ query2.Object = DbObject::GetOrCreateByObject(checkable);
+
+ Dictionary::Ptr fields2 = new Dictionary();
+ fields2->Set("scheduled_downtime_depth", checkable->GetDowntimeDepth());
+
+ query2.Fields = fields2;
+ query2.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ queries.emplace_back(std::move(query2));
+ }
+}
+
+void DbEvents::RemoveDowntime(const Downtime::Ptr& downtime)
+{
+ std::vector<DbQuery> queries;
+ RemoveDowntimeInternal(queries, downtime);
+ DbObject::OnMultipleQueries(queries);
+}
+
+void DbEvents::RemoveDowntimeInternal(std::vector<DbQuery>& queries, const Downtime::Ptr& downtime)
+{
+ Checkable::Ptr checkable = downtime->GetCheckable();
+
+ /* Status */
+ DbQuery query1;
+ query1.Table = "scheduleddowntime";
+ query1.Type = DbQueryDelete;
+ query1.Category = DbCatDowntime;
+ query1.WhereCriteria = new Dictionary();
+
+ query1.WhereCriteria->Set("object_id", checkable);
+ query1.WhereCriteria->Set("entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()));
+ query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+ query1.WhereCriteria->Set("scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()));
+ query1.WhereCriteria->Set("scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()));
+ query1.WhereCriteria->Set("name", downtime->GetName());
+ queries.emplace_back(std::move(query1));
+
+ /* History - update actual_end_time, was_cancelled for service (and host in case) */
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ DbQuery query3;
+ query3.Table = "downtimehistory";
+ query3.Type = DbQueryUpdate;
+ query3.Category = DbCatDowntime;
+
+ Dictionary::Ptr fields3 = new Dictionary();
+ fields3->Set("was_cancelled", downtime->GetWasCancelled() ? 1 : 0);
+
+ if (downtime->GetFixed() || (!downtime->GetFixed() && downtime->GetTriggerTime() > 0)) {
+ fields3->Set("actual_end_time", DbValue::FromTimestamp(timeBag.first));
+ fields3->Set("actual_end_time_usec", timeBag.second);
+ }
+
+ fields3->Set("is_in_effect", 0);
+ query3.Fields = fields3;
+
+ query3.WhereCriteria = new Dictionary({
+ { "object_id", checkable },
+ { "entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()) },
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()) },
+ { "scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()) },
+ { "name", downtime->GetName() }
+ });
+
+ queries.emplace_back(std::move(query3));
+
+ /* host/service status */
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query4;
+ query4.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query4.Table = "servicestatus";
+ query4.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query4.Table = "hoststatus";
+ query4.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query4.Type = DbQueryUpdate;
+ query4.Category = DbCatState;
+ query4.StatusUpdate = true;
+ query4.Object = DbObject::GetOrCreateByObject(checkable);
+
+ Dictionary::Ptr fields4 = new Dictionary();
+ fields4->Set("scheduled_downtime_depth", checkable->GetDowntimeDepth());
+
+ query4.Fields = fields4;
+ query4.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ queries.emplace_back(std::move(query4));
+}
+
+void DbEvents::TriggerDowntime(const Downtime::Ptr& downtime)
+{
+ Checkable::Ptr checkable = downtime->GetCheckable();
+
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ /* Status */
+ DbQuery query1;
+ query1.Table = "scheduleddowntime";
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatDowntime;
+
+ query1.Fields = new Dictionary({
+ { "was_started", 1 },
+ { "actual_start_time", DbValue::FromTimestamp(timeBag.first) },
+ { "actual_start_time_usec", timeBag.second },
+ { "is_in_effect", (downtime->IsInEffect() ? 1 : 0) },
+ { "trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()) },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+
+ query1.WhereCriteria = new Dictionary({
+ { "object_id", checkable },
+ { "entry_time", DbValue::FromTimestamp(downtime->GetEntryTime()) },
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "scheduled_start_time", DbValue::FromTimestamp(downtime->GetStartTime()) },
+ { "scheduled_end_time", DbValue::FromTimestamp(downtime->GetEndTime()) },
+ { "name", downtime->GetName() }
+ });
+
+ DbObject::OnQuery(query1);
+
+ /* History - downtime was started for service (and host in case) */
+ DbQuery query3;
+ query3.Table = "downtimehistory";
+ query3.Type = DbQueryUpdate;
+ query3.Category = DbCatDowntime;
+
+ query3.Fields = new Dictionary({
+ { "was_started", 1 },
+ { "is_in_effect", 1 },
+ { "actual_start_time", DbValue::FromTimestamp(timeBag.first) },
+ { "actual_start_time_usec", timeBag.second },
+ { "trigger_time", DbValue::FromTimestamp(downtime->GetTriggerTime()) }
+ });
+
+ query3.WhereCriteria = query1.WhereCriteria;
+
+ DbObject::OnQuery(query3);
+
+ /* host/service status */
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query4;
+ query4.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query4.Table = "servicestatus";
+ query4.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query4.Table = "hoststatus";
+ query4.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query4.Type = DbQueryUpdate;
+ query4.Category = DbCatState;
+ query4.StatusUpdate = true;
+ query4.Object = DbObject::GetOrCreateByObject(checkable);
+
+ query4.Fields = new Dictionary({
+ { "scheduled_downtime_depth", checkable->GetDowntimeDepth() }
+ });
+ query4.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ DbObject::OnQuery(query4);
+}
+
+/* acknowledgements */
+void DbEvents::AddAcknowledgementHistory(const Checkable::Ptr& checkable, const String& author, const String& comment,
+ AcknowledgementType type, bool notify, double expiry)
+{
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ DbQuery query1;
+ query1.Table = "acknowledgements";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatAcknowledgement;
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ Dictionary::Ptr fields1 = new Dictionary();
+
+ fields1->Set("entry_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("entry_time_usec", timeBag.second);
+ fields1->Set("acknowledgement_type", type);
+ fields1->Set("object_id", checkable);
+ fields1->Set("author_name", author);
+ fields1->Set("comment_data", comment);
+ fields1->Set("persistent_comment", 1);
+ fields1->Set("notify_contacts", notify);
+ fields1->Set("is_sticky", type == AcknowledgementSticky);
+ fields1->Set("end_time", DbValue::FromTimestamp(expiry));
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ if (service)
+ fields1->Set("state", service->GetState());
+ else
+ fields1->Set("state", GetHostState(host));
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+void DbEvents::AddAcknowledgement(const Checkable::Ptr& checkable, AcknowledgementType type)
+{
+ AddAcknowledgementInternal(checkable, type, true);
+}
+
+void DbEvents::RemoveAcknowledgement(const Checkable::Ptr& checkable)
+{
+ AddAcknowledgementInternal(checkable, AcknowledgementNone, false);
+}
+
+void DbEvents::AddAcknowledgementInternal(const Checkable::Ptr& checkable, AcknowledgementType type, bool add)
+{
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query1;
+ query1.WhereCriteria = new Dictionary();
+
+ if (service) {
+ query1.Table = "servicestatus";
+ query1.WhereCriteria->Set("service_object_id", service);
+ } else {
+ query1.Table = "hoststatus";
+ query1.WhereCriteria->Set("host_object_id", host);
+ }
+
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatState;
+ query1.StatusUpdate = true;
+ query1.Object = DbObject::GetOrCreateByObject(checkable);
+
+ query1.Fields = new Dictionary({
+ { "acknowledgement_type", type },
+ { "problem_has_been_acknowledged", add ? 1 : 0 }
+ });
+
+ query1.WhereCriteria->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ DbObject::OnQuery(query1);
+}
+
+/* notifications */
+void DbEvents::AddNotificationHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const std::set<User::Ptr>& users, NotificationType type,
+ const CheckResult::Ptr& cr, const String& author, const String& text)
+{
+ /* NotificationInsertID has to be tracked per IDO instance, therefore the OnQuery and OnMultipleQueries signals
+ * cannot be called directly as all IDO instances would insert rows with the same ID which is (most likely) only
+ * correct in one database. Instead, pass a lambda which generates the queries with new DbValue for
+ * NotificationInsertID each IDO instance.
+ */
+ DbObject::OnMakeQueries([&checkable, &users, &type, &cr](const DbObject::QueryCallbacks& callbacks) {
+ /* start and end happen at the same time */
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ DbQuery query1;
+ query1.Table = "notifications";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatNotification;
+ query1.NotificationInsertID = new DbValue(DbValueObjectInsertID, -1);
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ Dictionary::Ptr fields1 = new Dictionary();
+ fields1->Set("notification_type", 1); /* service */
+ fields1->Set("notification_reason", MapNotificationReasonType(type));
+ fields1->Set("object_id", checkable);
+ fields1->Set("start_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("start_time_usec", timeBag.second);
+ fields1->Set("end_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("end_time_usec", timeBag.second);
+
+ if (service)
+ fields1->Set("state", service->GetState());
+ else
+ fields1->Set("state", GetHostState(host));
+
+ if (cr) {
+ fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
+ fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
+ }
+
+ fields1->Set("escalated", 0);
+ fields1->Set("contacts_notified", static_cast<long>(users.size()));
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ callbacks.Query(query1);
+
+ std::vector<DbQuery> queries;
+
+ for (const User::Ptr& user : users) {
+ DbQuery query2;
+ query2.Table = "contactnotifications";
+ query2.Type = DbQueryInsert;
+ query2.Category = DbCatNotification;
+
+ query2.Fields = new Dictionary({
+ { "contact_object_id", user },
+ { "start_time", DbValue::FromTimestamp(timeBag.first) },
+ { "start_time_usec", timeBag.second },
+ { "end_time", DbValue::FromTimestamp(timeBag.first) },
+ { "end_time_usec", timeBag.second },
+ { "notification_id", query1.NotificationInsertID },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+
+ queries.emplace_back(std::move(query2));
+ }
+
+ callbacks.MultipleQueries(queries);
+ });
+}
+
+/* statehistory */
+void DbEvents::AddStateChangeHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type)
+{
+ double ts = cr->GetExecutionEnd();
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(ts);
+
+ DbQuery query1;
+ query1.Table = "statehistory";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatStateHistory;
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ Dictionary::Ptr fields1 = new Dictionary();
+ fields1->Set("state_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("state_time_usec", timeBag.second);
+ fields1->Set("object_id", checkable);
+ fields1->Set("state_change", 1); /* service */
+ fields1->Set("state_type", checkable->GetStateType());
+ fields1->Set("current_check_attempt", checkable->GetCheckAttempt());
+ fields1->Set("max_check_attempts", checkable->GetMaxCheckAttempts());
+
+ if (service) {
+ fields1->Set("state", service->GetState());
+ fields1->Set("last_state", service->GetLastState());
+ fields1->Set("last_hard_state", service->GetLastHardState());
+ } else {
+ fields1->Set("state", GetHostState(host));
+ fields1->Set("last_state", host->GetLastState());
+ fields1->Set("last_hard_state", host->GetLastHardState());
+ }
+
+ if (cr) {
+ fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
+ fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
+ fields1->Set("check_source", cr->GetCheckSource());
+ }
+
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+/* logentries */
+void DbEvents::AddCheckResultLogHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr)
+{
+ if (!cr)
+ return;
+
+ Dictionary::Ptr varsBefore = cr->GetVarsBefore();
+ Dictionary::Ptr varsAfter = cr->GetVarsAfter();
+
+ if (varsBefore && varsAfter) {
+ if (varsBefore->Get("state") == varsAfter->Get("state") &&
+ varsBefore->Get("state_type") == varsAfter->Get("state_type") &&
+ varsBefore->Get("attempt") == varsAfter->Get("attempt") &&
+ varsBefore->Get("reachable") == varsAfter->Get("reachable"))
+ return; /* Nothing changed, ignore this checkresult. */
+ }
+
+ LogEntryType type;
+ String output = CompatUtility::GetCheckResultOutput(cr);
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ std::ostringstream msgbuf;
+
+ if (service) {
+ msgbuf << "SERVICE ALERT: "
+ << host->GetName() << ";"
+ << service->GetShortName() << ";"
+ << Service::StateToString(service->GetState()) << ";"
+ << Service::StateTypeToString(service->GetStateType()) << ";"
+ << service->GetCheckAttempt() << ";"
+ << output << ""
+ << "";
+
+ switch (service->GetState()) {
+ case ServiceOK:
+ type = LogEntryTypeServiceOk;
+ break;
+ case ServiceUnknown:
+ type = LogEntryTypeServiceUnknown;
+ break;
+ case ServiceWarning:
+ type = LogEntryTypeServiceWarning;
+ break;
+ case ServiceCritical:
+ type = LogEntryTypeServiceCritical;
+ break;
+ default:
+ Log(LogCritical, "DbEvents")
+ << "Unknown service state: " << service->GetState();
+ return;
+ }
+ } else {
+ msgbuf << "HOST ALERT: "
+ << host->GetName() << ";"
+ << GetHostStateString(host) << ";"
+ << Host::StateTypeToString(host->GetStateType()) << ";"
+ << host->GetCheckAttempt() << ";"
+ << output << ""
+ << "";
+
+ switch (host->GetState()) {
+ case HostUp:
+ type = LogEntryTypeHostUp;
+ break;
+ case HostDown:
+ type = LogEntryTypeHostDown;
+ break;
+ default:
+ Log(LogCritical, "DbEvents")
+ << "Unknown host state: " << host->GetState();
+ return;
+ }
+
+ if (!host->IsReachable())
+ type = LogEntryTypeHostUnreachable;
+ }
+
+ AddLogHistory(checkable, msgbuf.str(), type);
+}
+
+void DbEvents::AddTriggerDowntimeLogHistory(const Downtime::Ptr& downtime)
+{
+ Checkable::Ptr checkable = downtime->GetCheckable();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ std::ostringstream msgbuf;
+
+ if (service) {
+ msgbuf << "SERVICE DOWNTIME ALERT: "
+ << host->GetName() << ";"
+ << service->GetShortName() << ";"
+ << "STARTED" << "; "
+ << "Service has entered a period of scheduled downtime."
+ << "";
+ } else {
+ msgbuf << "HOST DOWNTIME ALERT: "
+ << host->GetName() << ";"
+ << "STARTED" << "; "
+ << "Service has entered a period of scheduled downtime."
+ << "";
+ }
+
+ AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
+}
+
+void DbEvents::AddRemoveDowntimeLogHistory(const Downtime::Ptr& downtime)
+{
+ Checkable::Ptr checkable = downtime->GetCheckable();
+
+ String downtimeOutput;
+ String downtimeStateStr;
+
+ if (downtime->GetWasCancelled()) {
+ downtimeOutput = "Scheduled downtime for service has been cancelled.";
+ downtimeStateStr = "CANCELLED";
+ } else {
+ downtimeOutput = "Service has exited from a period of scheduled downtime.";
+ downtimeStateStr = "STOPPED";
+ }
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ std::ostringstream msgbuf;
+
+ if (service) {
+ msgbuf << "SERVICE DOWNTIME ALERT: "
+ << host->GetName() << ";"
+ << service->GetShortName() << ";"
+ << downtimeStateStr << "; "
+ << downtimeOutput
+ << "";
+ } else {
+ msgbuf << "HOST DOWNTIME ALERT: "
+ << host->GetName() << ";"
+ << downtimeStateStr << "; "
+ << downtimeOutput
+ << "";
+ }
+
+ AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
+}
+
+void DbEvents::AddNotificationSentLogHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable, const User::Ptr& user,
+ NotificationType notification_type, const CheckResult::Ptr& cr,
+ const String& author, const String& comment_text)
+{
+ CheckCommand::Ptr commandObj = checkable->GetCheckCommand();
+
+ String checkCommandName;
+
+ if (commandObj)
+ checkCommandName = commandObj->GetName();
+
+ String notificationTypeStr = Notification::NotificationTypeToStringCompat(notification_type); //TODO: Change that to our own types.
+
+ String author_comment = "";
+ if (notification_type == NotificationCustom || notification_type == NotificationAcknowledgement) {
+ author_comment = ";" + author + ";" + comment_text;
+ }
+
+ if (!cr)
+ return;
+
+ String output = CompatUtility::GetCheckResultOutput(cr);
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ std::ostringstream msgbuf;
+
+ if (service) {
+ msgbuf << "SERVICE NOTIFICATION: "
+ << user->GetName() << ";"
+ << host->GetName() << ";"
+ << service->GetShortName() << ";"
+ << notificationTypeStr << " "
+ << "(" << Service::StateToString(service->GetState()) << ");"
+ << checkCommandName << ";"
+ << output << author_comment
+ << "";
+ } else {
+ msgbuf << "HOST NOTIFICATION: "
+ << user->GetName() << ";"
+ << host->GetName() << ";"
+ << notificationTypeStr << " "
+ << "(" << Host::StateToString(host->GetState()) << ");"
+ << checkCommandName << ";"
+ << output << author_comment
+ << "";
+ }
+
+ AddLogHistory(checkable, msgbuf.str(), LogEntryTypeHostNotification);
+}
+
+void DbEvents::AddFlappingChangedLogHistory(const Checkable::Ptr& checkable)
+{
+ String flappingStateStr;
+ String flappingOutput;
+
+ if (checkable->IsFlapping()) {
+ flappingOutput = "Service appears to have started flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change >= " + Convert::ToString(checkable->GetFlappingThresholdHigh()) + "% threshold)";
+ flappingStateStr = "STARTED";
+ } else {
+ flappingOutput = "Service appears to have stopped flapping (" + Convert::ToString(checkable->GetFlappingCurrent()) + "% change < " + Convert::ToString(checkable->GetFlappingThresholdLow()) + "% threshold)";
+ flappingStateStr = "STOPPED";
+ }
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ std::ostringstream msgbuf;
+
+ if (service) {
+ msgbuf << "SERVICE FLAPPING ALERT: "
+ << host->GetName() << ";"
+ << service->GetShortName() << ";"
+ << flappingStateStr << "; "
+ << flappingOutput
+ << "";
+ } else {
+ msgbuf << "HOST FLAPPING ALERT: "
+ << host->GetName() << ";"
+ << flappingStateStr << "; "
+ << flappingOutput
+ << "";
+ }
+
+ AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
+}
+
+void DbEvents::AddEnableFlappingChangedLogHistory(const Checkable::Ptr& checkable)
+{
+ if (!checkable->GetEnableFlapping())
+ return;
+
+ String flappingOutput = "Flap detection has been disabled";
+ String flappingStateStr = "DISABLED";
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ std::ostringstream msgbuf;
+
+ if (service) {
+ msgbuf << "SERVICE FLAPPING ALERT: "
+ << host->GetName() << ";"
+ << service->GetShortName() << ";"
+ << flappingStateStr << "; "
+ << flappingOutput
+ << "";
+ } else {
+ msgbuf << "HOST FLAPPING ALERT: "
+ << host->GetName() << ";"
+ << flappingStateStr << "; "
+ << flappingOutput
+ << "";
+ }
+
+ AddLogHistory(checkable, msgbuf.str(), LogEntryTypeInfoMessage);
+}
+
+void DbEvents::AddLogHistory(const Checkable::Ptr& checkable, const String& buffer, LogEntryType type)
+{
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ DbQuery query1;
+ query1.Table = "logentries";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatLog;
+
+ Dictionary::Ptr fields1 = new Dictionary();
+
+ fields1->Set("logentry_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("entry_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("entry_time_usec", timeBag.second);
+ fields1->Set("object_id", checkable);
+ fields1->Set("logentry_type", type);
+ fields1->Set("logentry_data", buffer);
+
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+/* flappinghistory */
+void DbEvents::AddFlappingChangedHistory(const Checkable::Ptr& checkable)
+{
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ DbQuery query1;
+ query1.Table = "flappinghistory";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatFlapping;
+
+ Dictionary::Ptr fields1 = new Dictionary();
+
+ fields1->Set("event_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("event_time_usec", timeBag.second);
+
+ if (checkable->IsFlapping())
+ fields1->Set("event_type", 1000);
+ else {
+ fields1->Set("event_type", 1001);
+ fields1->Set("reason_type", 1);
+ }
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ fields1->Set("flapping_type", service ? 1 : 0);
+ fields1->Set("object_id", checkable);
+ fields1->Set("percent_state_change", checkable->GetFlappingCurrent());
+ fields1->Set("low_threshold", checkable->GetFlappingThresholdLow());
+ fields1->Set("high_threshold", checkable->GetFlappingThresholdHigh());
+
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+void DbEvents::AddEnableFlappingChangedHistory(const Checkable::Ptr& checkable)
+{
+ if (!checkable->GetEnableFlapping())
+ return;
+
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ DbQuery query1;
+ query1.Table = "flappinghistory";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatFlapping;
+
+ Dictionary::Ptr fields1 = new Dictionary();
+
+ fields1->Set("event_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("event_time_usec", timeBag.second);
+
+ fields1->Set("event_type", 1001);
+ fields1->Set("reason_type", 2);
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ fields1->Set("flapping_type", service ? 1 : 0);
+ fields1->Set("object_id", checkable);
+ fields1->Set("percent_state_change", checkable->GetFlappingCurrent());
+ fields1->Set("low_threshold", checkable->GetFlappingThresholdLow());
+ fields1->Set("high_threshold", checkable->GetFlappingThresholdHigh());
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+/* servicechecks */
+void DbEvents::AddCheckableCheckHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
+{
+ if (!cr)
+ return;
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ DbQuery query1;
+ query1.Table = service ? "servicechecks" : "hostchecks";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatCheck;
+
+ Dictionary::Ptr fields1 = new Dictionary();
+ fields1->Set("check_type", !checkable->GetEnableActiveChecks()); /* 0 .. active, 1 .. passive */
+ fields1->Set("current_check_attempt", checkable->GetCheckAttempt());
+ fields1->Set("max_check_attempts", checkable->GetMaxCheckAttempts());
+ fields1->Set("state_type", checkable->GetStateType());
+
+ double start = cr->GetExecutionStart();
+ double end = cr->GetExecutionEnd();
+ double executionTime = cr->CalculateExecutionTime();
+
+ std::pair<unsigned long, unsigned long> timeBagStart = ConvertTimestamp(start);
+ std::pair<unsigned long, unsigned long> timeBagEnd = ConvertTimestamp(end);
+
+ fields1->Set("start_time", DbValue::FromTimestamp(timeBagStart.first));
+ fields1->Set("start_time_usec", timeBagStart.second);
+ fields1->Set("end_time", DbValue::FromTimestamp(timeBagEnd.first));
+ fields1->Set("end_time_usec", timeBagEnd.second);
+ fields1->Set("command_object_id", checkable->GetCheckCommand());
+ fields1->Set("execution_time", executionTime);
+ fields1->Set("latency", cr->CalculateLatency());
+ fields1->Set("return_code", cr->GetExitStatus());
+ fields1->Set("perfdata", PluginUtility::FormatPerfdata(cr->GetPerformanceData()));
+
+ fields1->Set("output", CompatUtility::GetCheckResultOutput(cr));
+ fields1->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
+ fields1->Set("command_line", CompatUtility::GetCommandLine(checkable->GetCheckCommand()));
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ if (service) {
+ fields1->Set("service_object_id", service);
+ fields1->Set("state", service->GetState());
+ } else {
+ fields1->Set("host_object_id", host);
+ fields1->Set("state", GetHostState(host));
+ }
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+/* eventhandlers */
+void DbEvents::AddEventHandlerHistory(const Checkable::Ptr& checkable)
+{
+ DbQuery query1;
+ query1.Table = "eventhandlers";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatEventHandler;
+
+ Dictionary::Ptr fields1 = new Dictionary();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ fields1->Set("object_id", checkable);
+ fields1->Set("state_type", checkable->GetStateType());
+ fields1->Set("command_object_id", checkable->GetEventCommand());
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ if (service) {
+ fields1->Set("state", service->GetState());
+ fields1->Set("eventhandler_type", 1);
+ } else {
+ fields1->Set("state", GetHostState(host));
+ fields1->Set("eventhandler_type", 0);
+ }
+
+ std::pair<unsigned long, unsigned long> timeBag = ConvertTimestamp(Utility::GetTime());
+
+ fields1->Set("start_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("start_time_usec", timeBag.second);
+ fields1->Set("end_time", DbValue::FromTimestamp(timeBag.first));
+ fields1->Set("end_time_usec", timeBag.second);
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+/* externalcommands */
+void DbEvents::AddExternalCommandHistory(double time, const String& command, const std::vector<String>& arguments)
+{
+ DbQuery query1;
+ query1.Table = "externalcommands";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatExternalCommand;
+
+ Dictionary::Ptr fields1 = new Dictionary();
+
+ fields1->Set("entry_time", DbValue::FromTimestamp(time));
+ fields1->Set("command_type", MapExternalCommandType(command));
+ fields1->Set("command_name", command);
+ fields1->Set("command_args", boost::algorithm::join(arguments, ";"));
+ fields1->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(IcingaApplication::GetInstance()->GetNodeName());
+
+ if (endpoint)
+ fields1->Set("endpoint_object_id", endpoint);
+
+ query1.Fields = fields1;
+ DbObject::OnQuery(query1);
+}
+
+int DbEvents::GetHostState(const Host::Ptr& host)
+{
+ int currentState = host->GetState();
+
+ if (currentState != HostUp && !host->IsReachable())
+ currentState = 2; /* hardcoded compat state */
+
+ return currentState;
+}
+
+String DbEvents::GetHostStateString(const Host::Ptr& host)
+{
+ if (host->GetState() != HostUp && !host->IsReachable())
+ return "UNREACHABLE"; /* hardcoded compat state */
+
+ return Host::StateToString(host->GetState());
+}
+
+std::pair<unsigned long, unsigned long> DbEvents::ConvertTimestamp(double time)
+{
+ unsigned long time_sec = static_cast<long>(time);
+ unsigned long time_usec = (time - time_sec) * 1000 * 1000;
+
+ return std::make_pair(time_sec, time_usec);
+}
+
+int DbEvents::MapNotificationReasonType(NotificationType type)
+{
+ switch (type) {
+ case NotificationDowntimeStart:
+ return 5;
+ case NotificationDowntimeEnd:
+ return 6;
+ case NotificationDowntimeRemoved:
+ return 7;
+ case NotificationCustom:
+ return 8;
+ case NotificationAcknowledgement:
+ return 1;
+ case NotificationProblem:
+ return 0;
+ case NotificationRecovery:
+ return 0;
+ case NotificationFlappingStart:
+ return 2;
+ case NotificationFlappingEnd:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+int DbEvents::MapExternalCommandType(const String& name)
+{
+ if (name == "NONE")
+ return 0;
+ if (name == "ADD_HOST_COMMENT")
+ return 1;
+ if (name == "DEL_HOST_COMMENT")
+ return 2;
+ if (name == "ADD_SVC_COMMENT")
+ return 3;
+ if (name == "DEL_SVC_COMMENT")
+ return 4;
+ if (name == "ENABLE_SVC_CHECK")
+ return 5;
+ if (name == "DISABLE_SVC_CHECK")
+ return 6;
+ if (name == "SCHEDULE_SVC_CHECK")
+ return 7;
+ if (name == "DELAY_SVC_NOTIFICATION")
+ return 9;
+ if (name == "DELAY_HOST_NOTIFICATION")
+ return 10;
+ if (name == "DISABLE_NOTIFICATIONS")
+ return 11;
+ if (name == "ENABLE_NOTIFICATIONS")
+ return 12;
+ if (name == "RESTART_PROCESS")
+ return 13;
+ if (name == "SHUTDOWN_PROCESS")
+ return 14;
+ if (name == "ENABLE_HOST_SVC_CHECKS")
+ return 15;
+ if (name == "DISABLE_HOST_SVC_CHECKS")
+ return 16;
+ if (name == "SCHEDULE_HOST_SVC_CHECKS")
+ return 17;
+ if (name == "DELAY_HOST_SVC_NOTIFICATIONS")
+ return 19;
+ if (name == "DEL_ALL_HOST_COMMENTS")
+ return 20;
+ if (name == "DEL_ALL_SVC_COMMENTS")
+ return 21;
+ if (name == "ENABLE_SVC_NOTIFICATIONS")
+ return 22;
+ if (name == "DISABLE_SVC_NOTIFICATIONS")
+ return 23;
+ if (name == "ENABLE_HOST_NOTIFICATIONS")
+ return 24;
+ if (name == "DISABLE_HOST_NOTIFICATIONS")
+ return 25;
+ if (name == "ENABLE_ALL_NOTIFICATIONS_BEYOND_HOST")
+ return 26;
+ if (name == "DISABLE_ALL_NOTIFICATIONS_BEYOND_HOST")
+ return 27;
+ if (name == "ENABLE_HOST_SVC_NOTIFICATIONS")
+ return 28;
+ if (name == "DISABLE_HOST_SVC_NOTIFICATIONS")
+ return 29;
+ if (name == "PROCESS_SERVICE_CHECK_RESULT")
+ return 30;
+ if (name == "SAVE_STATE_INFORMATION")
+ return 31;
+ if (name == "READ_STATE_INFORMATION")
+ return 32;
+ if (name == "ACKNOWLEDGE_HOST_PROBLEM")
+ return 33;
+ if (name == "ACKNOWLEDGE_SVC_PROBLEM")
+ return 34;
+ if (name == "START_EXECUTING_SVC_CHECKS")
+ return 35;
+ if (name == "STOP_EXECUTING_SVC_CHECKS")
+ return 36;
+ if (name == "START_ACCEPTING_PASSIVE_SVC_CHECKS")
+ return 37;
+ if (name == "STOP_ACCEPTING_PASSIVE_SVC_CHECKS")
+ return 38;
+ if (name == "ENABLE_PASSIVE_SVC_CHECKS")
+ return 39;
+ if (name == "DISABLE_PASSIVE_SVC_CHECKS")
+ return 40;
+ if (name == "ENABLE_EVENT_HANDLERS")
+ return 41;
+ if (name == "DISABLE_EVENT_HANDLERS")
+ return 42;
+ if (name == "ENABLE_HOST_EVENT_HANDLER")
+ return 43;
+ if (name == "DISABLE_HOST_EVENT_HANDLER")
+ return 44;
+ if (name == "ENABLE_SVC_EVENT_HANDLER")
+ return 45;
+ if (name == "DISABLE_SVC_EVENT_HANDLER")
+ return 46;
+ if (name == "ENABLE_HOST_CHECK")
+ return 47;
+ if (name == "DISABLE_HOST_CHECK")
+ return 48;
+ if (name == "START_OBSESSING_OVER_SVC_CHECKS")
+ return 49;
+ if (name == "STOP_OBSESSING_OVER_SVC_CHECKS")
+ return 50;
+ if (name == "REMOVE_HOST_ACKNOWLEDGEMENT")
+ return 51;
+ if (name == "REMOVE_SVC_ACKNOWLEDGEMENT")
+ return 52;
+ if (name == "SCHEDULE_FORCED_HOST_SVC_CHECKS")
+ return 53;
+ if (name == "SCHEDULE_FORCED_SVC_CHECK")
+ return 54;
+ if (name == "SCHEDULE_HOST_DOWNTIME")
+ return 55;
+ if (name == "SCHEDULE_SVC_DOWNTIME")
+ return 56;
+ if (name == "ENABLE_HOST_FLAP_DETECTION")
+ return 57;
+ if (name == "DISABLE_HOST_FLAP_DETECTION")
+ return 58;
+ if (name == "ENABLE_SVC_FLAP_DETECTION")
+ return 59;
+ if (name == "DISABLE_SVC_FLAP_DETECTION")
+ return 60;
+ if (name == "ENABLE_FLAP_DETECTION")
+ return 61;
+ if (name == "DISABLE_FLAP_DETECTION")
+ return 62;
+ if (name == "ENABLE_HOSTGROUP_SVC_NOTIFICATIONS")
+ return 63;
+ if (name == "DISABLE_HOSTGROUP_SVC_NOTIFICATIONS")
+ return 64;
+ if (name == "ENABLE_HOSTGROUP_HOST_NOTIFICATIONS")
+ return 65;
+ if (name == "DISABLE_HOSTGROUP_HOST_NOTIFICATIONS")
+ return 66;
+ if (name == "ENABLE_HOSTGROUP_SVC_CHECKS")
+ return 67;
+ if (name == "DISABLE_HOSTGROUP_SVC_CHECKS")
+ return 68;
+ if (name == "CANCEL_HOST_DOWNTIME")
+ return 69;
+ if (name == "CANCEL_SVC_DOWNTIME")
+ return 70;
+ if (name == "CANCEL_ACTIVE_HOST_DOWNTIME")
+ return 71;
+ if (name == "CANCEL_PENDING_HOST_DOWNTIME")
+ return 72;
+ if (name == "CANCEL_ACTIVE_SVC_DOWNTIME")
+ return 73;
+ if (name == "CANCEL_PENDING_SVC_DOWNTIME")
+ return 74;
+ if (name == "CANCEL_ACTIVE_HOST_SVC_DOWNTIME")
+ return 75;
+ if (name == "CANCEL_PENDING_HOST_SVC_DOWNTIME")
+ return 76;
+ if (name == "FLUSH_PENDING_COMMANDS")
+ return 77;
+ if (name == "DEL_HOST_DOWNTIME")
+ return 78;
+ if (name == "DEL_SVC_DOWNTIME")
+ return 79;
+ if (name == "ENABLE_FAILURE_PREDICTION")
+ return 80;
+ if (name == "DISABLE_FAILURE_PREDICTION")
+ return 81;
+ if (name == "ENABLE_PERFORMANCE_DATA")
+ return 82;
+ if (name == "DISABLE_PERFORMANCE_DATA")
+ return 83;
+ if (name == "SCHEDULE_HOSTGROUP_HOST_DOWNTIME")
+ return 84;
+ if (name == "SCHEDULE_HOSTGROUP_SVC_DOWNTIME")
+ return 85;
+ if (name == "SCHEDULE_HOST_SVC_DOWNTIME")
+ return 86;
+ if (name == "PROCESS_HOST_CHECK_RESULT")
+ return 87;
+ if (name == "START_EXECUTING_HOST_CHECKS")
+ return 88;
+ if (name == "STOP_EXECUTING_HOST_CHECKS")
+ return 89;
+ if (name == "START_ACCEPTING_PASSIVE_HOST_CHECKS")
+ return 90;
+ if (name == "STOP_ACCEPTING_PASSIVE_HOST_CHECKS")
+ return 91;
+ if (name == "ENABLE_PASSIVE_HOST_CHECKS")
+ return 92;
+ if (name == "DISABLE_PASSIVE_HOST_CHECKS")
+ return 93;
+ if (name == "START_OBSESSING_OVER_HOST_CHECKS")
+ return 94;
+ if (name == "STOP_OBSESSING_OVER_HOST_CHECKS")
+ return 95;
+ if (name == "SCHEDULE_HOST_CHECK")
+ return 96;
+ if (name == "SCHEDULE_FORCED_HOST_CHECK")
+ return 98;
+ if (name == "START_OBSESSING_OVER_SVC")
+ return 99;
+ if (name == "STOP_OBSESSING_OVER_SVC")
+ return 100;
+ if (name == "START_OBSESSING_OVER_HOST")
+ return 101;
+ if (name == "STOP_OBSESSING_OVER_HOST")
+ return 102;
+ if (name == "ENABLE_HOSTGROUP_HOST_CHECKS")
+ return 103;
+ if (name == "DISABLE_HOSTGROUP_HOST_CHECKS")
+ return 104;
+ if (name == "ENABLE_HOSTGROUP_PASSIVE_SVC_CHECKS")
+ return 105;
+ if (name == "DISABLE_HOSTGROUP_PASSIVE_SVC_CHECKS")
+ return 106;
+ if (name == "ENABLE_HOSTGROUP_PASSIVE_HOST_CHECKS")
+ return 107;
+ if (name == "DISABLE_HOSTGROUP_PASSIVE_HOST_CHECKS")
+ return 108;
+ if (name == "ENABLE_SERVICEGROUP_SVC_NOTIFICATIONS")
+ return 109;
+ if (name == "DISABLE_SERVICEGROUP_SVC_NOTIFICATIONS")
+ return 110;
+ if (name == "ENABLE_SERVICEGROUP_HOST_NOTIFICATIONS")
+ return 111;
+ if (name == "DISABLE_SERVICEGROUP_HOST_NOTIFICATIONS")
+ return 112;
+ if (name == "ENABLE_SERVICEGROUP_SVC_CHECKS")
+ return 113;
+ if (name == "DISABLE_SERVICEGROUP_SVC_CHECKS")
+ return 114;
+ if (name == "ENABLE_SERVICEGROUP_HOST_CHECKS")
+ return 115;
+ if (name == "DISABLE_SERVICEGROUP_HOST_CHECKS")
+ return 116;
+ if (name == "ENABLE_SERVICEGROUP_PASSIVE_SVC_CHECKS")
+ return 117;
+ if (name == "DISABLE_SERVICEGROUP_PASSIVE_SVC_CHECKS")
+ return 118;
+ if (name == "ENABLE_SERVICEGROUP_PASSIVE_HOST_CHECKS")
+ return 119;
+ if (name == "DISABLE_SERVICEGROUP_PASSIVE_HOST_CHECKS")
+ return 120;
+ if (name == "SCHEDULE_SERVICEGROUP_HOST_DOWNTIME")
+ return 121;
+ if (name == "SCHEDULE_SERVICEGROUP_SVC_DOWNTIME")
+ return 122;
+ if (name == "CHANGE_GLOBAL_HOST_EVENT_HANDLER")
+ return 123;
+ if (name == "CHANGE_GLOBAL_SVC_EVENT_HANDLER")
+ return 124;
+ if (name == "CHANGE_HOST_EVENT_HANDLER")
+ return 125;
+ if (name == "CHANGE_SVC_EVENT_HANDLER")
+ return 126;
+ if (name == "CHANGE_HOST_CHECK_COMMAND")
+ return 127;
+ if (name == "CHANGE_SVC_CHECK_COMMAND")
+ return 128;
+ if (name == "CHANGE_NORMAL_HOST_CHECK_INTERVAL")
+ return 129;
+ if (name == "CHANGE_NORMAL_SVC_CHECK_INTERVAL")
+ return 130;
+ if (name == "CHANGE_RETRY_SVC_CHECK_INTERVAL")
+ return 131;
+ if (name == "CHANGE_MAX_HOST_CHECK_ATTEMPTS")
+ return 132;
+ if (name == "CHANGE_MAX_SVC_CHECK_ATTEMPTS")
+ return 133;
+ if (name == "SCHEDULE_AND_PROPAGATE_TRIGGERED_HOST_DOWNTIME")
+ return 134;
+ if (name == "ENABLE_HOST_AND_CHILD_NOTIFICATIONS")
+ return 135;
+ if (name == "DISABLE_HOST_AND_CHILD_NOTIFICATIONS")
+ return 136;
+ if (name == "SCHEDULE_AND_PROPAGATE_HOST_DOWNTIME")
+ return 137;
+ if (name == "ENABLE_SERVICE_FRESHNESS_CHECKS")
+ return 138;
+ if (name == "DISABLE_SERVICE_FRESHNESS_CHECKS")
+ return 139;
+ if (name == "ENABLE_HOST_FRESHNESS_CHECKS")
+ return 140;
+ if (name == "DISABLE_HOST_FRESHNESS_CHECKS")
+ return 141;
+ if (name == "SET_HOST_NOTIFICATION_NUMBER")
+ return 142;
+ if (name == "SET_SVC_NOTIFICATION_NUMBER")
+ return 143;
+ if (name == "CHANGE_HOST_CHECK_TIMEPERIOD")
+ return 144;
+ if (name == "CHANGE_SVC_CHECK_TIMEPERIOD")
+ return 145;
+ if (name == "PROCESS_FILE")
+ return 146;
+ if (name == "CHANGE_CUSTOM_HOST_VAR")
+ return 147;
+ if (name == "CHANGE_CUSTOM_SVC_VAR")
+ return 148;
+ if (name == "CHANGE_CUSTOM_CONTACT_VAR")
+ return 149;
+ if (name == "ENABLE_CONTACT_HOST_NOTIFICATIONS")
+ return 150;
+ if (name == "DISABLE_CONTACT_HOST_NOTIFICATIONS")
+ return 151;
+ if (name == "ENABLE_CONTACT_SVC_NOTIFICATIONS")
+ return 152;
+ if (name == "DISABLE_CONTACT_SVC_NOTIFICATIONS")
+ return 153;
+ if (name == "ENABLE_CONTACTGROUP_HOST_NOTIFICATIONS")
+ return 154;
+ if (name == "DISABLE_CONTACTGROUP_HOST_NOTIFICATIONS")
+ return 155;
+ if (name == "ENABLE_CONTACTGROUP_SVC_NOTIFICATIONS")
+ return 156;
+ if (name == "DISABLE_CONTACTGROUP_SVC_NOTIFICATIONS")
+ return 157;
+ if (name == "CHANGE_RETRY_HOST_CHECK_INTERVAL")
+ return 158;
+ if (name == "SEND_CUSTOM_HOST_NOTIFICATION")
+ return 159;
+ if (name == "SEND_CUSTOM_SVC_NOTIFICATION")
+ return 160;
+ if (name == "CHANGE_HOST_NOTIFICATION_TIMEPERIOD")
+ return 161;
+ if (name == "CHANGE_SVC_NOTIFICATION_TIMEPERIOD")
+ return 162;
+ if (name == "CHANGE_CONTACT_HOST_NOTIFICATION_TIMEPERIOD")
+ return 163;
+ if (name == "CHANGE_CONTACT_SVC_NOTIFICATION_TIMEPERIOD")
+ return 164;
+ if (name == "CHANGE_HOST_MODATTR")
+ return 165;
+ if (name == "CHANGE_SVC_MODATTR")
+ return 166;
+ if (name == "CHANGE_CONTACT_MODATTR")
+ return 167;
+ if (name == "CHANGE_CONTACT_MODHATTR")
+ return 168;
+ if (name == "CHANGE_CONTACT_MODSATTR")
+ return 169;
+ if (name == "SYNC_STATE_INFORMATION")
+ return 170;
+ if (name == "DEL_DOWNTIME_BY_HOST_NAME")
+ return 171;
+ if (name == "DEL_DOWNTIME_BY_HOSTGROUP_NAME")
+ return 172;
+ if (name == "DEL_DOWNTIME_BY_START_TIME_COMMENT")
+ return 173;
+ if (name == "ACKNOWLEDGE_HOST_PROBLEM_EXPIRE")
+ return 174;
+ if (name == "ACKNOWLEDGE_SVC_PROBLEM_EXPIRE")
+ return 175;
+ if (name == "DISABLE_NOTIFICATIONS_EXPIRE_TIME")
+ return 176;
+ if (name == "CUSTOM_COMMAND")
+ return 999;
+
+ return 0;
+}
diff --git a/lib/db_ido/dbevents.hpp b/lib/db_ido/dbevents.hpp
new file mode 100644
index 0000000..858f3b3
--- /dev/null
+++ b/lib/db_ido/dbevents.hpp
@@ -0,0 +1,128 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DBEVENTS_H
+#define DBEVENTS_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+#include "icinga/service.hpp"
+
+namespace icinga
+{
+
+enum LogEntryType
+{
+ LogEntryTypeRuntimeError = 1,
+ LogEntryTypeRuntimeWarning = 2,
+ LogEntryTypeVerificationError = 4,
+ LogEntryTypeVerificationWarning = 8,
+ LogEntryTypeConfigError = 16,
+ LogEntryTypeConfigWarning = 32,
+ LogEntryTypeProcessInfo = 64,
+ LogEntryTypeEventHandler = 128,
+ LogEntryTypeExternalCommand = 512,
+ LogEntryTypeHostUp = 1024,
+ LogEntryTypeHostDown = 2048,
+ LogEntryTypeHostUnreachable = 4096,
+ LogEntryTypeServiceOk = 8192,
+ LogEntryTypeServiceUnknown = 16384,
+ LogEntryTypeServiceWarning = 32768,
+ LogEntryTypeServiceCritical = 65536,
+ LogEntryTypePassiveCheck = 1231072,
+ LogEntryTypeInfoMessage = 262144,
+ LogEntryTypeHostNotification = 524288,
+ LogEntryTypeServiceNotification = 1048576
+};
+
+/**
+ * IDO events
+ *
+ * @ingroup ido
+ */
+class DbEvents
+{
+public:
+ static void StaticInitialize();
+
+ static void AddComments(const Checkable::Ptr& checkable);
+
+ static void AddDowntimes(const Checkable::Ptr& checkable);
+ static void RemoveDowntimes(const Checkable::Ptr& checkable);
+
+ static void AddLogHistory(const Checkable::Ptr& checkable, const String& buffer, LogEntryType type);
+
+ /* Status */
+ static void NextCheckUpdatedHandler(const Checkable::Ptr& checkable);
+ static void FlappingChangedHandler(const Checkable::Ptr& checkable);
+ static void LastNotificationChangedHandler(const Notification::Ptr& notification, const Checkable::Ptr& checkable);
+
+ static void EnableActiveChecksChangedHandler(const Checkable::Ptr& checkable);
+ static void EnablePassiveChecksChangedHandler(const Checkable::Ptr& checkable);
+ static void EnableNotificationsChangedHandler(const Checkable::Ptr& checkable);
+ static void EnablePerfdataChangedHandler(const Checkable::Ptr& checkable);
+ static void EnableFlappingChangedHandler(const Checkable::Ptr& checkable);
+
+ static void AddComment(const Comment::Ptr& comment);
+ static void RemoveComment(const Comment::Ptr& comment);
+
+ static void AddDowntime(const Downtime::Ptr& downtime);
+ static void RemoveDowntime(const Downtime::Ptr& downtime);
+ static void TriggerDowntime(const Downtime::Ptr& downtime);
+
+ static void AddAcknowledgement(const Checkable::Ptr& checkable, AcknowledgementType type);
+ static void RemoveAcknowledgement(const Checkable::Ptr& checkable);
+ static void AddAcknowledgementInternal(const Checkable::Ptr& checkable, AcknowledgementType type, bool add);
+
+ static void ReachabilityChangedHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, std::set<Checkable::Ptr> children);
+
+ /* comment, downtime, acknowledgement history */
+ static void AddCommentHistory(const Comment::Ptr& comment);
+ static void AddDowntimeHistory(const Downtime::Ptr& downtime);
+ static void AddAcknowledgementHistory(const Checkable::Ptr& checkable, const String& author, const String& comment,
+ AcknowledgementType type, bool notify, double expiry);
+
+ /* notification & contactnotification history */
+ static void AddNotificationHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable,
+ const std::set<User::Ptr>& users, NotificationType type, const CheckResult::Ptr& cr, const String& author,
+ const String& text);
+
+ /* statehistory */
+ static void AddStateChangeHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr, StateType type);
+
+ /* logentries */
+ static void AddCheckResultLogHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr);
+ static void AddTriggerDowntimeLogHistory(const Downtime::Ptr& downtime);
+ static void AddRemoveDowntimeLogHistory(const Downtime::Ptr& downtime);
+ static void AddNotificationSentLogHistory(const Notification::Ptr& notification, const Checkable::Ptr& checkable,
+ const User::Ptr& user, NotificationType notification_type, const CheckResult::Ptr& cr, const String& author,
+ const String& comment_text);
+
+ static void AddFlappingChangedLogHistory(const Checkable::Ptr& checkable);
+ static void AddEnableFlappingChangedLogHistory(const Checkable::Ptr& checkable);
+
+ /* other history */
+ static void AddFlappingChangedHistory(const Checkable::Ptr& checkable);
+ static void AddEnableFlappingChangedHistory(const Checkable::Ptr& checkable);
+ static void AddCheckableCheckHistory(const Checkable::Ptr& checkable, const CheckResult::Ptr &cr);
+ static void AddEventHandlerHistory(const Checkable::Ptr& checkable);
+ static void AddExternalCommandHistory(double time, const String& command, const std::vector<String>& arguments);
+
+private:
+ DbEvents();
+
+ static void AddCommentInternal(std::vector<DbQuery>& queries, const Comment::Ptr& comment, bool historical);
+ static void RemoveCommentInternal(std::vector<DbQuery>& queries, const Comment::Ptr& comment);
+ static void AddDowntimeInternal(std::vector<DbQuery>& queries, const Downtime::Ptr& downtime, bool historical);
+ static void RemoveDowntimeInternal(std::vector<DbQuery>& queries, const Downtime::Ptr& downtime);
+ static void EnableChangedHandlerInternal(const Checkable::Ptr& checkable, const String& fieldName, bool enabled);
+
+ static int GetHostState(const Host::Ptr& host);
+ static String GetHostStateString(const Host::Ptr& host);
+ static std::pair<unsigned long, unsigned long> ConvertTimestamp(double time);
+ static int MapNotificationReasonType(NotificationType type);
+ static int MapExternalCommandType(const String& name);
+};
+
+}
+
+#endif /* DBEVENTS_H */
diff --git a/lib/db_ido/dbobject.cpp b/lib/db_ido/dbobject.cpp
new file mode 100644
index 0000000..406bf52
--- /dev/null
+++ b/lib/db_ido/dbobject.cpp
@@ -0,0 +1,430 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "icinga/customvarobject.hpp"
+#include "icinga/service.hpp"
+#include "icinga/compatutility.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/eventcommand.hpp"
+#include "icinga/notificationcommand.hpp"
+#include "remote/endpoint.hpp"
+#include "base/configobject.hpp"
+#include "base/configtype.hpp"
+#include "base/json.hpp"
+#include "base/serializer.hpp"
+#include "base/json.hpp"
+#include "base/convert.hpp"
+#include "base/objectlock.hpp"
+#include "base/utility.hpp"
+#include "base/initialize.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+boost::signals2::signal<void (const DbQuery&)> DbObject::OnQuery;
+boost::signals2::signal<void (const std::vector<DbQuery>&)> DbObject::OnMultipleQueries;
+boost::signals2::signal<void (const std::function<void (const DbObject::QueryCallbacks&)>&)> DbObject::OnMakeQueries;
+
+INITIALIZE_ONCE(&DbObject::StaticInitialize);
+
+DbObject::DbObject(intrusive_ptr<DbType> type, String name1, String name2)
+ : m_Name1(std::move(name1)), m_Name2(std::move(name2)), m_Type(std::move(type)), m_LastConfigUpdate(0), m_LastStatusUpdate(0)
+{ }
+
+void DbObject::StaticInitialize()
+{
+ /* triggered in ProcessCheckResult(), requires UpdateNextCheck() to be called before */
+ ConfigObject::OnStateChanged.connect([](const ConfigObject::Ptr& object) { StateChangedHandler(object); });
+ CustomVarObject::OnVarsChanged.connect([](const CustomVarObject::Ptr& customVar, const Value&) { VarsChangedHandler(customVar); });
+
+ /* triggered on create, update and delete objects */
+ ConfigObject::OnVersionChanged.connect([](const ConfigObject::Ptr& object, const Value&) { VersionChangedHandler(object); });
+}
+
+void DbObject::SetObject(const ConfigObject::Ptr& object)
+{
+ m_Object = object;
+}
+
+ConfigObject::Ptr DbObject::GetObject() const
+{
+ return m_Object;
+}
+
+String DbObject::GetName1() const
+{
+ return m_Name1;
+}
+
+String DbObject::GetName2() const
+{
+ return m_Name2;
+}
+
+DbType::Ptr DbObject::GetType() const
+{
+ return m_Type;
+}
+
+String DbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) const
+{
+ Dictionary::Ptr configFieldsDup = configFields->ShallowClone();
+
+ {
+ ObjectLock olock(configFieldsDup);
+
+ for (const Dictionary::Pair& kv : configFieldsDup) {
+ if (kv.second.IsObjectType<ConfigObject>()) {
+ ConfigObject::Ptr obj = kv.second;
+ configFieldsDup->Set(kv.first, obj->GetName());
+ }
+ }
+ }
+
+ Array::Ptr data = new Array();
+ data->Add(configFieldsDup);
+
+ CustomVarObject::Ptr custom_var_object = dynamic_pointer_cast<CustomVarObject>(GetObject());
+
+ if (custom_var_object)
+ data->Add(custom_var_object->GetVars());
+
+ return HashValue(data);
+}
+
+String DbObject::HashValue(const Value& value)
+{
+ Value temp;
+
+ Type::Ptr type = value.GetReflectionType();
+
+ if (ConfigObject::TypeInstance->IsAssignableFrom(type))
+ temp = Serialize(value, FAConfig);
+ else
+ temp = value;
+
+ return SHA256(JsonEncode(temp));
+}
+
+void DbObject::SendConfigUpdateHeavy(const Dictionary::Ptr& configFields)
+{
+ /* update custom var config and status */
+ SendVarsConfigUpdateHeavy();
+
+ /* config attributes */
+ if (!configFields)
+ return;
+
+ ASSERT(configFields->Contains("config_hash"));
+
+ ConfigObject::Ptr object = GetObject();
+
+ DbQuery query;
+ query.Table = GetType()->GetTable() + "s";
+ query.Type = DbQueryInsert | DbQueryUpdate;
+ query.Category = DbCatConfig;
+ query.Fields = configFields;
+ query.Fields->Set(GetType()->GetIDColumn(), object);
+ query.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
+ query.Fields->Set("config_type", 1);
+ query.WhereCriteria = new Dictionary({
+ { GetType()->GetIDColumn(), object }
+ });
+ query.Object = this;
+ query.ConfigUpdate = true;
+ OnQuery(query);
+
+ m_LastConfigUpdate = Utility::GetTime();
+
+ OnConfigUpdateHeavy();
+}
+
+void DbObject::SendConfigUpdateLight()
+{
+ OnConfigUpdateLight();
+}
+
+void DbObject::SendStatusUpdate()
+{
+ /* status attributes */
+ Dictionary::Ptr fields = GetStatusFields();
+
+ if (!fields)
+ return;
+
+ DbQuery query;
+ query.Table = GetType()->GetTable() + "status";
+ query.Type = DbQueryInsert | DbQueryUpdate;
+ query.Category = DbCatState;
+ query.Fields = fields;
+ query.Fields->Set(GetType()->GetIDColumn(), GetObject());
+
+ /* do not override endpoint_object_id for endpoints & zones */
+ if (query.Table != "endpointstatus" && query.Table != "zonestatus") {
+ String node = IcingaApplication::GetInstance()->GetNodeName();
+
+ Endpoint::Ptr endpoint = Endpoint::GetByName(node);
+ if (endpoint)
+ query.Fields->Set("endpoint_object_id", endpoint);
+ }
+
+ query.Fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+ query.Fields->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
+ query.WhereCriteria = new Dictionary({
+ { GetType()->GetIDColumn(), GetObject() }
+ });
+ query.Object = this;
+ query.StatusUpdate = true;
+ OnQuery(query);
+
+ m_LastStatusUpdate = Utility::GetTime();
+
+ OnStatusUpdate();
+}
+
+void DbObject::SendVarsConfigUpdateHeavy()
+{
+ ConfigObject::Ptr obj = GetObject();
+
+ CustomVarObject::Ptr custom_var_object = dynamic_pointer_cast<CustomVarObject>(obj);
+
+ if (!custom_var_object)
+ return;
+
+ std::vector<DbQuery> queries;
+
+ DbQuery query1;
+ query1.Table = "customvariables";
+ query1.Type = DbQueryDelete;
+ query1.Category = DbCatConfig;
+ query1.WhereCriteria = new Dictionary({
+ { "object_id", obj }
+ });
+ queries.emplace_back(std::move(query1));
+
+ DbQuery query2;
+ query2.Table = "customvariablestatus";
+ query2.Type = DbQueryDelete;
+ query2.Category = DbCatConfig;
+ query2.WhereCriteria = new Dictionary({
+ { "object_id", obj }
+ });
+ queries.emplace_back(std::move(query2));
+
+ Dictionary::Ptr vars = custom_var_object->GetVars();
+
+ if (vars) {
+ ObjectLock olock (vars);
+
+ for (const Dictionary::Pair& kv : vars) {
+ if (kv.first.IsEmpty())
+ continue;
+
+ String value;
+ int is_json = 0;
+
+ if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) {
+ value = JsonEncode(kv.second);
+ is_json = 1;
+ } else
+ value = kv.second;
+
+ DbQuery query3;
+ query3.Table = "customvariables";
+ query3.Type = DbQueryInsert;
+ query3.Category = DbCatConfig;
+ query3.Fields = new Dictionary({
+ { "varname", kv.first },
+ { "varvalue", value },
+ { "is_json", is_json },
+ { "config_type", 1 },
+ { "object_id", obj },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ queries.emplace_back(std::move(query3));
+
+ DbQuery query4;
+ query4.Table = "customvariablestatus";
+ query4.Type = DbQueryInsert;
+ query4.Category = DbCatState;
+
+ query4.Fields = new Dictionary({
+ { "varname", kv.first },
+ { "varvalue", value },
+ { "is_json", is_json },
+ { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) },
+ { "object_id", obj },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+
+ queries.emplace_back(std::move(query4));
+ }
+ }
+
+ OnMultipleQueries(queries);
+}
+
+void DbObject::SendVarsStatusUpdate()
+{
+ ConfigObject::Ptr obj = GetObject();
+
+ CustomVarObject::Ptr custom_var_object = dynamic_pointer_cast<CustomVarObject>(obj);
+
+ if (!custom_var_object)
+ return;
+
+ Dictionary::Ptr vars = custom_var_object->GetVars();
+
+ if (vars) {
+ std::vector<DbQuery> queries;
+ ObjectLock olock (vars);
+
+ for (const Dictionary::Pair& kv : vars) {
+ if (kv.first.IsEmpty())
+ continue;
+
+ String value;
+ int is_json = 0;
+
+ if (kv.second.IsObjectType<Array>() || kv.second.IsObjectType<Dictionary>()) {
+ value = JsonEncode(kv.second);
+ is_json = 1;
+ } else
+ value = kv.second;
+
+ DbQuery query;
+ query.Table = "customvariablestatus";
+ query.Type = DbQueryInsert | DbQueryUpdate;
+ query.Category = DbCatState;
+
+ query.Fields = new Dictionary({
+ { "varname", kv.first },
+ { "varvalue", value },
+ { "is_json", is_json },
+ { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) },
+ { "object_id", obj },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+
+ query.WhereCriteria = new Dictionary({
+ { "object_id", obj },
+ { "varname", kv.first }
+ });
+
+ queries.emplace_back(std::move(query));
+ }
+
+ OnMultipleQueries(queries);
+ }
+}
+
+double DbObject::GetLastConfigUpdate() const
+{
+ return m_LastConfigUpdate;
+}
+
+double DbObject::GetLastStatusUpdate() const
+{
+ return m_LastStatusUpdate;
+}
+
+void DbObject::OnConfigUpdateHeavy()
+{
+ /* Default handler does nothing. */
+}
+
+void DbObject::OnConfigUpdateLight()
+{
+ /* Default handler does nothing. */
+}
+
+void DbObject::OnStatusUpdate()
+{
+ /* Default handler does nothing. */
+}
+
+DbObject::Ptr DbObject::GetOrCreateByObject(const ConfigObject::Ptr& object)
+{
+ std::unique_lock<std::mutex> lock(GetStaticMutex());
+
+ DbObject::Ptr dbobj = object->GetExtension("DbObject");
+
+ if (dbobj)
+ return dbobj;
+
+ DbType::Ptr dbtype = DbType::GetByName(object->GetReflectionType()->GetName());
+
+ if (!dbtype)
+ return nullptr;
+
+ Service::Ptr service;
+ String name1, name2;
+
+ service = dynamic_pointer_cast<Service>(object);
+
+ if (service) {
+ Host::Ptr host = service->GetHost();
+
+ name1 = service->GetHost()->GetName();
+ name2 = service->GetShortName();
+ } else {
+ if (object->GetReflectionType() == CheckCommand::TypeInstance ||
+ object->GetReflectionType() == EventCommand::TypeInstance ||
+ object->GetReflectionType() == NotificationCommand::TypeInstance) {
+ Command::Ptr command = dynamic_pointer_cast<Command>(object);
+ name1 = CompatUtility::GetCommandName(command);
+ }
+ else
+ name1 = object->GetName();
+ }
+
+ dbobj = dbtype->GetOrCreateObjectByName(name1, name2);
+
+ dbobj->SetObject(object);
+ object->SetExtension("DbObject", dbobj);
+
+ return dbobj;
+}
+
+void DbObject::StateChangedHandler(const ConfigObject::Ptr& object)
+{
+ DbObject::Ptr dbobj = GetOrCreateByObject(object);
+
+ if (!dbobj)
+ return;
+
+ dbobj->SendStatusUpdate();
+}
+
+void DbObject::VarsChangedHandler(const CustomVarObject::Ptr& object)
+{
+ DbObject::Ptr dbobj = GetOrCreateByObject(object);
+
+ if (!dbobj)
+ return;
+
+ dbobj->SendVarsStatusUpdate();
+}
+
+void DbObject::VersionChangedHandler(const ConfigObject::Ptr& object)
+{
+ DbObject::Ptr dbobj = DbObject::GetOrCreateByObject(object);
+
+ if (dbobj) {
+ Dictionary::Ptr configFields = dbobj->GetConfigFields();
+ String configHash = dbobj->CalculateConfigHash(configFields);
+ configFields->Set("config_hash", configHash);
+
+ dbobj->SendConfigUpdateHeavy(configFields);
+ dbobj->SendStatusUpdate();
+ }
+}
+
+std::mutex& DbObject::GetStaticMutex()
+{
+ static std::mutex mutex;
+ return mutex;
+}
diff --git a/lib/db_ido/dbobject.hpp b/lib/db_ido/dbobject.hpp
new file mode 100644
index 0000000..399b77d
--- /dev/null
+++ b/lib/db_ido/dbobject.hpp
@@ -0,0 +1,112 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DBOBJECT_H
+#define DBOBJECT_H
+
+#include "db_ido/i2-db_ido.hpp"
+#include "db_ido/dbreference.hpp"
+#include "db_ido/dbquery.hpp"
+#include "db_ido/dbtype.hpp"
+#include "icinga/customvarobject.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+enum DbObjectUpdateType
+{
+ DbObjectCreated,
+ DbObjectRemoved
+};
+
+enum DbObjectType
+{
+ DbObjectTypeHost = 1,
+ DbObjectTypeService = 2,
+ DbObjectTypeHostGroup = 3,
+ DbObjectTypeServiceGroup = 4,
+ DbObjectTypeHostEscalation = 5,
+ DbObjectTypeServiceEscalation = 6,
+ DbObjectTypeHostDependency = 7,
+ DbObjectTypeServiceDependency = 8,
+ DbObjectTypeTimePeriod = 9,
+ DbObjectTypeContact = 10,
+ DbObjectTypeContactGroup = 11,
+ DbObjectTypeCommand = 12,
+ DbObjectTypeEndpoint = 13,
+ DbObjectTypeZone = 14,
+};
+
+/**
+ * A database object.
+ *
+ * @ingroup ido
+ */
+class DbObject : public Object
+{
+public:
+ DECLARE_PTR_TYPEDEFS(DbObject);
+
+ static void StaticInitialize();
+
+ void SetObject(const ConfigObject::Ptr& object);
+ ConfigObject::Ptr GetObject() const;
+
+ String GetName1() const;
+ String GetName2() const;
+ intrusive_ptr<DbType> GetType() const;
+
+ virtual Dictionary::Ptr GetConfigFields() const = 0;
+ virtual Dictionary::Ptr GetStatusFields() const = 0;
+
+ static DbObject::Ptr GetOrCreateByObject(const ConfigObject::Ptr& object);
+
+ struct QueryCallbacks {
+ std::function<void(const DbQuery&)> Query;
+ std::function<void(const std::vector<DbQuery>&)> MultipleQueries;
+ };
+
+ static boost::signals2::signal<void (const DbQuery&)> OnQuery;
+ static boost::signals2::signal<void (const std::vector<DbQuery>&)> OnMultipleQueries;
+ static boost::signals2::signal<void (const std::function<void (const QueryCallbacks&)>&)> OnMakeQueries;
+
+ void SendConfigUpdateHeavy(const Dictionary::Ptr& configFields);
+ void SendConfigUpdateLight();
+ void SendStatusUpdate();
+ void SendVarsConfigUpdateHeavy();
+ void SendVarsStatusUpdate();
+
+ double GetLastConfigUpdate() const;
+ double GetLastStatusUpdate() const;
+
+ virtual String CalculateConfigHash(const Dictionary::Ptr& configFields) const;
+
+protected:
+ DbObject(intrusive_ptr<DbType> type, String name1, String name2);
+
+ virtual void OnConfigUpdateHeavy();
+ virtual void OnConfigUpdateLight();
+ virtual void OnStatusUpdate();
+
+ static String HashValue(const Value& value);
+
+private:
+ String m_Name1;
+ String m_Name2;
+ intrusive_ptr<DbType> m_Type;
+ ConfigObject::Ptr m_Object;
+ double m_LastConfigUpdate;
+ double m_LastStatusUpdate;
+
+ static void StateChangedHandler(const ConfigObject::Ptr& object);
+ static void VarsChangedHandler(const CustomVarObject::Ptr& object);
+ static void VersionChangedHandler(const ConfigObject::Ptr& object);
+
+ static std::mutex& GetStaticMutex();
+
+ friend class DbType;
+};
+
+}
+
+#endif /* DBOBJECT_H */
diff --git a/lib/db_ido/dbquery.cpp b/lib/db_ido/dbquery.cpp
new file mode 100644
index 0000000..01196a5
--- /dev/null
+++ b/lib/db_ido/dbquery.cpp
@@ -0,0 +1,52 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbquery.hpp"
+#include "base/initialize.hpp"
+#include "base/scriptglobal.hpp"
+
+using namespace icinga;
+
+INITIALIZE_ONCE(&DbQuery::StaticInitialize);
+
+std::map<String, int> DbQuery::m_CategoryFilterMap;
+
+void DbQuery::StaticInitialize()
+{
+ ScriptGlobal::Set("Icinga.DbCatConfig", DbCatConfig, true);
+ ScriptGlobal::Set("Icinga.DbCatState", DbCatState, true);
+ ScriptGlobal::Set("Icinga.DbCatAcknowledgement", DbCatAcknowledgement, true);
+ ScriptGlobal::Set("Icinga.DbCatComment", DbCatComment, true);
+ ScriptGlobal::Set("Icinga.DbCatDowntime", DbCatDowntime, true);
+ ScriptGlobal::Set("Icinga.DbCatEventHandler", DbCatEventHandler, true);
+ ScriptGlobal::Set("Icinga.DbCatExternalCommand", DbCatExternalCommand, true);
+ ScriptGlobal::Set("Icinga.DbCatFlapping", DbCatFlapping, true);
+ ScriptGlobal::Set("Icinga.DbCatCheck", DbCatCheck, true);
+ ScriptGlobal::Set("Icinga.DbCatLog", DbCatLog, true);
+ ScriptGlobal::Set("Icinga.DbCatNotification", DbCatNotification, true);
+ ScriptGlobal::Set("Icinga.DbCatProgramStatus", DbCatProgramStatus, true);
+ ScriptGlobal::Set("Icinga.DbCatRetention", DbCatRetention, true);
+ ScriptGlobal::Set("Icinga.DbCatStateHistory", DbCatStateHistory, true);
+
+ ScriptGlobal::Set("Icinga.DbCatEverything", DbCatEverything, true);
+
+ m_CategoryFilterMap["DbCatConfig"] = DbCatConfig;
+ m_CategoryFilterMap["DbCatState"] = DbCatState;
+ m_CategoryFilterMap["DbCatAcknowledgement"] = DbCatAcknowledgement;
+ m_CategoryFilterMap["DbCatComment"] = DbCatComment;
+ m_CategoryFilterMap["DbCatDowntime"] = DbCatDowntime;
+ m_CategoryFilterMap["DbCatEventHandler"] = DbCatEventHandler;
+ m_CategoryFilterMap["DbCatExternalCommand"] = DbCatExternalCommand;
+ m_CategoryFilterMap["DbCatFlapping"] = DbCatFlapping;
+ m_CategoryFilterMap["DbCatCheck"] = DbCatCheck;
+ m_CategoryFilterMap["DbCatLog"] = DbCatLog;
+ m_CategoryFilterMap["DbCatNotification"] = DbCatNotification;
+ m_CategoryFilterMap["DbCatProgramStatus"] = DbCatProgramStatus;
+ m_CategoryFilterMap["DbCatRetention"] = DbCatRetention;
+ m_CategoryFilterMap["DbCatStateHistory"] = DbCatStateHistory;
+ m_CategoryFilterMap["DbCatEverything"] = DbCatEverything;
+}
+
+const std::map<String, int>& DbQuery::GetCategoryFilterMap()
+{
+ return m_CategoryFilterMap;
+}
diff --git a/lib/db_ido/dbquery.hpp b/lib/db_ido/dbquery.hpp
new file mode 100644
index 0000000..fecb2e3
--- /dev/null
+++ b/lib/db_ido/dbquery.hpp
@@ -0,0 +1,72 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DBQUERY_H
+#define DBQUERY_H
+
+#include "db_ido/i2-db_ido.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "icinga/customvarobject.hpp"
+#include "base/dictionary.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+enum DbQueryType
+{
+ DbQueryInsert = 1,
+ DbQueryUpdate = 2,
+ DbQueryDelete = 4,
+ DbQueryNewTransaction = 8
+};
+
+enum DbQueryCategory
+{
+ DbCatInvalid = 0, //-1 is required for DbCatEverything
+ DbCatEverything = ~0,
+
+ DbCatConfig = 1,
+ DbCatState = 2,
+ DbCatAcknowledgement = 4,
+ DbCatComment = 8,
+ DbCatDowntime = 16,
+ DbCatEventHandler = 32,
+ DbCatExternalCommand = 64,
+ DbCatFlapping = 128,
+ DbCatCheck = 256,
+ DbCatLog = 512,
+ DbCatNotification = 1024,
+ DbCatProgramStatus = 2048,
+ DbCatRetention = 4096,
+ DbCatStateHistory = 8192
+};
+
+class DbObject;
+
+struct DbQuery
+{
+ int Type{0};
+ DbQueryCategory Category{DbCatInvalid};
+ String Table;
+ String IdColumn;
+ Dictionary::Ptr Fields;
+ Dictionary::Ptr WhereCriteria;
+ intrusive_ptr<DbObject> Object;
+ DbValue::Ptr NotificationInsertID;
+ bool ConfigUpdate{false};
+ bool StatusUpdate{false};
+ WorkQueuePriority Priority{PriorityNormal};
+
+ static void StaticInitialize();
+
+ static const std::map<String, int>& GetCategoryFilterMap();
+
+private:
+ static std::map<String, int> m_CategoryFilterMap;
+};
+
+}
+
+#endif /* DBQUERY_H */
+
+#include "db_ido/dbobject.hpp"
diff --git a/lib/db_ido/dbreference.cpp b/lib/db_ido/dbreference.cpp
new file mode 100644
index 0000000..e8f13c0
--- /dev/null
+++ b/lib/db_ido/dbreference.cpp
@@ -0,0 +1,19 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "dbreference.hpp"
+
+using namespace icinga;
+
+DbReference::DbReference(long id)
+ : m_Id(id)
+{ }
+
+bool DbReference::IsValid() const
+{
+ return (m_Id != -1);
+}
+
+DbReference::operator long() const
+{
+ return m_Id;
+}
diff --git a/lib/db_ido/dbreference.hpp b/lib/db_ido/dbreference.hpp
new file mode 100644
index 0000000..70edf9a
--- /dev/null
+++ b/lib/db_ido/dbreference.hpp
@@ -0,0 +1,30 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DBREFERENCE_H
+#define DBREFERENCE_H
+
+#include "db_ido/i2-db_ido.hpp"
+
+namespace icinga
+{
+
+/**
+ * A database reference.
+ *
+ * @ingroup ido
+ */
+struct DbReference
+{
+public:
+ DbReference() = default;
+ DbReference(long id);
+
+ bool IsValid() const;
+ operator long() const;
+private:
+ long m_Id{-1};
+};
+
+}
+
+#endif /* DBREFERENCE_H */
diff --git a/lib/db_ido/dbtype.cpp b/lib/db_ido/dbtype.cpp
new file mode 100644
index 0000000..bc45dcb
--- /dev/null
+++ b/lib/db_ido/dbtype.cpp
@@ -0,0 +1,141 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbconnection.hpp"
+#include "base/objectlock.hpp"
+#include "base/debug.hpp"
+#include <boost/thread/once.hpp>
+
+using namespace icinga;
+
+DbType::DbType(String name, String table, long tid, String idcolumn, DbType::ObjectFactory factory)
+ : m_Name(std::move(name)), m_Table(std::move(table)), m_TypeID(tid), m_IDColumn(std::move(idcolumn)), m_ObjectFactory(std::move(factory))
+{ }
+
+String DbType::GetName() const
+{
+ return m_Name;
+}
+
+String DbType::GetTable() const
+{
+ return m_Table;
+}
+
+long DbType::GetTypeID() const
+{
+ return m_TypeID;
+}
+
+String DbType::GetIDColumn() const
+{
+ return m_IDColumn;
+}
+
+void DbType::RegisterType(const DbType::Ptr& type)
+{
+ std::unique_lock<std::mutex> lock(GetStaticMutex());
+ GetTypes()[type->GetName()] = type;
+}
+
+DbType::Ptr DbType::GetByName(const String& name)
+{
+ String typeName;
+
+ if (name == "CheckCommand" || name == "NotificationCommand" || name == "EventCommand")
+ typeName = "Command";
+ else
+ typeName = name;
+
+ std::unique_lock<std::mutex> lock(GetStaticMutex());
+ auto it = GetTypes().find(typeName);
+
+ if (it == GetTypes().end())
+ return nullptr;
+
+ return it->second;
+}
+
+DbType::Ptr DbType::GetByID(long tid)
+{
+ std::unique_lock<std::mutex> lock(GetStaticMutex());
+
+ for (const TypeMap::value_type& kv : GetTypes()) {
+ if (kv.second->GetTypeID() == tid)
+ return kv.second;
+ }
+
+ return nullptr;
+}
+
+DbObject::Ptr DbType::GetOrCreateObjectByName(const String& name1, const String& name2)
+{
+ ObjectLock olock(this);
+
+ auto it = m_Objects.find(std::make_pair(name1, name2));
+
+ if (it != m_Objects.end())
+ return it->second;
+
+ DbObject::Ptr dbobj = m_ObjectFactory(this, name1, name2);
+ m_Objects[std::make_pair(name1, name2)] = dbobj;
+
+ String objName = name1;
+
+ if (!name2.IsEmpty())
+ objName += "!" + name2;
+
+ String objType = m_Name;
+
+ if (m_TypeID == DbObjectTypeCommand) {
+ if (objName.SubStr(0, 6) == "check_") {
+ objType = "CheckCommand";
+ objName = objName.SubStr(6);
+ } else if (objName.SubStr(0, 13) == "notification_") {
+ objType = "NotificationCommand";
+ objName = objName.SubStr(13);
+ } else if (objName.SubStr(0, 6) == "event_") {
+ objType = "EventCommand";
+ objName = objName.SubStr(6);
+ }
+ }
+
+ dbobj->SetObject(ConfigObject::GetObject(objType, objName));
+
+ return dbobj;
+}
+
+std::mutex& DbType::GetStaticMutex()
+{
+ static std::mutex mutex;
+ return mutex;
+}
+
+/**
+ * Caller must hold static mutex.
+ */
+DbType::TypeMap& DbType::GetTypes()
+{
+ static DbType::TypeMap tm;
+ return tm;
+}
+
+std::set<DbType::Ptr> DbType::GetAllTypes()
+{
+ std::set<DbType::Ptr> result;
+
+ {
+ std::unique_lock<std::mutex> lock(GetStaticMutex());
+ for (const auto& kv : GetTypes()) {
+ result.insert(kv.second);
+ }
+ }
+
+ return result;
+}
+
+DbTypeRegistry *DbTypeRegistry::GetInstance()
+{
+ return Singleton<DbTypeRegistry>::GetInstance();
+}
+
diff --git a/lib/db_ido/dbtype.hpp b/lib/db_ido/dbtype.hpp
new file mode 100644
index 0000000..c8ebc45
--- /dev/null
+++ b/lib/db_ido/dbtype.hpp
@@ -0,0 +1,90 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DBTYPE_H
+#define DBTYPE_H
+
+#include "db_ido/i2-db_ido.hpp"
+#include "base/object.hpp"
+#include "base/registry.hpp"
+#include "base/singleton.hpp"
+#include <set>
+
+namespace icinga
+{
+
+class DbObject;
+
+/**
+ * A database object type.
+ *
+ * @ingroup ido
+ */
+class DbType final : public Object
+{
+public:
+ DECLARE_PTR_TYPEDEFS(DbType);
+
+ typedef std::function<intrusive_ptr<DbObject> (const intrusive_ptr<DbType>&, const String&, const String&)> ObjectFactory;
+ typedef std::map<String, DbType::Ptr> TypeMap;
+ typedef std::map<std::pair<String, String>, intrusive_ptr<DbObject> > ObjectMap;
+
+ DbType(String name, String table, long tid, String idcolumn, ObjectFactory factory);
+
+ String GetName() const;
+ String GetTable() const;
+ long GetTypeID() const;
+ String GetIDColumn() const;
+
+ static void RegisterType(const DbType::Ptr& type);
+
+ static DbType::Ptr GetByName(const String& name);
+ static DbType::Ptr GetByID(long tid);
+
+ intrusive_ptr<DbObject> GetOrCreateObjectByName(const String& name1, const String& name2);
+
+ static std::set<DbType::Ptr> GetAllTypes();
+
+private:
+ String m_Name;
+ String m_Table;
+ long m_TypeID;
+ String m_IDColumn;
+ ObjectFactory m_ObjectFactory;
+
+ static std::mutex& GetStaticMutex();
+ static TypeMap& GetTypes();
+
+ ObjectMap m_Objects;
+};
+
+/**
+ * A registry for DbType objects.
+ *
+ * @ingroup ido
+ */
+class DbTypeRegistry : public Registry<DbTypeRegistry, DbType::Ptr>
+{
+public:
+ static DbTypeRegistry *GetInstance();
+};
+
+/**
+ * Factory function for DbObject-based classes.
+ *
+ * @ingroup ido
+ */
+template<typename T>
+intrusive_ptr<T> DbObjectFactory(const DbType::Ptr& type, const String& name1, const String& name2)
+{
+ return new T(type, name1, name2);
+}
+
+#define REGISTER_DBTYPE(name, table, tid, idcolumn, type) \
+ INITIALIZE_ONCE([]() { \
+ DbType::Ptr dbtype = new DbType(#name, table, tid, idcolumn, DbObjectFactory<type>); \
+ DbType::RegisterType(dbtype); \
+ })
+
+}
+
+#endif /* DBTYPE_H */
diff --git a/lib/db_ido/dbvalue.cpp b/lib/db_ido/dbvalue.cpp
new file mode 100644
index 0000000..e1e3e6c
--- /dev/null
+++ b/lib/db_ido/dbvalue.cpp
@@ -0,0 +1,69 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbvalue.hpp"
+
+using namespace icinga;
+
+DbValue::DbValue(DbValueType type, Value value)
+ : m_Type(type), m_Value(std::move(value))
+{ }
+
+Value DbValue::FromTimestamp(const Value& ts)
+{
+ if (ts.IsEmpty() || ts == 0)
+ return Empty;
+
+ return new DbValue(DbValueTimestamp, ts);
+}
+
+Value DbValue::FromValue(const Value& value)
+{
+ return value;
+}
+
+Value DbValue::FromObjectInsertID(const Value& value)
+{
+ return new DbValue(DbValueObjectInsertID, value);
+}
+
+bool DbValue::IsTimestamp(const Value& value)
+{
+ if (!value.IsObjectType<DbValue>())
+ return false;
+
+ DbValue::Ptr dbv = value;
+ return dbv->GetType() == DbValueTimestamp;
+}
+
+bool DbValue::IsObjectInsertID(const Value& value)
+{
+ if (!value.IsObjectType<DbValue>())
+ return false;
+
+ DbValue::Ptr dbv = value;
+ return dbv->GetType() == DbValueObjectInsertID;
+}
+
+Value DbValue::ExtractValue(const Value& value)
+{
+ if (!value.IsObjectType<DbValue>())
+ return value;
+
+ DbValue::Ptr dbv = value;
+ return dbv->GetValue();
+}
+
+DbValueType DbValue::GetType() const
+{
+ return m_Type;
+}
+
+Value DbValue::GetValue() const
+{
+ return m_Value;
+}
+
+void DbValue::SetValue(const Value& value)
+{
+ m_Value = value;
+}
diff --git a/lib/db_ido/dbvalue.hpp b/lib/db_ido/dbvalue.hpp
new file mode 100644
index 0000000..cb59e3a
--- /dev/null
+++ b/lib/db_ido/dbvalue.hpp
@@ -0,0 +1,52 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef DBVALUE_H
+#define DBVALUE_H
+
+#include "db_ido/i2-db_ido.hpp"
+#include "base/object.hpp"
+#include "base/value.hpp"
+
+namespace icinga
+{
+
+enum DbValueType
+{
+ DbValueTimestamp,
+ DbValueObjectInsertID
+};
+
+/**
+ * A database value.
+ *
+ * @ingroup ido
+ */
+struct DbValue final : public Object
+{
+public:
+ DECLARE_PTR_TYPEDEFS(DbValue);
+
+ DbValue(DbValueType type, Value value);
+
+ static Value FromTimestamp(const Value& ts);
+ static Value FromValue(const Value& value);
+ static Value FromObjectInsertID(const Value& value);
+
+ static bool IsTimestamp(const Value& value);
+ static bool IsObjectInsertID(const Value& value);
+
+ static Value ExtractValue(const Value& value);
+
+ DbValueType GetType() const;
+
+ Value GetValue() const;
+ void SetValue(const Value& value);
+
+private:
+ DbValueType m_Type;
+ Value m_Value;
+};
+
+}
+
+#endif /* DBVALUE_H */
diff --git a/lib/db_ido/endpointdbobject.cpp b/lib/db_ido/endpointdbobject.cpp
new file mode 100644
index 0000000..ea16dd7
--- /dev/null
+++ b/lib/db_ido/endpointdbobject.cpp
@@ -0,0 +1,91 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/endpointdbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "base/objectlock.hpp"
+#include "base/initialize.hpp"
+#include "base/configtype.hpp"
+#include "base/utility.hpp"
+#include "base/convert.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(Endpoint, "endpoint", DbObjectTypeEndpoint, "endpoint_object_id", EndpointDbObject);
+
+INITIALIZE_ONCE(&EndpointDbObject::StaticInitialize);
+
+void EndpointDbObject::StaticInitialize()
+{
+ Endpoint::OnConnected.connect([](const Endpoint::Ptr& endpoint, const JsonRpcConnection::Ptr&) { EndpointDbObject::UpdateConnectedStatus(endpoint); });
+ Endpoint::OnDisconnected.connect([](const Endpoint::Ptr& endpoint, const JsonRpcConnection::Ptr&) { EndpointDbObject::UpdateConnectedStatus(endpoint); });
+}
+
+EndpointDbObject::EndpointDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr EndpointDbObject::GetConfigFields() const
+{
+ Endpoint::Ptr endpoint = static_pointer_cast<Endpoint>(GetObject());
+
+ return new Dictionary({
+ { "identity", endpoint->GetName() },
+ { "node", IcingaApplication::GetInstance()->GetNodeName() },
+ { "zone_object_id", endpoint->GetZone() }
+ });
+}
+
+Dictionary::Ptr EndpointDbObject::GetStatusFields() const
+{
+ Endpoint::Ptr endpoint = static_pointer_cast<Endpoint>(GetObject());
+
+
+ Log(LogDebug, "EndpointDbObject")
+ << "update status for endpoint '" << endpoint->GetName() << "'";
+
+ return new Dictionary({
+ { "identity", endpoint->GetName() },
+ { "node", IcingaApplication::GetInstance()->GetNodeName() },
+ { "zone_object_id", endpoint->GetZone() },
+ { "is_connected", EndpointIsConnected(endpoint) }
+ });
+}
+
+void EndpointDbObject::UpdateConnectedStatus(const Endpoint::Ptr& endpoint)
+{
+ bool connected = EndpointIsConnected(endpoint);
+
+ Log(LogDebug, "EndpointDbObject")
+ << "update is_connected=" << connected << " for endpoint '" << endpoint->GetName() << "'";
+
+ DbQuery query1;
+ query1.Table = "endpointstatus";
+ query1.Type = DbQueryUpdate;
+ query1.Category = DbCatState;
+
+ query1.Fields = new Dictionary({
+ { "is_connected", (connected ? 1 : 0) },
+ { "status_update_time", DbValue::FromTimestamp(Utility::GetTime()) }
+ });
+
+ query1.WhereCriteria = new Dictionary({
+ { "endpoint_object_id", endpoint },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+
+ OnQuery(query1);
+}
+
+int EndpointDbObject::EndpointIsConnected(const Endpoint::Ptr& endpoint)
+{
+ unsigned int is_connected = endpoint->GetConnected() ? 1 : 0;
+
+ /* if identity is equal to node, fake is_connected */
+ if (endpoint->GetName() == IcingaApplication::GetInstance()->GetNodeName())
+ is_connected = 1;
+
+ return is_connected;
+}
diff --git a/lib/db_ido/endpointdbobject.hpp b/lib/db_ido/endpointdbobject.hpp
new file mode 100644
index 0000000..e4fba36
--- /dev/null
+++ b/lib/db_ido/endpointdbobject.hpp
@@ -0,0 +1,37 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef ENDPOINTDBOBJECT_H
+#define ENDPOINTDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+#include "remote/endpoint.hpp"
+
+namespace icinga
+{
+
+/**
+ * A Command database object.
+ *
+ * @ingroup ido
+ */
+class EndpointDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(EndpointDbObject);
+
+ EndpointDbObject(const intrusive_ptr<DbType>& type, const String& name1, const String& name2);
+
+ static void StaticInitialize();
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+
+private:
+ static void UpdateConnectedStatus(const Endpoint::Ptr& endpoint);
+ static int EndpointIsConnected(const Endpoint::Ptr& endpoint);
+};
+
+}
+
+#endif /* ENDPOINTDBOBJECT_H */
diff --git a/lib/db_ido/hostdbobject.cpp b/lib/db_ido/hostdbobject.cpp
new file mode 100644
index 0000000..60d1a99
--- /dev/null
+++ b/lib/db_ido/hostdbobject.cpp
@@ -0,0 +1,423 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/hostdbobject.hpp"
+#include "db_ido/hostgroupdbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "db_ido/dbevents.hpp"
+#include "icinga/host.hpp"
+#include "icinga/service.hpp"
+#include "icinga/notification.hpp"
+#include "icinga/dependency.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/eventcommand.hpp"
+#include "icinga/compatutility.hpp"
+#include "icinga/pluginutility.hpp"
+#include "base/convert.hpp"
+#include "base/objectlock.hpp"
+#include "base/logger.hpp"
+#include "base/json.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(Host, "host", DbObjectTypeHost, "host_object_id", HostDbObject);
+
+HostDbObject::HostDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr HostDbObject::GetConfigFields() const
+{
+ Dictionary::Ptr fields = new Dictionary();
+ Host::Ptr host = static_pointer_cast<Host>(GetObject());
+
+ /* Compatibility fallback. */
+ String displayName = host->GetDisplayName();
+
+ unsigned long notificationStateFilter = CompatUtility::GetCheckableNotificationTypeFilter(host);
+ unsigned long notificationTypeFilter = CompatUtility::GetCheckableNotificationTypeFilter(host);
+
+ return new Dictionary({
+ { "alias", !displayName.IsEmpty() ? displayName : host->GetName() },
+ { "display_name", displayName },
+ { "address", host->GetAddress() },
+ { "address6", host->GetAddress6() },
+ { "check_command_object_id", host->GetCheckCommand() },
+ { "eventhandler_command_object_id", host->GetEventCommand() },
+ { "check_timeperiod_object_id", host->GetCheckPeriod() },
+ { "check_interval", host->GetCheckInterval() / 60.0 },
+ { "retry_interval", host->GetRetryInterval() / 60.0 },
+ { "max_check_attempts", host->GetMaxCheckAttempts() },
+ { "flap_detection_enabled", host->GetEnableFlapping() },
+ { "low_flap_threshold", host->GetFlappingThresholdLow() },
+ { "high_flap_threshold", host->GetFlappingThresholdLow() },
+ { "process_performance_data", host->GetEnablePerfdata() },
+ { "freshness_checks_enabled", 1 },
+ { "freshness_threshold", Convert::ToLong(host->GetCheckInterval()) },
+ { "event_handler_enabled", host->GetEnableEventHandler() },
+ { "passive_checks_enabled", host->GetEnablePassiveChecks() },
+ { "active_checks_enabled", host->GetEnableActiveChecks() },
+ { "notifications_enabled", host->GetEnableNotifications() },
+ { "notes", host->GetNotes() },
+ { "notes_url", host->GetNotesUrl() },
+ { "action_url", host->GetActionUrl() },
+ { "icon_image", host->GetIconImage() },
+ { "icon_image_alt", host->GetIconImageAlt() },
+ { "notification_interval", CompatUtility::GetCheckableNotificationNotificationInterval(host) },
+ { "notify_on_down", (notificationStateFilter & (ServiceWarning | ServiceCritical)) ? 1 : 0 },
+ { "notify_on_unreachable", 1 }, /* We don't have this filter and state, and as such we don't filter such notifications. */
+ { "notify_on_recovery", (notificationTypeFilter & NotificationRecovery) ? 1 : 0 },
+ { "notify_on_flapping", (notificationTypeFilter & (NotificationFlappingStart | NotificationFlappingEnd)) ? 1 : 0 },
+ { "notify_on_downtime", (notificationTypeFilter & (NotificationDowntimeStart | NotificationDowntimeEnd | NotificationDowntimeRemoved)) ? 1 : 0 }
+ });
+}
+
+Dictionary::Ptr HostDbObject::GetStatusFields() const
+{
+ Dictionary::Ptr fields = new Dictionary();
+ Host::Ptr host = static_pointer_cast<Host>(GetObject());
+
+ CheckResult::Ptr cr = host->GetLastCheckResult();
+
+ if (cr) {
+ fields->Set("output", CompatUtility::GetCheckResultOutput(cr));
+ fields->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
+ fields->Set("perfdata", PluginUtility::FormatPerfdata(cr->GetPerformanceData()));
+ fields->Set("check_source", cr->GetCheckSource());
+ fields->Set("latency", cr->CalculateLatency());
+ fields->Set("execution_time", cr->CalculateExecutionTime());
+ }
+
+ int currentState = host->GetState();
+
+ if (currentState != HostUp && !host->GetLastReachable())
+ currentState = 2; /* hardcoded compat state */
+
+ fields->Set("current_state", currentState);
+ fields->Set("has_been_checked", host->HasBeenChecked());
+ fields->Set("should_be_scheduled", host->GetEnableActiveChecks());
+ fields->Set("current_check_attempt", host->GetCheckAttempt());
+ fields->Set("max_check_attempts", host->GetMaxCheckAttempts());
+ fields->Set("last_check", DbValue::FromTimestamp(host->GetLastCheck()));
+ fields->Set("next_check", DbValue::FromTimestamp(host->GetNextCheck()));
+ fields->Set("check_type", !host->GetEnableActiveChecks()); /* 0 .. active, 1 .. passive */
+ fields->Set("last_state_change", DbValue::FromTimestamp(host->GetLastStateChange()));
+ fields->Set("last_hard_state_change", DbValue::FromTimestamp(host->GetLastHardStateChange()));
+ fields->Set("last_hard_state", host->GetLastHardState());
+ fields->Set("last_time_up", DbValue::FromTimestamp(host->GetLastStateUp()));
+ fields->Set("last_time_down", DbValue::FromTimestamp(host->GetLastStateDown()));
+ fields->Set("last_time_unreachable", DbValue::FromTimestamp(host->GetLastStateUnreachable()));
+ fields->Set("state_type", host->GetStateType());
+ fields->Set("notifications_enabled", host->GetEnableNotifications());
+ fields->Set("problem_has_been_acknowledged", host->GetAcknowledgement() != AcknowledgementNone);
+ fields->Set("acknowledgement_type", host->GetAcknowledgement());
+ fields->Set("passive_checks_enabled", host->GetEnablePassiveChecks());
+ fields->Set("active_checks_enabled", host->GetEnableActiveChecks());
+ fields->Set("event_handler_enabled", host->GetEnableEventHandler());
+ fields->Set("flap_detection_enabled", host->GetEnableFlapping());
+ fields->Set("is_flapping", host->IsFlapping());
+ fields->Set("percent_state_change", host->GetFlappingCurrent());
+ fields->Set("scheduled_downtime_depth", host->GetDowntimeDepth());
+ fields->Set("process_performance_data", host->GetEnablePerfdata());
+ fields->Set("normal_check_interval", host->GetCheckInterval() / 60.0);
+ fields->Set("retry_check_interval", host->GetRetryInterval() / 60.0);
+ fields->Set("check_timeperiod_object_id", host->GetCheckPeriod());
+ fields->Set("is_reachable", host->GetLastReachable());
+ fields->Set("original_attributes", JsonEncode(host->GetOriginalAttributes()));
+
+ fields->Set("current_notification_number", CompatUtility::GetCheckableNotificationNotificationNumber(host));
+ fields->Set("last_notification", DbValue::FromTimestamp(CompatUtility::GetCheckableNotificationLastNotification(host)));
+ fields->Set("next_notification", DbValue::FromTimestamp(CompatUtility::GetCheckableNotificationNextNotification(host)));
+
+ EventCommand::Ptr eventCommand = host->GetEventCommand();
+
+ if (eventCommand)
+ fields->Set("event_handler", eventCommand->GetName());
+
+ CheckCommand::Ptr checkCommand = host->GetCheckCommand();
+
+ if (checkCommand)
+ fields->Set("check_command", checkCommand->GetName());
+
+ return fields;
+}
+
+void HostDbObject::OnConfigUpdateHeavy()
+{
+ Host::Ptr host = static_pointer_cast<Host>(GetObject());
+
+ /* groups */
+ Array::Ptr groups = host->GetGroups();
+
+ std::vector<DbQuery> queries;
+
+ DbQuery query1;
+ query1.Table = DbType::GetByName("HostGroup")->GetTable() + "_members";
+ query1.Type = DbQueryDelete;
+ query1.Category = DbCatConfig;
+ query1.WhereCriteria = new Dictionary();
+ query1.WhereCriteria->Set("host_object_id", host);
+ queries.emplace_back(std::move(query1));
+
+ if (groups) {
+ ObjectLock olock(groups);
+ for (const String& groupName : groups) {
+ HostGroup::Ptr group = HostGroup::GetByName(groupName);
+
+ DbQuery query2;
+ query2.Table = DbType::GetByName("HostGroup")->GetTable() + "_members";
+ query2.Type = DbQueryInsert;
+ query2.Category = DbCatConfig;
+ query2.Fields = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "hostgroup_id", DbValue::FromObjectInsertID(group) },
+ { "host_object_id", host }
+ });
+ query2.WhereCriteria = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "hostgroup_id", DbValue::FromObjectInsertID(group) },
+ { "host_object_id", host }
+ });
+ queries.emplace_back(std::move(query2));
+ }
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ queries.clear();
+
+ DbQuery query2;
+ query2.Table = GetType()->GetTable() + "_parenthosts";
+ query2.Type = DbQueryDelete;
+ query2.Category = DbCatConfig;
+ query2.WhereCriteria = new Dictionary({
+ { GetType()->GetTable() + "_id", DbValue::FromObjectInsertID(GetObject()) }
+ });
+ queries.emplace_back(std::move(query2));
+
+ /* parents */
+ for (const Checkable::Ptr& checkable : host->GetParents()) {
+ Host::Ptr parent = dynamic_pointer_cast<Host>(checkable);
+
+ if (!parent)
+ continue;
+
+ Log(LogDebug, "HostDbObject")
+ << "host parents: " << parent->GetName();
+
+ /* parents: host_id, parent_host_object_id */
+ DbQuery query1;
+ query1.Table = GetType()->GetTable() + "_parenthosts";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatConfig;
+ query1.Fields = new Dictionary({
+ { GetType()->GetTable() + "_id", DbValue::FromObjectInsertID(GetObject()) },
+ { "parent_host_object_id", parent },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ queries.emplace_back(std::move(query1));
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ /* host dependencies */
+ Log(LogDebug, "HostDbObject")
+ << "host dependencies for '" << host->GetName() << "'";
+
+ queries.clear();
+
+ DbQuery query3;
+ query3.Table = GetType()->GetTable() + "dependencies";
+ query3.Type = DbQueryDelete;
+ query3.Category = DbCatConfig;
+ query3.WhereCriteria = new Dictionary({
+ { "dependent_host_object_id", host }
+ });
+ queries.emplace_back(std::move(query3));
+
+ for (const Dependency::Ptr& dep : host->GetDependencies()) {
+ Checkable::Ptr parent = dep->GetParent();
+
+ if (!parent) {
+ Log(LogDebug, "HostDbObject")
+ << "Missing parent for dependency '" << dep->GetName() << "'.";
+ continue;
+ }
+
+ int stateFilter = dep->GetStateFilter();
+
+ Log(LogDebug, "HostDbObject")
+ << "parent host: " << parent->GetName();
+
+ DbQuery query2;
+ query2.Table = GetType()->GetTable() + "dependencies";
+ query2.Type = DbQueryInsert;
+ query2.Category = DbCatConfig;
+ query2.Fields = new Dictionary({
+ { "host_object_id", parent },
+ { "dependent_host_object_id", host },
+ { "inherits_parent", 1 },
+ { "timeperiod_object_id", dep->GetPeriod() },
+ { "fail_on_up", (stateFilter & StateFilterUp) ? 1 : 0 },
+ { "fail_on_down", (stateFilter & StateFilterDown) ? 1 : 0 },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ queries.emplace_back(std::move(query2));
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ Log(LogDebug, "HostDbObject")
+ << "host contacts: " << host->GetName();
+
+ queries.clear();
+
+ DbQuery query4;
+ query4.Table = GetType()->GetTable() + "_contacts";
+ query4.Type = DbQueryDelete;
+ query4.Category = DbCatConfig;
+ query4.WhereCriteria = new Dictionary({
+ { "host_id", DbValue::FromObjectInsertID(host) }
+ });
+ queries.emplace_back(std::move(query4));
+
+ for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(host)) {
+ Log(LogDebug, "HostDbObject")
+ << "host contacts: " << user->GetName();
+
+ DbQuery query_contact;
+ query_contact.Table = GetType()->GetTable() + "_contacts";
+ query_contact.Type = DbQueryInsert;
+ query_contact.Category = DbCatConfig;
+ query_contact.Fields = new Dictionary({
+ { "host_id", DbValue::FromObjectInsertID(host) },
+ { "contact_object_id", user },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ queries.emplace_back(std::move(query_contact));
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ Log(LogDebug, "HostDbObject")
+ << "host contactgroups: " << host->GetName();
+
+ queries.clear();
+
+ DbQuery query5;
+ query5.Table = GetType()->GetTable() + "_contactgroups";
+ query5.Type = DbQueryDelete;
+ query5.Category = DbCatConfig;
+ query5.WhereCriteria = new Dictionary({
+ { "host_id", DbValue::FromObjectInsertID(host) }
+ });
+ queries.emplace_back(std::move(query5));
+
+ for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(host)) {
+ Log(LogDebug, "HostDbObject")
+ << "host contactgroups: " << usergroup->GetName();
+
+ DbQuery query_contact;
+ query_contact.Table = GetType()->GetTable() + "_contactgroups";
+ query_contact.Type = DbQueryInsert;
+ query_contact.Category = DbCatConfig;
+ query_contact.Fields = new Dictionary({
+ { "host_id", DbValue::FromObjectInsertID(host) },
+ { "contactgroup_object_id", usergroup },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ queries.emplace_back(std::move(query_contact));
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ DoCommonConfigUpdate();
+}
+
+void HostDbObject::OnConfigUpdateLight()
+{
+ DoCommonConfigUpdate();
+}
+
+void HostDbObject::DoCommonConfigUpdate()
+{
+ Host::Ptr host = static_pointer_cast<Host>(GetObject());
+
+ /* update comments and downtimes on config change */
+ DbEvents::AddComments(host);
+ DbEvents::AddDowntimes(host);
+}
+
+String HostDbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) const
+{
+ String hashData = DbObject::CalculateConfigHash(configFields);
+
+ Host::Ptr host = static_pointer_cast<Host>(GetObject());
+
+ Array::Ptr groups = host->GetGroups();
+
+ if (groups) {
+ groups = groups->ShallowClone();
+ ObjectLock oLock (groups);
+ std::sort(groups->Begin(), groups->End());
+ hashData += DbObject::HashValue(groups);
+ }
+
+ ArrayData parents;
+
+ /* parents */
+ for (const Checkable::Ptr& checkable : host->GetParents()) {
+ Host::Ptr parent = dynamic_pointer_cast<Host>(checkable);
+
+ if (!parent)
+ continue;
+
+ parents.push_back(parent->GetName());
+ }
+
+ std::sort(parents.begin(), parents.end());
+
+ hashData += DbObject::HashValue(new Array(std::move(parents)));
+
+ ArrayData dependencies;
+
+ /* dependencies */
+ for (const Dependency::Ptr& dep : host->GetDependencies()) {
+ Checkable::Ptr parent = dep->GetParent();
+
+ if (!parent)
+ continue;
+
+ dependencies.push_back(new Array({
+ parent->GetName(),
+ dep->GetStateFilter(),
+ dep->GetPeriodRaw()
+ }));
+ }
+
+ std::sort(dependencies.begin(), dependencies.end());
+
+ hashData += DbObject::HashValue(new Array(std::move(dependencies)));
+
+ ArrayData users;
+
+ for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(host)) {
+ users.push_back(user->GetName());
+ }
+
+ std::sort(users.begin(), users.end());
+
+ hashData += DbObject::HashValue(new Array(std::move(users)));
+
+ ArrayData userGroups;
+
+ for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(host)) {
+ userGroups.push_back(usergroup->GetName());
+ }
+
+ std::sort(userGroups.begin(), userGroups.end());
+
+ hashData += DbObject::HashValue(new Array(std::move(userGroups)));
+
+ return SHA256(hashData);
+}
diff --git a/lib/db_ido/hostdbobject.hpp b/lib/db_ido/hostdbobject.hpp
new file mode 100644
index 0000000..9fff10a
--- /dev/null
+++ b/lib/db_ido/hostdbobject.hpp
@@ -0,0 +1,38 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef HOSTDBOBJECT_H
+#define HOSTDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+/**
+ * A Host database object.
+ *
+ * @ingroup ido
+ */
+class HostDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(HostDbObject);
+
+ HostDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+
+ void OnConfigUpdateHeavy() override;
+ void OnConfigUpdateLight() override;
+
+ String CalculateConfigHash(const Dictionary::Ptr& configFields) const override;
+
+private:
+ void DoCommonConfigUpdate();
+};
+
+}
+
+#endif /* HOSTDBOBJECT_H */
diff --git a/lib/db_ido/hostgroupdbobject.cpp b/lib/db_ido/hostgroupdbobject.cpp
new file mode 100644
index 0000000..cef6aa2
--- /dev/null
+++ b/lib/db_ido/hostgroupdbobject.cpp
@@ -0,0 +1,33 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/hostgroupdbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "base/objectlock.hpp"
+#include "base/initialize.hpp"
+#include "base/configtype.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(HostGroup, "hostgroup", DbObjectTypeHostGroup, "hostgroup_object_id", HostGroupDbObject);
+
+HostGroupDbObject::HostGroupDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr HostGroupDbObject::GetConfigFields() const
+{
+ HostGroup::Ptr group = static_pointer_cast<HostGroup>(GetObject());
+
+ return new Dictionary({
+ { "alias", group->GetDisplayName() },
+ { "notes", group->GetNotes() },
+ { "notes_url", group->GetNotesUrl() },
+ { "action_url", group->GetActionUrl() }
+ });
+}
+
+Dictionary::Ptr HostGroupDbObject::GetStatusFields() const
+{
+ return nullptr;
+}
diff --git a/lib/db_ido/hostgroupdbobject.hpp b/lib/db_ido/hostgroupdbobject.hpp
new file mode 100644
index 0000000..9c48f29
--- /dev/null
+++ b/lib/db_ido/hostgroupdbobject.hpp
@@ -0,0 +1,34 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef HOSTGROUPDBOBJECT_H
+#define HOSTGROUPDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "icinga/hostgroup.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+/**
+ * A HostGroup database object.
+ *
+ * @ingroup ido
+ */
+class HostGroupDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(HostGroupDbObject);
+
+ HostGroupDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+
+private:
+ static void MembersChangedHandler(const HostGroup::Ptr& hgfilter);
+};
+
+}
+
+#endif /* HOSTGROUPDBOBJECT_H */
diff --git a/lib/db_ido/i2-db_ido.hpp b/lib/db_ido/i2-db_ido.hpp
new file mode 100644
index 0000000..1da9fdc
--- /dev/null
+++ b/lib/db_ido/i2-db_ido.hpp
@@ -0,0 +1,14 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef I2DB_IDO_H
+#define I2DB_IDO_H
+
+/**
+ * @defgroup db_ido IDO library
+ *
+ * The Icinga library implements database-agnostic IDO functionality.
+ */
+
+#include "base/i2-base.hpp"
+
+#endif /* I2DB_IDO_H */
diff --git a/lib/db_ido/idochecktask.cpp b/lib/db_ido/idochecktask.cpp
new file mode 100644
index 0000000..6a7f0d3
--- /dev/null
+++ b/lib/db_ido/idochecktask.cpp
@@ -0,0 +1,198 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/idochecktask.hpp"
+#include "icinga/host.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/macroprocessor.hpp"
+#include "remote/apilistener.hpp"
+#include "remote/endpoint.hpp"
+#include "remote/zone.hpp"
+#include "base/function.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/configtype.hpp"
+#include "base/convert.hpp"
+#include <utility>
+
+using namespace icinga;
+
+REGISTER_FUNCTION_NONCONST(Internal, IdoCheck, &IdoCheckTask::ScriptFunc, "checkable:cr:resolvedMacros:useResolvedMacros");
+
+static void ReportIdoCheck(
+ const Checkable::Ptr& checkable, const CheckCommand::Ptr& commandObj,
+ const CheckResult::Ptr& cr, String output, ServiceState state = ServiceUnknown
+)
+{
+ if (Checkable::ExecuteCommandProcessFinishedHandler) {
+ double now = Utility::GetTime();
+ ProcessResult pr;
+ pr.PID = -1;
+ pr.Output = std::move(output);
+ pr.ExecutionStart = now;
+ pr.ExecutionEnd = now;
+ pr.ExitStatus = state;
+
+ Checkable::ExecuteCommandProcessFinishedHandler(commandObj->GetName(), pr);
+ } else {
+ cr->SetState(state);
+ cr->SetOutput(output);
+ checkable->ProcessCheckResult(cr);
+ }
+}
+
+void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
+{
+ ServiceState state;
+ CheckCommand::Ptr commandObj = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+ Value raw_command = commandObj->GetCommandLine();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", commandObj);
+ resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
+
+ String idoType = MacroProcessor::ResolveMacros("$ido_type$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ String idoName = MacroProcessor::ResolveMacros("$ido_name$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ String missingQueriesWarning;
+ String missingQueriesCritical;
+ String missingPendingQueriesWarning;
+ String missingPendingQueriesCritical;
+
+ double queriesWarning = MacroProcessor::ResolveMacros("$ido_queries_warning$", resolvers, checkable->GetLastCheckResult(),
+ &missingQueriesWarning, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ double queriesCritical = MacroProcessor::ResolveMacros("$ido_queries_critical$", resolvers, checkable->GetLastCheckResult(),
+ &missingQueriesCritical, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ double pendingQueriesWarning = MacroProcessor::ResolveMacros("$ido_pending_queries_warning$", resolvers, checkable->GetLastCheckResult(),
+ &missingPendingQueriesWarning, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ double pendingQueriesCritical = MacroProcessor::ResolveMacros("$ido_pending_queries_critical$", resolvers, checkable->GetLastCheckResult(),
+ &missingPendingQueriesCritical, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+
+ if (resolvedMacros && !useResolvedMacros)
+ return;
+
+ if (idoType.IsEmpty()) {
+ ReportIdoCheck(checkable, commandObj, cr, "Attribute 'ido_type' must be set.");
+ return;
+ }
+
+ if (idoName.IsEmpty()) {
+ ReportIdoCheck(checkable, commandObj, cr, "Attribute 'ido_name' must be set.");
+ return;
+ }
+
+ Type::Ptr type = Type::GetByName(idoType);
+
+ if (!type || !DbConnection::TypeInstance->IsAssignableFrom(type)) {
+ ReportIdoCheck(checkable, commandObj, cr, "DB IDO type '" + idoType + "' is invalid.");
+ return;
+ }
+
+ auto *dtype = dynamic_cast<ConfigType *>(type.get());
+ VERIFY(dtype);
+
+ DbConnection::Ptr conn = static_pointer_cast<DbConnection>(dtype->GetObject(idoName));
+
+ if (!conn) {
+ ReportIdoCheck(checkable, commandObj, cr, "DB IDO connection '" + idoName + "' does not exist.");
+ return;
+ }
+
+ double qps = conn->GetQueryCount(60) / 60.0;
+
+ if (conn->IsPaused()) {
+ ReportIdoCheck(checkable, commandObj, cr, "DB IDO connection is temporarily disabled on this cluster instance.", ServiceOK);
+ return;
+ }
+
+ double pendingQueries = conn->GetPendingQueryCount();
+
+ if (!conn->GetConnected()) {
+ if (conn->GetShouldConnect()) {
+ ReportIdoCheck(checkable, commandObj, cr, "Could not connect to the database server.", ServiceCritical);
+ } else {
+ ReportIdoCheck(
+ checkable, commandObj, cr,
+ "Not currently enabled: Another cluster instance is responsible for the IDO database.", ServiceOK
+ );
+ }
+ return;
+ }
+
+ /* Schema versions. */
+ String schema_version = conn->GetSchemaVersion();
+ std::ostringstream msgbuf;
+
+ if (Utility::CompareVersion(conn->GetLatestSchemaVersion(), schema_version) < 0) {
+ msgbuf << "Outdated schema version: '" << schema_version << "'. Latest version: '"
+ << conn->GetLatestSchemaVersion() << "'."
+ << " Queries per second: " << std::fixed << std::setprecision(3) << qps
+ << " Pending queries: " << std::fixed << std::setprecision(3) << pendingQueries << ".";
+
+ state = ServiceWarning;
+ } else {
+ msgbuf << "Connected to the database server (Schema version: '" << schema_version << "')."
+ << " Queries per second: " << std::fixed << std::setprecision(3) << qps
+ << " Pending queries: " << std::fixed << std::setprecision(3) << pendingQueries << ".";
+
+ state = ServiceOK;
+ }
+
+ if (conn->GetEnableHa()) {
+ double failoverTs = conn->GetLastFailover();
+
+ msgbuf << " Last failover: " << Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", failoverTs) << ".";
+ }
+
+ /* Check whether the thresholds have been defined and match. */
+ if (missingQueriesCritical.IsEmpty() && qps < queriesCritical) {
+ msgbuf << " " << qps << " queries/s lower than critical threshold (" << queriesCritical << " queries/s).";
+
+ state = ServiceCritical;
+ } else if (missingQueriesWarning.IsEmpty() && qps < queriesWarning) {
+ msgbuf << " " << qps << " queries/s lower than warning threshold (" << queriesWarning << " queries/s).";
+
+ state = ServiceWarning;
+ }
+
+ if (missingPendingQueriesCritical.IsEmpty() && pendingQueries > pendingQueriesCritical) {
+ msgbuf << " " << pendingQueries << " pending queries greater than critical threshold ("
+ << pendingQueriesCritical << " queries).";
+
+ state = ServiceCritical;
+ } else if (missingPendingQueriesWarning.IsEmpty() && pendingQueries > pendingQueriesWarning) {
+ msgbuf << " " << pendingQueries << " pending queries greater than warning threshold ("
+ << pendingQueriesWarning << " queries).";
+
+ if (state == ServiceOK) {
+ state = ServiceWarning;
+ }
+ }
+
+ cr->SetPerformanceData(new Array({
+ { new PerfdataValue("queries", qps, false, "", queriesWarning, queriesCritical) },
+ { new PerfdataValue("queries_1min", conn->GetQueryCount(60)) },
+ { new PerfdataValue("queries_5mins", conn->GetQueryCount(5 * 60)) },
+ { new PerfdataValue("queries_15mins", conn->GetQueryCount(15 * 60)) },
+ { new PerfdataValue("pending_queries", pendingQueries, false, "", pendingQueriesWarning, pendingQueriesCritical) }
+ }));
+
+ ReportIdoCheck(checkable, commandObj, cr, msgbuf.str(), state);
+}
diff --git a/lib/db_ido/idochecktask.hpp b/lib/db_ido/idochecktask.hpp
new file mode 100644
index 0000000..5868c38
--- /dev/null
+++ b/lib/db_ido/idochecktask.hpp
@@ -0,0 +1,29 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef IDOCHECKTASK_H
+#define IDOCHECKTASK_H
+
+#include "db_ido/dbconnection.hpp"
+#include "icinga/checkable.hpp"
+
+namespace icinga
+{
+
+/**
+ * IDO check type.
+ *
+ * @ingroup db_ido
+ */
+class IdoCheckTask
+{
+public:
+ static void ScriptFunc(const Checkable::Ptr& service, const CheckResult::Ptr& cr,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros);
+
+private:
+ IdoCheckTask();
+};
+
+}
+
+#endif /* IDOCHECKTASK_H */
diff --git a/lib/db_ido/servicedbobject.cpp b/lib/db_ido/servicedbobject.cpp
new file mode 100644
index 0000000..7f711df
--- /dev/null
+++ b/lib/db_ido/servicedbobject.cpp
@@ -0,0 +1,359 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/servicedbobject.hpp"
+#include "db_ido/servicegroupdbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "db_ido/dbevents.hpp"
+#include "icinga/notification.hpp"
+#include "icinga/dependency.hpp"
+#include "icinga/checkcommand.hpp"
+#include "icinga/eventcommand.hpp"
+#include "icinga/externalcommandprocessor.hpp"
+#include "icinga/compatutility.hpp"
+#include "icinga/pluginutility.hpp"
+#include "icinga/icingaapplication.hpp"
+#include "remote/endpoint.hpp"
+#include "base/convert.hpp"
+#include "base/objectlock.hpp"
+#include "base/initialize.hpp"
+#include "base/configtype.hpp"
+#include "base/utility.hpp"
+#include "base/logger.hpp"
+#include "base/json.hpp"
+#include <boost/algorithm/string/join.hpp>
+
+using namespace icinga;
+
+REGISTER_DBTYPE(Service, "service", DbObjectTypeService, "service_object_id", ServiceDbObject);
+
+ServiceDbObject::ServiceDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr ServiceDbObject::GetConfigFields() const
+{
+ Service::Ptr service = static_pointer_cast<Service>(GetObject());
+ Host::Ptr host = service->GetHost();
+
+ unsigned long notificationStateFilter = CompatUtility::GetCheckableNotificationTypeFilter(service);
+ unsigned long notificationTypeFilter = CompatUtility::GetCheckableNotificationTypeFilter(service);
+
+ return new Dictionary({
+ { "host_object_id", host },
+ { "display_name", service->GetDisplayName() },
+ { "check_command_object_id", service->GetCheckCommand() },
+ { "eventhandler_command_object_id", service->GetEventCommand() },
+ { "check_timeperiod_object_id", service->GetCheckPeriod() },
+ { "check_interval", service->GetCheckInterval() / 60.0 },
+ { "retry_interval", service->GetRetryInterval() / 60.0 },
+ { "max_check_attempts", service->GetMaxCheckAttempts() },
+ { "is_volatile", service->GetVolatile() },
+ { "flap_detection_enabled", service->GetEnableFlapping() },
+ { "low_flap_threshold", service->GetFlappingThresholdLow() },
+ { "high_flap_threshold", service->GetFlappingThresholdLow() },
+ { "process_performance_data", service->GetEnablePerfdata() },
+ { "freshness_checks_enabled", 1 },
+ { "freshness_threshold", Convert::ToLong(service->GetCheckInterval()) },
+ { "event_handler_enabled", service->GetEnableEventHandler() },
+ { "passive_checks_enabled", service->GetEnablePassiveChecks() },
+ { "active_checks_enabled", service->GetEnableActiveChecks() },
+ { "notifications_enabled", service->GetEnableNotifications() },
+ { "notes", service->GetNotes() },
+ { "notes_url", service->GetNotesUrl() },
+ { "action_url", service->GetActionUrl() },
+ { "icon_image", service->GetIconImage() },
+ { "icon_image_alt", service->GetIconImageAlt() },
+ { "notification_interval", CompatUtility::GetCheckableNotificationNotificationInterval(service) },
+ { "notify_on_warning", (notificationStateFilter & ServiceWarning) ? 1 : 0 },
+ { "notify_on_unknown", (notificationStateFilter & ServiceUnknown) ? 1 : 0 },
+ { "notify_on_critical", (notificationStateFilter & ServiceCritical) ? 1 : 0 },
+ { "notify_on_recovery", (notificationTypeFilter & NotificationRecovery) ? 1 : 0 },
+ { "notify_on_flapping", (notificationTypeFilter & (NotificationFlappingStart | NotificationFlappingEnd)) ? 1 : 0 },
+ { "notify_on_downtime", (notificationTypeFilter & (NotificationDowntimeStart | NotificationDowntimeEnd | NotificationDowntimeRemoved)) ? 1 : 0 }
+ });
+}
+
+Dictionary::Ptr ServiceDbObject::GetStatusFields() const
+{
+ Dictionary::Ptr fields = new Dictionary();
+ Service::Ptr service = static_pointer_cast<Service>(GetObject());
+ CheckResult::Ptr cr = service->GetLastCheckResult();
+
+ if (cr) {
+ fields->Set("output", CompatUtility::GetCheckResultOutput(cr));
+ fields->Set("long_output", CompatUtility::GetCheckResultLongOutput(cr));
+ fields->Set("perfdata", PluginUtility::FormatPerfdata(cr->GetPerformanceData()));
+ fields->Set("check_source", cr->GetCheckSource());
+ fields->Set("latency", cr->CalculateLatency());
+ fields->Set("execution_time", cr->CalculateExecutionTime());
+ }
+
+ fields->Set("current_state", service->GetState());
+ fields->Set("has_been_checked", service->HasBeenChecked());
+ fields->Set("should_be_scheduled", service->GetEnableActiveChecks());
+ fields->Set("current_check_attempt", service->GetCheckAttempt());
+ fields->Set("max_check_attempts", service->GetMaxCheckAttempts());
+ fields->Set("last_check", DbValue::FromTimestamp(service->GetLastCheck()));
+ fields->Set("next_check", DbValue::FromTimestamp(service->GetNextCheck()));
+ fields->Set("check_type", !service->GetEnableActiveChecks()); /* 0 .. active, 1 .. passive */
+ fields->Set("last_state_change", DbValue::FromTimestamp(service->GetLastStateChange()));
+ fields->Set("last_hard_state_change", DbValue::FromTimestamp(service->GetLastHardStateChange()));
+ fields->Set("last_hard_state", service->GetLastHardState());
+ fields->Set("last_time_ok", DbValue::FromTimestamp(service->GetLastStateOK()));
+ fields->Set("last_time_warning", DbValue::FromTimestamp(service->GetLastStateWarning()));
+ fields->Set("last_time_critical", DbValue::FromTimestamp(service->GetLastStateCritical()));
+ fields->Set("last_time_unknown", DbValue::FromTimestamp(service->GetLastStateUnknown()));
+ fields->Set("state_type", service->GetStateType());
+ fields->Set("notifications_enabled", service->GetEnableNotifications());
+ fields->Set("problem_has_been_acknowledged", service->GetAcknowledgement() != AcknowledgementNone);
+ fields->Set("acknowledgement_type", service->GetAcknowledgement());
+ fields->Set("passive_checks_enabled", service->GetEnablePassiveChecks());
+ fields->Set("active_checks_enabled", service->GetEnableActiveChecks());
+ fields->Set("event_handler_enabled", service->GetEnableEventHandler());
+ fields->Set("flap_detection_enabled", service->GetEnableFlapping());
+ fields->Set("is_flapping", service->IsFlapping());
+ fields->Set("percent_state_change", service->GetFlappingCurrent());
+ fields->Set("scheduled_downtime_depth", service->GetDowntimeDepth());
+ fields->Set("process_performance_data", service->GetEnablePerfdata());
+ fields->Set("normal_check_interval", service->GetCheckInterval() / 60.0);
+ fields->Set("retry_check_interval", service->GetRetryInterval() / 60.0);
+ fields->Set("check_timeperiod_object_id", service->GetCheckPeriod());
+ fields->Set("is_reachable", service->GetLastReachable());
+ fields->Set("original_attributes", JsonEncode(service->GetOriginalAttributes()));
+
+ fields->Set("current_notification_number", CompatUtility::GetCheckableNotificationNotificationNumber(service));
+ fields->Set("last_notification", DbValue::FromTimestamp(CompatUtility::GetCheckableNotificationLastNotification(service)));
+ fields->Set("next_notification", DbValue::FromTimestamp(CompatUtility::GetCheckableNotificationNextNotification(service)));
+
+ EventCommand::Ptr eventCommand = service->GetEventCommand();
+
+ if (eventCommand)
+ fields->Set("event_handler", eventCommand->GetName());
+
+ CheckCommand::Ptr checkCommand = service->GetCheckCommand();
+
+ if (checkCommand)
+ fields->Set("check_command", checkCommand->GetName());
+
+ return fields;
+}
+
+void ServiceDbObject::OnConfigUpdateHeavy()
+{
+ Service::Ptr service = static_pointer_cast<Service>(GetObject());
+
+ /* groups */
+ Array::Ptr groups = service->GetGroups();
+
+ std::vector<DbQuery> queries;
+
+ DbQuery query1;
+ query1.Table = DbType::GetByName("ServiceGroup")->GetTable() + "_members";
+ query1.Type = DbQueryDelete;
+ query1.Category = DbCatConfig;
+ query1.WhereCriteria = new Dictionary({
+ { "service_object_id", service }
+ });
+ queries.emplace_back(std::move(query1));
+
+ if (groups) {
+ ObjectLock olock(groups);
+ for (const String& groupName : groups) {
+ ServiceGroup::Ptr group = ServiceGroup::GetByName(groupName);
+
+ DbQuery query2;
+ query2.Table = DbType::GetByName("ServiceGroup")->GetTable() + "_members";
+ query2.Type = DbQueryInsert;
+ query2.Category = DbCatConfig;
+ query2.Fields = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "servicegroup_id", DbValue::FromObjectInsertID(group) },
+ { "service_object_id", service }
+ });
+ query2.WhereCriteria = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "servicegroup_id", DbValue::FromObjectInsertID(group) },
+ { "service_object_id", service }
+ });
+ queries.emplace_back(std::move(query2));
+ }
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ /* service dependencies */
+ queries.clear();
+
+ DbQuery query2;
+ query2.Table = GetType()->GetTable() + "dependencies";
+ query2.Type = DbQueryDelete;
+ query2.Category = DbCatConfig;
+ query2.WhereCriteria = new Dictionary({
+ { "dependent_service_object_id", service }
+ });
+ queries.emplace_back(std::move(query2));
+
+ for (const Dependency::Ptr& dep : service->GetDependencies()) {
+ Checkable::Ptr parent = dep->GetParent();
+
+ if (!parent) {
+ Log(LogDebug, "ServiceDbObject")
+ << "Missing parent for dependency '" << dep->GetName() << "'.";
+ continue;
+ }
+
+ Log(LogDebug, "ServiceDbObject")
+ << "service parents: " << parent->GetName();
+
+ int stateFilter = dep->GetStateFilter();
+
+ /* service dependencies */
+ DbQuery query1;
+ query1.Table = GetType()->GetTable() + "dependencies";
+ query1.Type = DbQueryInsert;
+ query1.Category = DbCatConfig;
+ query1.Fields = new Dictionary({
+ { "service_object_id", parent },
+ { "dependent_service_object_id", service },
+ { "inherits_parent", 1 },
+ { "timeperiod_object_id", dep->GetPeriod() },
+ { "fail_on_ok", (stateFilter & StateFilterOK) ? 1 : 0 },
+ { "fail_on_warning", (stateFilter & StateFilterWarning) ? 1 : 0 },
+ { "fail_on_critical", (stateFilter & StateFilterCritical) ? 1 : 0 },
+ { "fail_on_unknown", (stateFilter & StateFilterUnknown) ? 1 : 0 },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ queries.emplace_back(std::move(query1));
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ /* service contacts, contactgroups */
+ queries.clear();
+
+ DbQuery query3;
+ query3.Table = GetType()->GetTable() + "_contacts";
+ query3.Type = DbQueryDelete;
+ query3.Category = DbCatConfig;
+ query3.WhereCriteria = new Dictionary({
+ { "service_id", DbValue::FromObjectInsertID(service) }
+ });
+ queries.emplace_back(std::move(query3));
+
+ for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(service)) {
+ DbQuery query_contact;
+ query_contact.Table = GetType()->GetTable() + "_contacts";
+ query_contact.Type = DbQueryInsert;
+ query_contact.Category = DbCatConfig;
+ query_contact.Fields = new Dictionary({
+ { "service_id", DbValue::FromObjectInsertID(service) },
+ { "contact_object_id", user },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+
+ });
+ queries.emplace_back(std::move(query_contact));
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ queries.clear();
+
+ DbQuery query4;
+ query4.Table = GetType()->GetTable() + "_contactgroups";
+ query4.Type = DbQueryDelete;
+ query4.Category = DbCatConfig;
+ query4.WhereCriteria = new Dictionary({
+ { "service_id", DbValue::FromObjectInsertID(service) }
+ });
+ queries.emplace_back(std::move(query4));
+
+ for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(service)) {
+ DbQuery query_contact;
+ query_contact.Table = GetType()->GetTable() + "_contactgroups";
+ query_contact.Type = DbQueryInsert;
+ query_contact.Category = DbCatConfig;
+ query_contact.Fields = new Dictionary({
+ { "service_id", DbValue::FromObjectInsertID(service) },
+ { "contactgroup_object_id", usergroup },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+ });
+ queries.emplace_back(std::move(query_contact));
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ DoCommonConfigUpdate();
+}
+
+void ServiceDbObject::OnConfigUpdateLight()
+{
+ DoCommonConfigUpdate();
+}
+
+void ServiceDbObject::DoCommonConfigUpdate()
+{
+ Service::Ptr service = static_pointer_cast<Service>(GetObject());
+
+ /* update comments and downtimes on config change */
+ DbEvents::AddComments(service);
+ DbEvents::AddDowntimes(service);
+}
+
+String ServiceDbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) const
+{
+ String hashData = DbObject::CalculateConfigHash(configFields);
+
+ Service::Ptr service = static_pointer_cast<Service>(GetObject());
+
+ Array::Ptr groups = service->GetGroups();
+
+ if (groups) {
+ groups = groups->ShallowClone();
+ ObjectLock oLock (groups);
+ std::sort(groups->Begin(), groups->End());
+ hashData += DbObject::HashValue(groups);
+ }
+
+ ArrayData dependencies;
+
+ /* dependencies */
+ for (const Dependency::Ptr& dep : service->GetDependencies()) {
+ Checkable::Ptr parent = dep->GetParent();
+
+ if (!parent)
+ continue;
+
+ dependencies.push_back(new Array({
+ parent->GetName(),
+ dep->GetStateFilter(),
+ dep->GetPeriodRaw()
+ }));
+ }
+
+ std::sort(dependencies.begin(), dependencies.end());
+
+ hashData += DbObject::HashValue(new Array(std::move(dependencies)));
+
+ ArrayData users;
+
+ for (const User::Ptr& user : CompatUtility::GetCheckableNotificationUsers(service)) {
+ users.push_back(user->GetName());
+ }
+
+ std::sort(users.begin(), users.end());
+
+ hashData += DbObject::HashValue(new Array(std::move(users)));
+
+ ArrayData userGroups;
+
+ for (const UserGroup::Ptr& usergroup : CompatUtility::GetCheckableNotificationUserGroups(service)) {
+ userGroups.push_back(usergroup->GetName());
+ }
+
+ std::sort(userGroups.begin(), userGroups.end());
+
+ hashData += DbObject::HashValue(new Array(std::move(userGroups)));
+
+ return SHA256(hashData);
+}
diff --git a/lib/db_ido/servicedbobject.hpp b/lib/db_ido/servicedbobject.hpp
new file mode 100644
index 0000000..19824be
--- /dev/null
+++ b/lib/db_ido/servicedbobject.hpp
@@ -0,0 +1,41 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef SERVICEDBOBJECT_H
+#define SERVICEDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+#include "icinga/service.hpp"
+
+namespace icinga
+{
+
+/**
+ * A Service database object.
+ *
+ * @ingroup ido
+ */
+class ServiceDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(ServiceDbObject);
+
+ ServiceDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+ static void StaticInitialize();
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+
+ void OnConfigUpdateHeavy() override;
+ void OnConfigUpdateLight() override;
+
+ String CalculateConfigHash(const Dictionary::Ptr& configFields) const override;
+
+private:
+ void DoCommonConfigUpdate();
+};
+
+}
+
+#endif /* SERVICEDBOBJECT_H */
diff --git a/lib/db_ido/servicegroupdbobject.cpp b/lib/db_ido/servicegroupdbobject.cpp
new file mode 100644
index 0000000..ea4d40c
--- /dev/null
+++ b/lib/db_ido/servicegroupdbobject.cpp
@@ -0,0 +1,32 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/servicegroupdbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "base/objectlock.hpp"
+#include "base/initialize.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(ServiceGroup, "servicegroup", DbObjectTypeServiceGroup, "servicegroup_object_id", ServiceGroupDbObject);
+
+ServiceGroupDbObject::ServiceGroupDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr ServiceGroupDbObject::GetConfigFields() const
+{
+ ServiceGroup::Ptr group = static_pointer_cast<ServiceGroup>(GetObject());
+
+ return new Dictionary({
+ { "alias", group->GetDisplayName() },
+ { "notes", group->GetNotes() },
+ { "notes_url", group->GetNotesUrl() },
+ { "action_url", group->GetActionUrl() }
+ });
+}
+
+Dictionary::Ptr ServiceGroupDbObject::GetStatusFields() const
+{
+ return nullptr;
+}
diff --git a/lib/db_ido/servicegroupdbobject.hpp b/lib/db_ido/servicegroupdbobject.hpp
new file mode 100644
index 0000000..7f0d6c1
--- /dev/null
+++ b/lib/db_ido/servicegroupdbobject.hpp
@@ -0,0 +1,31 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef SERVICEGROUPDBOBJECT_H
+#define SERVICEGROUPDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "icinga/servicegroup.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+/**
+ * A ServiceGroup database object.
+ *
+ * @ingroup ido
+ */
+class ServiceGroupDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(ServiceGroupDbObject);
+
+ ServiceGroupDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+};
+
+}
+
+#endif /* SERVICEGROUPDBOBJECT_H */
diff --git a/lib/db_ido/timeperioddbobject.cpp b/lib/db_ido/timeperioddbobject.cpp
new file mode 100644
index 0000000..98997f5
--- /dev/null
+++ b/lib/db_ido/timeperioddbobject.cpp
@@ -0,0 +1,85 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/timeperioddbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "icinga/timeperiod.hpp"
+#include "icinga/legacytimeperiod.hpp"
+#include "base/utility.hpp"
+#include "base/exception.hpp"
+#include "base/objectlock.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(TimePeriod, "timeperiod", DbObjectTypeTimePeriod, "timeperiod_object_id", TimePeriodDbObject);
+
+TimePeriodDbObject::TimePeriodDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr TimePeriodDbObject::GetConfigFields() const
+{
+ TimePeriod::Ptr tp = static_pointer_cast<TimePeriod>(GetObject());
+
+ return new Dictionary({
+ { "alias", tp->GetDisplayName() }
+ });
+}
+
+Dictionary::Ptr TimePeriodDbObject::GetStatusFields() const
+{
+ return Empty;
+}
+
+void TimePeriodDbObject::OnConfigUpdateHeavy()
+{
+ TimePeriod::Ptr tp = static_pointer_cast<TimePeriod>(GetObject());
+
+ DbQuery query_del1;
+ query_del1.Table = GetType()->GetTable() + "_timeranges";
+ query_del1.Type = DbQueryDelete;
+ query_del1.Category = DbCatConfig;
+ query_del1.WhereCriteria = new Dictionary({
+ { "timeperiod_id", DbValue::FromObjectInsertID(tp) }
+ });
+ OnQuery(query_del1);
+
+ Dictionary::Ptr ranges = tp->GetRanges();
+
+ if (!ranges)
+ return;
+
+ time_t refts = Utility::GetTime();
+ ObjectLock olock(ranges);
+ for (const Dictionary::Pair& kv : ranges) {
+ int wday = LegacyTimePeriod::WeekdayFromString(kv.first);
+
+ if (wday == -1)
+ continue;
+
+ tm reference = Utility::LocalTime(refts);
+
+ Array::Ptr segments = new Array();
+ LegacyTimePeriod::ProcessTimeRanges(kv.second, &reference, segments);
+
+ ObjectLock olock(segments);
+ for (const Value& vsegment : segments) {
+ Dictionary::Ptr segment = vsegment;
+ int begin = segment->Get("begin");
+ int end = segment->Get("end");
+
+ DbQuery query;
+ query.Table = GetType()->GetTable() + "_timeranges";
+ query.Type = DbQueryInsert;
+ query.Category = DbCatConfig;
+ query.Fields = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "timeperiod_id", DbValue::FromObjectInsertID(tp) },
+ { "day", wday },
+ { "start_sec", begin % 86400 },
+ { "end_sec", end % 86400 }
+ });
+ OnQuery(query);
+ }
+ }
+}
diff --git a/lib/db_ido/timeperioddbobject.hpp b/lib/db_ido/timeperioddbobject.hpp
new file mode 100644
index 0000000..e3cc13c
--- /dev/null
+++ b/lib/db_ido/timeperioddbobject.hpp
@@ -0,0 +1,33 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef TIMEPERIODDBOBJECT_H
+#define TIMEPERIODDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+/**
+ * A TimePeriod database object.
+ *
+ * @ingroup ido
+ */
+class TimePeriodDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(TimePeriodDbObject);
+
+ TimePeriodDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+protected:
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+
+ void OnConfigUpdateHeavy() override;
+};
+
+}
+
+#endif /* TIMEPERIODDBOBJECT_H */
diff --git a/lib/db_ido/userdbobject.cpp b/lib/db_ido/userdbobject.cpp
new file mode 100644
index 0000000..439b8fb
--- /dev/null
+++ b/lib/db_ido/userdbobject.cpp
@@ -0,0 +1,161 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/userdbobject.hpp"
+#include "db_ido/usergroupdbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "icinga/user.hpp"
+#include "icinga/notification.hpp"
+#include "base/convert.hpp"
+#include "base/objectlock.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(User, "contact", DbObjectTypeContact, "contact_object_id", UserDbObject);
+
+UserDbObject::UserDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr UserDbObject::GetConfigFields() const
+{
+ User::Ptr user = static_pointer_cast<User>(GetObject());
+
+ int typeFilter = user->GetTypeFilter();
+ int stateFilter = user->GetStateFilter();
+
+ return new Dictionary({
+ { "alias", user->GetDisplayName() },
+ { "email_address", user->GetEmail() },
+ { "pager_address", user->GetPager() },
+ { "host_timeperiod_object_id", user->GetPeriod() },
+ { "service_timeperiod_object_id", user->GetPeriod() },
+ { "host_notifications_enabled", user->GetEnableNotifications() },
+ { "service_notifications_enabled", user->GetEnableNotifications() },
+ { "can_submit_commands", 1 },
+ { "notify_service_recovery", (typeFilter & NotificationRecovery) ? 1 : 0 },
+ { "notify_service_warning", (stateFilter & StateFilterWarning) ? 1 : 0 },
+ { "notify_service_unknown", (stateFilter & StateFilterUnknown) ? 1 : 0 },
+ { "notify_service_critical", (stateFilter & StateFilterCritical) ? 1 : 0 },
+ { "notify_service_flapping", (typeFilter & (NotificationFlappingStart | NotificationFlappingEnd)) ? 1 : 0 },
+ { "notify_service_downtime", (typeFilter & (NotificationDowntimeStart | NotificationDowntimeEnd | NotificationDowntimeRemoved)) ? 1 : 0 },
+ { "notify_host_recovery", (typeFilter & NotificationRecovery) ? 1 : 0 },
+ { "notify_host_down", (stateFilter & StateFilterDown) ? 1 : 0 },
+ { "notify_host_flapping", (typeFilter & (NotificationFlappingStart | NotificationFlappingEnd)) ? 1 : 0 },
+ { "notify_host_downtime", (typeFilter & (NotificationDowntimeStart | NotificationDowntimeEnd | NotificationDowntimeRemoved)) ? 1 : 0 }
+ });
+}
+
+Dictionary::Ptr UserDbObject::GetStatusFields() const
+{
+ User::Ptr user = static_pointer_cast<User>(GetObject());
+
+ return new Dictionary({
+ { "host_notifications_enabled", user->GetEnableNotifications() },
+ { "service_notifications_enabled", user->GetEnableNotifications() },
+ { "last_host_notification", DbValue::FromTimestamp(user->GetLastNotification()) },
+ { "last_service_notification", DbValue::FromTimestamp(user->GetLastNotification()) }
+ });
+}
+
+void UserDbObject::OnConfigUpdateHeavy()
+{
+ User::Ptr user = static_pointer_cast<User>(GetObject());
+
+ /* groups */
+ Array::Ptr groups = user->GetGroups();
+
+ std::vector<DbQuery> queries;
+
+ DbQuery query1;
+ query1.Table = DbType::GetByName("UserGroup")->GetTable() + "_members";
+ query1.Type = DbQueryDelete;
+ query1.Category = DbCatConfig;
+ query1.WhereCriteria = new Dictionary({
+ { "contact_object_id", user }
+ });
+ queries.emplace_back(std::move(query1));
+
+ if (groups) {
+ ObjectLock olock(groups);
+ for (const String& groupName : groups) {
+ UserGroup::Ptr group = UserGroup::GetByName(groupName);
+
+ DbQuery query2;
+ query2.Table = DbType::GetByName("UserGroup")->GetTable() + "_members";
+ query2.Type = DbQueryInsert | DbQueryUpdate;
+ query2.Category = DbCatConfig;
+ query2.Fields = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "contactgroup_id", DbValue::FromObjectInsertID(group) },
+ { "contact_object_id", user }
+ });
+ query2.WhereCriteria = new Dictionary({
+ { "instance_id", 0 }, /* DbConnection class fills in real ID */
+ { "contactgroup_id", DbValue::FromObjectInsertID(group) },
+ { "contact_object_id", user }
+ });
+ queries.emplace_back(std::move(query2));
+ }
+ }
+
+ DbObject::OnMultipleQueries(queries);
+
+ queries.clear();
+
+ DbQuery query2;
+ query2.Table = "contact_addresses";
+ query2.Type = DbQueryDelete;
+ query2.Category = DbCatConfig;
+ query2.WhereCriteria = new Dictionary({
+ { "contact_id", DbValue::FromObjectInsertID(user) }
+ });
+ queries.emplace_back(std::move(query2));
+
+ Dictionary::Ptr vars = user->GetVars();
+
+ if (vars) { /* This is sparta. */
+ for (int i = 1; i <= 6; i++) {
+ String key = "address" + Convert::ToString(i);
+
+ if (!vars->Contains(key))
+ continue;
+
+ String val = vars->Get(key);
+
+ DbQuery query;
+ query.Type = DbQueryInsert;
+ query.Table = "contact_addresses";
+ query.Category = DbCatConfig;
+ query.Fields = new Dictionary({
+ { "contact_id", DbValue::FromObjectInsertID(user) },
+ { "address_number", i },
+ { "address", val },
+ { "instance_id", 0 } /* DbConnection class fills in real ID */
+
+ });
+ queries.emplace_back(std::move(query));
+ }
+ }
+
+ DbObject::OnMultipleQueries(queries);
+}
+
+String UserDbObject::CalculateConfigHash(const Dictionary::Ptr& configFields) const
+{
+ String hashData = DbObject::CalculateConfigHash(configFields);
+
+ User::Ptr user = static_pointer_cast<User>(GetObject());
+
+ Array::Ptr groups = user->GetGroups();
+
+ if (groups) {
+ groups = groups->ShallowClone();
+ ObjectLock oLock (groups);
+ std::sort(groups->Begin(), groups->End());
+ hashData += DbObject::HashValue(groups);
+ }
+
+ return SHA256(hashData);
+}
diff --git a/lib/db_ido/userdbobject.hpp b/lib/db_ido/userdbobject.hpp
new file mode 100644
index 0000000..e0f36c5
--- /dev/null
+++ b/lib/db_ido/userdbobject.hpp
@@ -0,0 +1,35 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef USERDBOBJECT_H
+#define USERDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+/**
+ * A User database object.
+ *
+ * @ingroup ido
+ */
+class UserDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(UserDbObject);
+
+ UserDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+protected:
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+
+ void OnConfigUpdateHeavy() override;
+
+ String CalculateConfigHash(const Dictionary::Ptr& configFields) const override;
+};
+
+}
+
+#endif /* USERDBOBJECT_H */
diff --git a/lib/db_ido/usergroupdbobject.cpp b/lib/db_ido/usergroupdbobject.cpp
new file mode 100644
index 0000000..23b3581
--- /dev/null
+++ b/lib/db_ido/usergroupdbobject.cpp
@@ -0,0 +1,30 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/usergroupdbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "base/objectlock.hpp"
+#include "base/initialize.hpp"
+#include "base/configtype.hpp"
+
+using namespace icinga;
+
+REGISTER_DBTYPE(UserGroup, "contactgroup", DbObjectTypeContactGroup, "contactgroup_object_id", UserGroupDbObject);
+
+UserGroupDbObject::UserGroupDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr UserGroupDbObject::GetConfigFields() const
+{
+ UserGroup::Ptr group = static_pointer_cast<UserGroup>(GetObject());
+
+ return new Dictionary({
+ { "alias", group->GetDisplayName() }
+ });
+}
+
+Dictionary::Ptr UserGroupDbObject::GetStatusFields() const
+{
+ return nullptr;
+}
diff --git a/lib/db_ido/usergroupdbobject.hpp b/lib/db_ido/usergroupdbobject.hpp
new file mode 100644
index 0000000..9469823
--- /dev/null
+++ b/lib/db_ido/usergroupdbobject.hpp
@@ -0,0 +1,31 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef USERGROUPDBOBJECT_H
+#define USERGROUPDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "icinga/usergroup.hpp"
+#include "base/configobject.hpp"
+
+namespace icinga
+{
+
+/**
+ * A UserGroup database object.
+ *
+ * @ingroup ido
+ */
+class UserGroupDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(UserGroupDbObject);
+
+ UserGroupDbObject(const DbType::Ptr& type, const String& name1, const String& name2);
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+};
+
+}
+
+#endif /* USERGROUPDBOBJECT_H */
diff --git a/lib/db_ido/zonedbobject.cpp b/lib/db_ido/zonedbobject.cpp
new file mode 100644
index 0000000..b8ad0c1
--- /dev/null
+++ b/lib/db_ido/zonedbobject.cpp
@@ -0,0 +1,38 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/zonedbobject.hpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "base/logger.hpp"
+
+using namespace icinga;
+
+
+REGISTER_DBTYPE(Zone, "zone", DbObjectTypeZone, "zone_object_id", ZoneDbObject);
+
+ZoneDbObject::ZoneDbObject(const DbType::Ptr& type, const String& name1, const String& name2)
+ : DbObject(type, name1, name2)
+{ }
+
+Dictionary::Ptr ZoneDbObject::GetConfigFields() const
+{
+ Zone::Ptr zone = static_pointer_cast<Zone>(GetObject());
+
+ return new Dictionary({
+ { "is_global", zone->IsGlobal() ? 1 : 0 },
+ { "parent_zone_object_id", zone->GetParent() }
+
+ });
+}
+
+Dictionary::Ptr ZoneDbObject::GetStatusFields() const
+{
+ Zone::Ptr zone = static_pointer_cast<Zone>(GetObject());
+
+ Log(LogDebug, "ZoneDbObject")
+ << "update status for zone '" << zone->GetName() << "'";
+
+ return new Dictionary({
+ { "parent_zone_object_id", zone->GetParent() }
+ });
+}
diff --git a/lib/db_ido/zonedbobject.hpp b/lib/db_ido/zonedbobject.hpp
new file mode 100644
index 0000000..3901c81
--- /dev/null
+++ b/lib/db_ido/zonedbobject.hpp
@@ -0,0 +1,31 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef ZONEDBOBJECT_H
+#define ZONEDBOBJECT_H
+
+#include "db_ido/dbobject.hpp"
+#include "base/configobject.hpp"
+#include "remote/zone.hpp"
+
+namespace icinga
+{
+
+/**
+ * An Endpoint database object.
+ *
+ * @ingroup ido
+ */
+class ZoneDbObject final : public DbObject
+{
+public:
+ DECLARE_PTR_TYPEDEFS(ZoneDbObject);
+
+ ZoneDbObject(const intrusive_ptr<DbType>& type, const String& name1, const String& name2);
+
+ Dictionary::Ptr GetConfigFields() const override;
+ Dictionary::Ptr GetStatusFields() const override;
+};
+
+}
+
+#endif /* ZONEDBOBJECT_H */
diff --git a/lib/db_ido_mysql/CMakeLists.txt b/lib/db_ido_mysql/CMakeLists.txt
new file mode 100644
index 0000000..70cb90d
--- /dev/null
+++ b/lib/db_ido_mysql/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+
+mkclass_target(idomysqlconnection.ti idomysqlconnection-ti.cpp idomysqlconnection-ti.hpp)
+
+set(db_ido_mysql_SOURCES
+ idomysqlconnection.cpp idomysqlconnection.hpp idomysqlconnection-ti.hpp
+)
+
+if(ICINGA2_UNITY_BUILD)
+ mkunity_target(db_ido_mysql db_ido_mysql db_ido_mysql_SOURCES)
+endif()
+
+add_library(db_ido_mysql OBJECT ${db_ido_mysql_SOURCES})
+
+include_directories(${MYSQL_INCLUDE_DIR})
+
+add_dependencies(db_ido_mysql base config icinga db_ido)
+
+set_target_properties (
+ db_ido_mysql PROPERTIES
+ FOLDER Components
+)
+
+install_if_not_exists(
+ ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-mysql.conf
+ ${ICINGA2_CONFIGDIR}/features-available
+)
+
+install(
+ DIRECTORY schema
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-mysql
+ FILES_MATCHING PATTERN "*.sql"
+)
+
+install(
+ DIRECTORY schema/upgrade
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-mysql/schema
+ FILES_MATCHING PATTERN "*.sql"
+)
+
+set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE)
diff --git a/lib/db_ido_mysql/idomysqlconnection.cpp b/lib/db_ido_mysql/idomysqlconnection.cpp
new file mode 100644
index 0000000..42c8332
--- /dev/null
+++ b/lib/db_ido_mysql/idomysqlconnection.cpp
@@ -0,0 +1,1268 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido_mysql/idomysqlconnection.hpp"
+#include "db_ido_mysql/idomysqlconnection-ti.cpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "base/logger.hpp"
+#include "base/objectlock.hpp"
+#include "base/convert.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/application.hpp"
+#include "base/configtype.hpp"
+#include "base/exception.hpp"
+#include "base/statsfunction.hpp"
+#include "base/defer.hpp"
+#include <utility>
+
+using namespace icinga;
+
+REGISTER_TYPE(IdoMysqlConnection);
+REGISTER_STATSFUNCTION(IdoMysqlConnection, &IdoMysqlConnection::StatsFunc);
+
+const char * IdoMysqlConnection::GetLatestSchemaVersion() const noexcept
+{
+ return "1.15.1";
+}
+
+const char * IdoMysqlConnection::GetCompatSchemaVersion() const noexcept
+{
+ return "1.14.3";
+}
+
+void IdoMysqlConnection::OnConfigLoaded()
+{
+ ObjectImpl<IdoMysqlConnection>::OnConfigLoaded();
+
+ m_QueryQueue.SetName("IdoMysqlConnection, " + GetName());
+
+ Library shimLibrary{"mysql_shim"};
+
+ auto create_mysql_shim = shimLibrary.GetSymbolAddress<create_mysql_shim_ptr>("create_mysql_shim");
+
+ m_Mysql.reset(create_mysql_shim());
+
+ std::swap(m_Library, shimLibrary);
+}
+
+void IdoMysqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)
+{
+ DictionaryData nodes;
+
+ for (const IdoMysqlConnection::Ptr& idomysqlconnection : ConfigType::GetObjectsByType<IdoMysqlConnection>()) {
+ size_t queryQueueItems = idomysqlconnection->m_QueryQueue.GetLength();
+ double queryQueueItemRate = idomysqlconnection->m_QueryQueue.GetTaskCount(60) / 60.0;
+
+ nodes.emplace_back(idomysqlconnection->GetName(), new Dictionary({
+ { "version", idomysqlconnection->GetSchemaVersion() },
+ { "instance_name", idomysqlconnection->GetInstanceName() },
+ { "connected", idomysqlconnection->GetConnected() },
+ { "query_queue_items", queryQueueItems },
+ { "query_queue_item_rate", queryQueueItemRate }
+ }));
+
+ perfdata->Add(new PerfdataValue("idomysqlconnection_" + idomysqlconnection->GetName() + "_queries_rate", idomysqlconnection->GetQueryCount(60) / 60.0));
+ perfdata->Add(new PerfdataValue("idomysqlconnection_" + idomysqlconnection->GetName() + "_queries_1min", idomysqlconnection->GetQueryCount(60)));
+ perfdata->Add(new PerfdataValue("idomysqlconnection_" + idomysqlconnection->GetName() + "_queries_5mins", idomysqlconnection->GetQueryCount(5 * 60)));
+ perfdata->Add(new PerfdataValue("idomysqlconnection_" + idomysqlconnection->GetName() + "_queries_15mins", idomysqlconnection->GetQueryCount(15 * 60)));
+ perfdata->Add(new PerfdataValue("idomysqlconnection_" + idomysqlconnection->GetName() + "_query_queue_items", queryQueueItems));
+ perfdata->Add(new PerfdataValue("idomysqlconnection_" + idomysqlconnection->GetName() + "_query_queue_item_rate", queryQueueItemRate));
+ }
+
+ status->Set("idomysqlconnection", new Dictionary(std::move(nodes)));
+}
+
+void IdoMysqlConnection::Resume()
+{
+ Log(LogInformation, "IdoMysqlConnection")
+ << "'" << GetName() << "' resumed.";
+
+ SetConnected(false);
+
+ m_QueryQueue.SetExceptionCallback([this](boost::exception_ptr exp) { ExceptionHandler(std::move(exp)); });
+
+ /* Immediately try to connect on Resume() without timer. */
+ m_QueryQueue.Enqueue([this]() { Reconnect(); }, PriorityImmediate);
+
+ m_TxTimer = new Timer();
+ m_TxTimer->SetInterval(1);
+ m_TxTimer->OnTimerExpired.connect([this](const Timer * const&) { NewTransaction(); });
+ m_TxTimer->Start();
+
+ m_ReconnectTimer = new Timer();
+ m_ReconnectTimer->SetInterval(10);
+ m_ReconnectTimer->OnTimerExpired.connect([this](const Timer * const&){ ReconnectTimerHandler(); });
+ m_ReconnectTimer->Start();
+
+ /* Start with queries after connect. */
+ DbConnection::Resume();
+
+ ASSERT(m_Mysql->thread_safe());
+}
+
+void IdoMysqlConnection::Pause()
+{
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Attempting to pause '" << GetName() << "'.";
+
+ DbConnection::Pause();
+
+ m_ReconnectTimer.reset();
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Rescheduling disconnect task.";
+#endif /* I2_DEBUG */
+
+ Log(LogInformation, "IdoMysqlConnection")
+ << "'" << GetName() << "' paused.";
+
+}
+
+void IdoMysqlConnection::ExceptionHandler(boost::exception_ptr exp)
+{
+ Log(LogCritical, "IdoMysqlConnection", "Exception during database operation: Verify that your database is operational!");
+
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Exception during database operation: " << DiagnosticInformation(std::move(exp));
+
+ if (GetConnected()) {
+ m_Mysql->close(&m_Connection);
+
+ SetConnected(false);
+ }
+}
+
+void IdoMysqlConnection::AssertOnWorkQueue()
+{
+ ASSERT(m_QueryQueue.IsWorkerThread());
+}
+
+void IdoMysqlConnection::Disconnect()
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected())
+ return;
+
+ Query("COMMIT");
+ m_Mysql->close(&m_Connection);
+
+ SetConnected(false);
+
+ Log(LogInformation, "IdoMysqlConnection")
+ << "Disconnected from '" << GetName() << "' database '" << GetDatabase() << "'.";
+}
+
+void IdoMysqlConnection::NewTransaction()
+{
+ if (IsPaused() && GetPauseCalled())
+ return;
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling new transaction and finishing async queries.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this]() { InternalNewTransaction(); }, PriorityHigh);
+}
+
+void IdoMysqlConnection::InternalNewTransaction()
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected())
+ return;
+
+ IncreasePendingQueries(2);
+
+ AsyncQuery("COMMIT");
+ AsyncQuery("BEGIN");
+
+ FinishAsyncQueries();
+}
+
+void IdoMysqlConnection::ReconnectTimerHandler()
+{
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling reconnect task.";
+#endif /* I2_DEBUG */
+
+ /* Only allow Reconnect events with high priority. */
+ m_QueryQueue.Enqueue([this]() { Reconnect(); }, PriorityImmediate);
+}
+
+void IdoMysqlConnection::Reconnect()
+{
+ AssertOnWorkQueue();
+
+ if (!IsActive())
+ return;
+
+ CONTEXT("Reconnecting to MySQL IDO database '" + GetName() + "'");
+
+ double startTime = Utility::GetTime();
+
+ SetShouldConnect(true);
+
+ bool reconnect = false;
+
+ /* Ensure to close old connections first. */
+ if (GetConnected()) {
+ /* Check if we're really still connected */
+ if (m_Mysql->ping(&m_Connection) == 0)
+ return;
+
+ m_Mysql->close(&m_Connection);
+ SetConnected(false);
+ reconnect = true;
+ }
+
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Reconnect: Clearing ID cache.";
+
+ ClearIDCache();
+
+ String ihost, isocket_path, iuser, ipasswd, idb;
+ String isslKey, isslCert, isslCa, isslCaPath, isslCipher;
+ const char *host, *socket_path, *user , *passwd, *db;
+ const char *sslKey, *sslCert, *sslCa, *sslCaPath, *sslCipher;
+ bool enableSsl;
+ long port;
+
+ ihost = GetHost();
+ isocket_path = GetSocketPath();
+ iuser = GetUser();
+ ipasswd = GetPassword();
+ idb = GetDatabase();
+
+ enableSsl = GetEnableSsl();
+ isslKey = GetSslKey();
+ isslCert = GetSslCert();
+ isslCa = GetSslCa();
+ isslCaPath = GetSslCapath();
+ isslCipher = GetSslCipher();
+
+ host = (!ihost.IsEmpty()) ? ihost.CStr() : nullptr;
+ port = GetPort();
+ socket_path = (!isocket_path.IsEmpty()) ? isocket_path.CStr() : nullptr;
+ user = (!iuser.IsEmpty()) ? iuser.CStr() : nullptr;
+ passwd = (!ipasswd.IsEmpty()) ? ipasswd.CStr() : nullptr;
+ db = (!idb.IsEmpty()) ? idb.CStr() : nullptr;
+
+ sslKey = (!isslKey.IsEmpty()) ? isslKey.CStr() : nullptr;
+ sslCert = (!isslCert.IsEmpty()) ? isslCert.CStr() : nullptr;
+ sslCa = (!isslCa.IsEmpty()) ? isslCa.CStr() : nullptr;
+ sslCaPath = (!isslCaPath.IsEmpty()) ? isslCaPath.CStr() : nullptr;
+ sslCipher = (!isslCipher.IsEmpty()) ? isslCipher.CStr() : nullptr;
+
+ /* connection */
+ if (!m_Mysql->init(&m_Connection)) {
+ Log(LogCritical, "IdoMysqlConnection")
+ << "mysql_init() failed: out of memory";
+
+ BOOST_THROW_EXCEPTION(std::bad_alloc());
+ }
+
+ /* Read "latin1" (here, in the schema and in Icinga Web) as "bytes".
+ Icinga 2 and Icinga Web use byte-strings everywhere and every byte-string is a valid latin1 string.
+ This way the (actually mostly UTF-8) bytes are transferred end-to-end as-is. */
+ m_Mysql->options(&m_Connection, MYSQL_SET_CHARSET_NAME, "latin1");
+
+ if (enableSsl)
+ m_Mysql->ssl_set(&m_Connection, sslKey, sslCert, sslCa, sslCaPath, sslCipher);
+
+ if (!m_Mysql->real_connect(&m_Connection, host, user, passwd, db, port, socket_path, CLIENT_FOUND_ROWS | CLIENT_MULTI_STATEMENTS)) {
+ Log(LogCritical, "IdoMysqlConnection")
+ << "Connection to database '" << db << "' with user '" << user << "' on '" << host << ":" << port
+ << "' " << (enableSsl ? "(SSL enabled) " : "") << "failed: \"" << m_Mysql->error(&m_Connection) << "\"";
+
+ BOOST_THROW_EXCEPTION(std::runtime_error(m_Mysql->error(&m_Connection)));
+ }
+
+ Log(LogNotice, "IdoMysqlConnection")
+ << "Reconnect: '" << GetName() << "' is now connected to database '" << GetDatabase() << "'.";
+
+ SetConnected(true);
+
+ IdoMysqlResult result = Query("SELECT @@global.max_allowed_packet AS max_allowed_packet");
+
+ Dictionary::Ptr row = FetchRow(result);
+
+ if (row)
+ m_MaxPacketSize = row->Get("max_allowed_packet");
+ else
+ m_MaxPacketSize = 64 * 1024;
+
+ DiscardRows(result);
+
+ String dbVersionName = "idoutils";
+ result = Query("SELECT version FROM " + GetTablePrefix() + "dbversion WHERE name='" + Escape(dbVersionName) + "'");
+
+ row = FetchRow(result);
+
+ if (!row) {
+ m_Mysql->close(&m_Connection);
+ SetConnected(false);
+
+ Log(LogCritical, "IdoMysqlConnection", "Schema does not provide any valid version! Verify your schema installation.");
+
+ BOOST_THROW_EXCEPTION(std::runtime_error("Invalid schema."));
+ }
+
+ DiscardRows(result);
+
+ String version = row->Get("version");
+
+ SetSchemaVersion(version);
+
+ if (Utility::CompareVersion(GetCompatSchemaVersion(), version) < 0) {
+ m_Mysql->close(&m_Connection);
+ SetConnected(false);
+
+ Log(LogCritical, "IdoMysqlConnection")
+ << "Schema version '" << version << "' does not match the required version '"
+ << GetCompatSchemaVersion() << "' (or newer)! Please check the upgrade documentation at "
+ << "https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/#upgrading-mysql-db";
+
+ BOOST_THROW_EXCEPTION(std::runtime_error("Schema version mismatch."));
+ }
+
+ String instanceName = GetInstanceName();
+
+ result = Query("SELECT instance_id FROM " + GetTablePrefix() + "instances WHERE instance_name = '" + Escape(instanceName) + "'");
+ row = FetchRow(result);
+
+ if (!row) {
+ Query("INSERT INTO " + GetTablePrefix() + "instances (instance_name, instance_description) VALUES ('" + Escape(instanceName) + "', '" + Escape(GetInstanceDescription()) + "')");
+ m_InstanceID = GetLastInsertID();
+ } else {
+ m_InstanceID = DbReference(row->Get("instance_id"));
+ }
+
+ DiscardRows(result);
+
+ Endpoint::Ptr my_endpoint = Endpoint::GetLocalEndpoint();
+
+ /* we have an endpoint in a cluster setup, so decide if we can proceed here */
+ if (my_endpoint && GetHAMode() == HARunOnce) {
+ /* get the current endpoint writing to programstatus table */
+ result = Query("SELECT UNIX_TIMESTAMP(status_update_time) AS status_update_time, endpoint_name FROM " +
+ GetTablePrefix() + "programstatus WHERE instance_id = " + Convert::ToString(m_InstanceID));
+ row = FetchRow(result);
+ DiscardRows(result);
+
+ String endpoint_name;
+
+ if (row)
+ endpoint_name = row->Get("endpoint_name");
+ else
+ Log(LogNotice, "IdoMysqlConnection", "Empty program status table");
+
+ /* if we did not write into the database earlier, another instance is active */
+ if (endpoint_name != my_endpoint->GetName()) {
+ double status_update_time;
+
+ if (row)
+ status_update_time = row->Get("status_update_time");
+ else
+ status_update_time = 0;
+
+ double now = Utility::GetTime();
+
+ double status_update_age = now - status_update_time;
+ double failoverTimeout = GetFailoverTimeout();
+
+ if (status_update_age < failoverTimeout) {
+ Log(LogInformation, "IdoMysqlConnection")
+ << "Last update by endpoint '" << endpoint_name << "' was "
+ << status_update_age << "s ago (< failover timeout of " << failoverTimeout << "s). Retrying.";
+
+ m_Mysql->close(&m_Connection);
+ SetConnected(false);
+ SetShouldConnect(false);
+
+ return;
+ }
+
+ /* activate the IDO only, if we're authoritative in this zone */
+ if (IsPaused()) {
+ Log(LogNotice, "IdoMysqlConnection")
+ << "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out.";
+
+ m_Mysql->close(&m_Connection);
+ SetConnected(false);
+
+ return;
+ }
+
+ SetLastFailover(now);
+
+ Log(LogInformation, "IdoMysqlConnection")
+ << "Last update by endpoint '" << endpoint_name << "' was "
+ << status_update_age << "s ago. Taking over '" << GetName() << "' in HA zone '" << Zone::GetLocalZone()->GetName() << "'.";
+ }
+
+ Log(LogNotice, "IdoMysqlConnection", "Enabling IDO connection in HA zone.");
+ }
+
+ Log(LogInformation, "IdoMysqlConnection")
+ << "MySQL IDO instance id: " << static_cast<long>(m_InstanceID) << " (schema version: '" + version + "')";
+
+ /* set session time zone to utc */
+ Query("SET SESSION TIME_ZONE='+00:00'");
+
+ Query("SET SESSION SQL_MODE='NO_AUTO_VALUE_ON_ZERO'");
+
+ Query("BEGIN");
+
+ /* update programstatus table */
+ UpdateProgramStatus();
+
+ /* record connection */
+ Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
+ "(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES ("
+ + Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), 'icinga2 db_ido_mysql', '" + Escape(Application::GetAppVersion())
+ + "', '" + (reconnect ? "RECONNECT" : "INITIAL") + "', NOW())");
+
+ /* clear config tables for the initial config dump */
+ PrepareDatabase();
+
+ std::ostringstream q1buf;
+ q1buf << "SELECT object_id, objecttype_id, name1, name2, is_active FROM " + GetTablePrefix() + "objects WHERE instance_id = " << static_cast<long>(m_InstanceID);
+ result = Query(q1buf.str());
+
+ std::vector<DbObject::Ptr> activeDbObjs;
+
+ while ((row = FetchRow(result))) {
+ DbType::Ptr dbtype = DbType::GetByID(row->Get("objecttype_id"));
+
+ if (!dbtype)
+ continue;
+
+ DbObject::Ptr dbobj = dbtype->GetOrCreateObjectByName(row->Get("name1"), row->Get("name2"));
+ SetObjectID(dbobj, DbReference(row->Get("object_id")));
+ bool active = row->Get("is_active");
+ SetObjectActive(dbobj, active);
+
+ if (active)
+ activeDbObjs.emplace_back(std::move(dbobj));
+ }
+
+ SetIDCacheValid(true);
+
+ EnableActiveChangedHandler();
+
+ for (const DbObject::Ptr& dbobj : activeDbObjs) {
+ if (dbobj->GetObject())
+ continue;
+
+ Log(LogNotice, "IdoMysqlConnection")
+ << "Deactivate deleted object name1: '" << dbobj->GetName1()
+ << "' name2: '" << dbobj->GetName2() + "'.";
+ DeactivateObject(dbobj);
+ }
+
+ UpdateAllObjects();
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling session table clear and finish connect task.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this]() { ClearTablesBySession(); }, PriorityNormal);
+
+ m_QueryQueue.Enqueue([this, startTime]() { FinishConnect(startTime); }, PriorityNormal);
+}
+
+void IdoMysqlConnection::FinishConnect(double startTime)
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected() || IsPaused())
+ return;
+
+ FinishAsyncQueries();
+
+ Log(LogInformation, "IdoMysqlConnection")
+ << "Finished reconnecting to '" << GetName() << "' database '" << GetDatabase() << "' in "
+ << std::setw(2) << Utility::GetTime() - startTime << " second(s).";
+
+ Query("COMMIT");
+ Query("BEGIN");
+}
+
+void IdoMysqlConnection::ClearTablesBySession()
+{
+ /* delete all comments and downtimes without current session token */
+ ClearTableBySession("comments");
+ ClearTableBySession("scheduleddowntime");
+}
+
+void IdoMysqlConnection::ClearTableBySession(const String& table)
+{
+ Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
+ Convert::ToString(static_cast<long>(m_InstanceID)) + " AND session_token <> " +
+ Convert::ToString(GetSessionToken()));
+}
+
+void IdoMysqlConnection::AsyncQuery(const String& query, const std::function<void (const IdoMysqlResult&)>& callback)
+{
+ AssertOnWorkQueue();
+
+ IdoAsyncQuery aq;
+ aq.Query = query;
+ /* XXX: Important: The callback must not immediately execute a query, but enqueue it!
+ * See https://github.com/Icinga/icinga2/issues/4603 for details.
+ */
+ aq.Callback = callback;
+ m_AsyncQueries.emplace_back(std::move(aq));
+}
+
+void IdoMysqlConnection::FinishAsyncQueries()
+{
+ std::vector<IdoAsyncQuery> queries;
+ m_AsyncQueries.swap(queries);
+
+ std::vector<IdoAsyncQuery>::size_type offset = 0;
+
+ // This will be executed if there is a problem with executing the queries,
+ // at which point this function throws an exception and the queries should
+ // not be listed as still pending in the queue.
+ Defer decreaseQueries ([this, &offset, &queries]() {
+ auto lostQueries = queries.size() - offset;
+
+ if (lostQueries > 0) {
+ DecreasePendingQueries(lostQueries);
+ }
+ });
+
+ while (offset < queries.size()) {
+ std::ostringstream querybuf;
+
+ std::vector<IdoAsyncQuery>::size_type count = 0;
+ size_t num_bytes = 0;
+
+ Defer decreaseQueries ([this, &offset, &count]() {
+ offset += count;
+ DecreasePendingQueries(count);
+ m_UncommittedAsyncQueries += count;
+ });
+
+ for (std::vector<IdoAsyncQuery>::size_type i = offset; i < queries.size(); i++) {
+ const IdoAsyncQuery& aq = queries[i];
+
+ size_t size_query = aq.Query.GetLength() + 1;
+
+ if (count > 0) {
+ if (num_bytes + size_query > m_MaxPacketSize - 512)
+ break;
+
+ querybuf << ";";
+ }
+
+ IncreaseQueryCount();
+ count++;
+
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Query: " << aq.Query;
+
+ querybuf << aq.Query;
+ num_bytes += size_query;
+ }
+
+ String query = querybuf.str();
+
+ if (m_Mysql->query(&m_Connection, query.CStr()) != 0) {
+ std::ostringstream msgbuf;
+ String message = m_Mysql->error(&m_Connection);
+ msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
+ Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
+
+ BOOST_THROW_EXCEPTION(
+ database_error()
+ << errinfo_message(m_Mysql->error(&m_Connection))
+ << errinfo_database_query(query)
+ );
+ }
+
+ for (std::vector<IdoAsyncQuery>::size_type i = offset; i < offset + count; i++) {
+ const IdoAsyncQuery& aq = queries[i];
+
+ MYSQL_RES *result = m_Mysql->store_result(&m_Connection);
+
+ m_AffectedRows = m_Mysql->affected_rows(&m_Connection);
+
+ IdoMysqlResult iresult;
+
+ if (!result) {
+ if (m_Mysql->field_count(&m_Connection) > 0) {
+ std::ostringstream msgbuf;
+ String message = m_Mysql->error(&m_Connection);
+ msgbuf << "Error \"" << message << "\" when executing query \"" << aq.Query << "\"";
+ Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
+
+ BOOST_THROW_EXCEPTION(
+ database_error()
+ << errinfo_message(m_Mysql->error(&m_Connection))
+ << errinfo_database_query(query)
+ );
+ }
+ } else
+ iresult = IdoMysqlResult(result, [this](MYSQL_RES* result) { m_Mysql->free_result(result); });
+
+ if (aq.Callback)
+ aq.Callback(iresult);
+
+ if (m_Mysql->next_result(&m_Connection) > 0) {
+ std::ostringstream msgbuf;
+ String message = m_Mysql->error(&m_Connection);
+ msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
+ Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
+
+ BOOST_THROW_EXCEPTION(
+ database_error()
+ << errinfo_message(m_Mysql->error(&m_Connection))
+ << errinfo_database_query(query)
+ );
+ }
+ }
+ }
+
+ if (m_UncommittedAsyncQueries > 25000) {
+ m_UncommittedAsyncQueries = 0;
+
+ Query("COMMIT");
+ Query("BEGIN");
+ }
+}
+
+IdoMysqlResult IdoMysqlConnection::Query(const String& query)
+{
+ AssertOnWorkQueue();
+
+ IncreasePendingQueries(1);
+ Defer decreaseQueries ([this]() { DecreasePendingQueries(1); });
+
+ /* finish all async queries to maintain the right order for queries */
+ FinishAsyncQueries();
+
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Query: " << query;
+
+ IncreaseQueryCount();
+
+ if (m_Mysql->query(&m_Connection, query.CStr()) != 0) {
+ std::ostringstream msgbuf;
+ String message = m_Mysql->error(&m_Connection);
+ msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
+ Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
+
+ BOOST_THROW_EXCEPTION(
+ database_error()
+ << errinfo_message(m_Mysql->error(&m_Connection))
+ << errinfo_database_query(query)
+ );
+ }
+
+ MYSQL_RES *result = m_Mysql->store_result(&m_Connection);
+
+ m_AffectedRows = m_Mysql->affected_rows(&m_Connection);
+
+ if (!result) {
+ if (m_Mysql->field_count(&m_Connection) > 0) {
+ std::ostringstream msgbuf;
+ String message = m_Mysql->error(&m_Connection);
+ msgbuf << "Error \"" << message << "\" when executing query \"" << query << "\"";
+ Log(LogCritical, "IdoMysqlConnection", msgbuf.str());
+
+ BOOST_THROW_EXCEPTION(
+ database_error()
+ << errinfo_message(m_Mysql->error(&m_Connection))
+ << errinfo_database_query(query)
+ );
+ }
+
+ return IdoMysqlResult();
+ }
+
+ return IdoMysqlResult(result, [this](MYSQL_RES* result) { m_Mysql->free_result(result); });
+}
+
+DbReference IdoMysqlConnection::GetLastInsertID()
+{
+ AssertOnWorkQueue();
+
+ return {static_cast<long>(m_Mysql->insert_id(&m_Connection))};
+}
+
+int IdoMysqlConnection::GetAffectedRows()
+{
+ AssertOnWorkQueue();
+
+ return m_AffectedRows;
+}
+
+String IdoMysqlConnection::Escape(const String& s)
+{
+ AssertOnWorkQueue();
+
+ String utf8s = Utility::ValidateUTF8(s);
+
+ size_t length = utf8s.GetLength();
+ auto *to = new char[utf8s.GetLength() * 2 + 1];
+
+ m_Mysql->real_escape_string(&m_Connection, to, utf8s.CStr(), length);
+
+ String result = String(to);
+
+ delete [] to;
+
+ return result;
+}
+
+Dictionary::Ptr IdoMysqlConnection::FetchRow(const IdoMysqlResult& result)
+{
+ AssertOnWorkQueue();
+
+ MYSQL_ROW row;
+ MYSQL_FIELD *field;
+ unsigned long *lengths, i;
+
+ row = m_Mysql->fetch_row(result.get());
+
+ if (!row)
+ return nullptr;
+
+ lengths = m_Mysql->fetch_lengths(result.get());
+
+ if (!lengths)
+ return nullptr;
+
+ Dictionary::Ptr dict = new Dictionary();
+
+ m_Mysql->field_seek(result.get(), 0);
+ for (field = m_Mysql->fetch_field(result.get()), i = 0; field; field = m_Mysql->fetch_field(result.get()), i++)
+ dict->Set(field->name, String(row[i], row[i] + lengths[i]));
+
+ return dict;
+}
+
+void IdoMysqlConnection::DiscardRows(const IdoMysqlResult& result)
+{
+ Dictionary::Ptr row;
+
+ while ((row = FetchRow(result)))
+ ; /* empty loop body */
+}
+
+void IdoMysqlConnection::ActivateObject(const DbObject::Ptr& dbobj)
+{
+ if (IsPaused())
+ return;
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling object activation task for '" << dbobj->GetName1() << "!" << dbobj->GetName2() << "'.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this, dbobj]() { InternalActivateObject(dbobj); }, PriorityNormal);
+}
+
+void IdoMysqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj)
+{
+ AssertOnWorkQueue();
+
+ if (IsPaused())
+ return;
+
+ if (!GetConnected())
+ return;
+
+ DbReference dbref = GetObjectID(dbobj);
+ std::ostringstream qbuf;
+
+ if (!dbref.IsValid()) {
+ if (!dbobj->GetName2().IsEmpty()) {
+ qbuf << "INSERT INTO " + GetTablePrefix() + "objects (instance_id, objecttype_id, name1, name2, is_active) VALUES ("
+ << static_cast<long>(m_InstanceID) << ", " << dbobj->GetType()->GetTypeID() << ", "
+ << "'" << Escape(dbobj->GetName1()) << "', '" << Escape(dbobj->GetName2()) << "', 1)";
+ } else {
+ qbuf << "INSERT INTO " + GetTablePrefix() + "objects (instance_id, objecttype_id, name1, is_active) VALUES ("
+ << static_cast<long>(m_InstanceID) << ", " << dbobj->GetType()->GetTypeID() << ", "
+ << "'" << Escape(dbobj->GetName1()) << "', 1)";
+ }
+
+ Query(qbuf.str());
+ SetObjectID(dbobj, GetLastInsertID());
+ } else {
+ qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 1 WHERE object_id = " << static_cast<long>(dbref);
+ IncreasePendingQueries(1);
+ AsyncQuery(qbuf.str());
+ }
+}
+
+void IdoMysqlConnection::DeactivateObject(const DbObject::Ptr& dbobj)
+{
+ if (IsPaused())
+ return;
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling object deactivation task for '" << dbobj->GetName1() << "!" << dbobj->GetName2() << "'.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this, dbobj]() { InternalDeactivateObject(dbobj); }, PriorityNormal);
+}
+
+void IdoMysqlConnection::InternalDeactivateObject(const DbObject::Ptr& dbobj)
+{
+ AssertOnWorkQueue();
+
+ if (IsPaused())
+ return;
+
+ if (!GetConnected())
+ return;
+
+ DbReference dbref = GetObjectID(dbobj);
+
+ if (!dbref.IsValid())
+ return;
+
+ std::ostringstream qbuf;
+ qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 0 WHERE object_id = " << static_cast<long>(dbref);
+ IncreasePendingQueries(1);
+ AsyncQuery(qbuf.str());
+
+ /* Note that we're _NOT_ clearing the db refs via SetReference/SetConfigUpdate/SetStatusUpdate
+ * because the object is still in the database. */
+
+ SetObjectActive(dbobj, false);
+}
+
+bool IdoMysqlConnection::FieldToEscapedString(const String& key, const Value& value, Value *result)
+{
+ if (key == "instance_id") {
+ *result = static_cast<long>(m_InstanceID);
+ return true;
+ } else if (key == "session_token") {
+ *result = GetSessionToken();
+ return true;
+ }
+
+ Value rawvalue = DbValue::ExtractValue(value);
+
+ if (rawvalue.GetType() == ValueEmpty) {
+ *result = "NULL";
+ } else if (rawvalue.IsObjectType<ConfigObject>()) {
+ DbObject::Ptr dbobjcol = DbObject::GetOrCreateByObject(rawvalue);
+
+ if (!dbobjcol) {
+ *result = 0;
+ return true;
+ }
+
+ if (!IsIDCacheValid())
+ return false;
+
+ DbReference dbrefcol;
+
+ if (DbValue::IsObjectInsertID(value)) {
+ dbrefcol = GetInsertID(dbobjcol);
+
+ if (!dbrefcol.IsValid())
+ return false;
+ } else {
+ dbrefcol = GetObjectID(dbobjcol);
+
+ if (!dbrefcol.IsValid()) {
+ InternalActivateObject(dbobjcol);
+
+ dbrefcol = GetObjectID(dbobjcol);
+
+ if (!dbrefcol.IsValid())
+ return false;
+ }
+ }
+
+ *result = static_cast<long>(dbrefcol);
+ } else if (DbValue::IsTimestamp(value)) {
+ long ts = rawvalue;
+ std::ostringstream msgbuf;
+ msgbuf << "FROM_UNIXTIME(" << ts << ")";
+ *result = Value(msgbuf.str());
+ } else if (DbValue::IsObjectInsertID(value)) {
+ auto id = static_cast<long>(rawvalue);
+
+ if (id <= 0)
+ return false;
+
+ *result = id;
+ return true;
+ } else {
+ Value fvalue;
+
+ if (rawvalue.IsBoolean())
+ fvalue = Convert::ToLong(rawvalue);
+ else
+ fvalue = rawvalue;
+
+ *result = "'" + Escape(fvalue) + "'";
+ }
+
+ return true;
+}
+
+void IdoMysqlConnection::ExecuteQuery(const DbQuery& query)
+{
+ if (IsPaused() && GetPauseCalled())
+ return;
+
+ ASSERT(query.Category != DbCatInvalid);
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling execute query task, type " << query.Type << ", table '" << query.Table << "'.";
+#endif /* I2_DEBUG */
+
+ IncreasePendingQueries(1);
+ m_QueryQueue.Enqueue([this, query]() { InternalExecuteQuery(query, -1); }, query.Priority, true);
+}
+
+void IdoMysqlConnection::ExecuteMultipleQueries(const std::vector<DbQuery>& queries)
+{
+ if (IsPaused())
+ return;
+
+ if (queries.empty())
+ return;
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling multiple execute query task, type " << queries[0].Type << ", table '" << queries[0].Table << "'.";
+#endif /* I2_DEBUG */
+
+ IncreasePendingQueries(queries.size());
+ m_QueryQueue.Enqueue([this, queries]() { InternalExecuteMultipleQueries(queries); }, queries[0].Priority, true);
+}
+
+bool IdoMysqlConnection::CanExecuteQuery(const DbQuery& query)
+{
+ if (query.Object && !IsIDCacheValid())
+ return false;
+
+ if (query.WhereCriteria) {
+ ObjectLock olock(query.WhereCriteria);
+ Value value;
+
+ for (const Dictionary::Pair& kv : query.WhereCriteria) {
+ if (!FieldToEscapedString(kv.first, kv.second, &value))
+ return false;
+ }
+ }
+
+ if (query.Fields) {
+ ObjectLock olock(query.Fields);
+
+ for (const Dictionary::Pair& kv : query.Fields) {
+ Value value;
+
+ if (!FieldToEscapedString(kv.first, kv.second, &value))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void IdoMysqlConnection::InternalExecuteMultipleQueries(const std::vector<DbQuery>& queries)
+{
+ AssertOnWorkQueue();
+
+ if (IsPaused()) {
+ DecreasePendingQueries(queries.size());
+ return;
+ }
+
+ if (!GetConnected()) {
+ DecreasePendingQueries(queries.size());
+ return;
+ }
+
+
+ for (const DbQuery& query : queries) {
+ ASSERT(query.Type == DbQueryNewTransaction || query.Category != DbCatInvalid);
+
+ if (!CanExecuteQuery(query)) {
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling multiple execute query task again: Cannot execute query now. Type '"
+ << query.Type << "', table '" << query.Table << "', queue size: '" << GetPendingQueryCount() << "'.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this, queries]() { InternalExecuteMultipleQueries(queries); }, query.Priority);
+ return;
+ }
+ }
+
+ for (const DbQuery& query : queries) {
+ InternalExecuteQuery(query);
+ }
+}
+
+void IdoMysqlConnection::InternalExecuteQuery(const DbQuery& query, int typeOverride)
+{
+ AssertOnWorkQueue();
+
+ if (IsPaused() && GetPauseCalled()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ if (!GetConnected()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ if (query.Type == DbQueryNewTransaction) {
+ DecreasePendingQueries(1);
+ InternalNewTransaction();
+ return;
+ }
+
+ /* check whether we're allowed to execute the query first */
+ if (GetCategoryFilter() != DbCatEverything && (query.Category & GetCategoryFilter()) == 0) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ if (query.Object && query.Object->GetObject()->GetExtension("agent_check").ToBool()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ /* check if there are missing object/insert ids and re-enqueue the query */
+ if (!CanExecuteQuery(query)) {
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling execute query task again: Cannot execute query now. Type '"
+ << typeOverride << "', table '" << query.Table << "', queue size: '" << GetPendingQueryCount() << "'.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this, query, typeOverride]() { InternalExecuteQuery(query, typeOverride); }, query.Priority);
+ return;
+ }
+
+ std::ostringstream qbuf, where;
+ int type;
+
+ if (query.WhereCriteria) {
+ where << " WHERE ";
+
+ ObjectLock olock(query.WhereCriteria);
+ Value value;
+ bool first = true;
+
+ for (const Dictionary::Pair& kv : query.WhereCriteria) {
+ if (!FieldToEscapedString(kv.first, kv.second, &value)) {
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling execute query task again: Cannot execute query now. Type '"
+ << typeOverride << "', table '" << query.Table << "', queue size: '" << GetPendingQueryCount() << "'.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this, query]() { InternalExecuteQuery(query, -1); }, query.Priority);
+ return;
+ }
+
+ if (!first)
+ where << " AND ";
+
+ where << kv.first << " = " << value;
+
+ if (first)
+ first = false;
+ }
+ }
+
+ type = (typeOverride != -1) ? typeOverride : query.Type;
+
+ bool upsert = false;
+
+ if ((type & DbQueryInsert) && (type & DbQueryUpdate)) {
+ bool hasid = false;
+
+ if (query.Object) {
+ if (query.ConfigUpdate)
+ hasid = GetConfigUpdate(query.Object);
+ else if (query.StatusUpdate)
+ hasid = GetStatusUpdate(query.Object);
+ }
+
+ if (!hasid)
+ upsert = true;
+
+ type = DbQueryUpdate;
+ }
+
+ if ((type & DbQueryInsert) && (type & DbQueryDelete)) {
+ std::ostringstream qdel;
+ qdel << "DELETE FROM " << GetTablePrefix() << query.Table << where.str();
+ IncreasePendingQueries(1);
+ AsyncQuery(qdel.str());
+
+ type = DbQueryInsert;
+ }
+
+ switch (type) {
+ case DbQueryInsert:
+ qbuf << "INSERT INTO " << GetTablePrefix() << query.Table;
+ break;
+ case DbQueryUpdate:
+ qbuf << "UPDATE " << GetTablePrefix() << query.Table << " SET";
+ break;
+ case DbQueryDelete:
+ qbuf << "DELETE FROM " << GetTablePrefix() << query.Table;
+ break;
+ default:
+ VERIFY(!"Invalid query type.");
+ }
+
+ if (type == DbQueryInsert || type == DbQueryUpdate) {
+ std::ostringstream colbuf, valbuf;
+
+ if (type == DbQueryUpdate && query.Fields->GetLength() == 0)
+ return;
+
+ ObjectLock olock(query.Fields);
+
+ bool first = true;
+ for (const Dictionary::Pair& kv : query.Fields) {
+ Value value;
+
+ if (!FieldToEscapedString(kv.first, kv.second, &value)) {
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Scheduling execute query task again: Cannot extract required INSERT/UPDATE fields, key '"
+ << kv.first << "', val '" << kv.second << "', type " << typeOverride << ", table '" << query.Table << "'.";
+#endif /* I2_DEBUG */
+
+ m_QueryQueue.Enqueue([this, query]() { InternalExecuteQuery(query, -1); }, query.Priority);
+ return;
+ }
+
+ if (type == DbQueryInsert) {
+ if (!first) {
+ colbuf << ", ";
+ valbuf << ", ";
+ }
+
+ colbuf << kv.first;
+ valbuf << value;
+ } else {
+ if (!first)
+ qbuf << ", ";
+
+ qbuf << " " << kv.first << " = " << value;
+ }
+
+ if (first)
+ first = false;
+ }
+
+ if (type == DbQueryInsert)
+ qbuf << " (" << colbuf.str() << ") VALUES (" << valbuf.str() << ")";
+ }
+
+ if (type != DbQueryInsert)
+ qbuf << where.str();
+
+ AsyncQuery(qbuf.str(), [this, query, type, upsert](const IdoMysqlResult&) { FinishExecuteQuery(query, type, upsert); });
+}
+
+void IdoMysqlConnection::FinishExecuteQuery(const DbQuery& query, int type, bool upsert)
+{
+ if (upsert && GetAffectedRows() == 0) {
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Rescheduling DELETE/INSERT query: Upsert UPDATE did not affect rows, type " << type << ", table '" << query.Table << "'.";
+#endif /* I2_DEBUG */
+
+ IncreasePendingQueries(1);
+ m_QueryQueue.Enqueue([this, query]() { InternalExecuteQuery(query, DbQueryDelete | DbQueryInsert); }, query.Priority);
+
+ return;
+ }
+
+ if (type == DbQueryInsert && query.Object) {
+ if (query.ConfigUpdate) {
+ SetInsertID(query.Object, GetLastInsertID());
+ SetConfigUpdate(query.Object, true);
+ } else if (query.StatusUpdate)
+ SetStatusUpdate(query.Object, true);
+ }
+
+ if (type == DbQueryInsert && query.Table == "notifications" && query.NotificationInsertID)
+ query.NotificationInsertID->SetValue(static_cast<long>(GetLastInsertID()));
+}
+
+void IdoMysqlConnection::CleanUpExecuteQuery(const String& table, const String& time_column, double max_age)
+{
+ if (IsPaused())
+ return;
+
+#ifdef I2_DEBUG /* I2_DEBUG */
+ Log(LogDebug, "IdoMysqlConnection")
+ << "Rescheduling cleanup query for table '" << table << "' and column '"
+ << time_column << "'. max_age is set to '" << max_age << "'.";
+#endif /* I2_DEBUG */
+
+ IncreasePendingQueries(1);
+ m_QueryQueue.Enqueue([this, table, time_column, max_age]() { InternalCleanUpExecuteQuery(table, time_column, max_age); }, PriorityLow, true);
+}
+
+void IdoMysqlConnection::InternalCleanUpExecuteQuery(const String& table, const String& time_column, double max_age)
+{
+ AssertOnWorkQueue();
+
+ if (IsPaused()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ if (!GetConnected()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ AsyncQuery("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
+ Convert::ToString(static_cast<long>(m_InstanceID)) + " AND " + time_column +
+ " < FROM_UNIXTIME(" + Convert::ToString(static_cast<long>(max_age)) + ")");
+}
+
+void IdoMysqlConnection::FillIDCache(const DbType::Ptr& type)
+{
+ String query = "SELECT " + type->GetIDColumn() + " AS object_id, " + type->GetTable() + "_id, config_hash FROM " + GetTablePrefix() + type->GetTable() + "s";
+ IdoMysqlResult result = Query(query);
+
+ Dictionary::Ptr row;
+
+ while ((row = FetchRow(result))) {
+ DbReference dbref(row->Get("object_id"));
+ SetInsertID(type, dbref, DbReference(row->Get(type->GetTable() + "_id")));
+ SetConfigHash(type, dbref, row->Get("config_hash"));
+ }
+}
+
+int IdoMysqlConnection::GetPendingQueryCount() const
+{
+ return m_QueryQueue.GetLength();
+}
diff --git a/lib/db_ido_mysql/idomysqlconnection.hpp b/lib/db_ido_mysql/idomysqlconnection.hpp
new file mode 100644
index 0000000..5a5c120
--- /dev/null
+++ b/lib/db_ido_mysql/idomysqlconnection.hpp
@@ -0,0 +1,114 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef IDOMYSQLCONNECTION_H
+#define IDOMYSQLCONNECTION_H
+
+#include "db_ido_mysql/idomysqlconnection-ti.hpp"
+#include "mysql_shim/mysqlinterface.hpp"
+#include "base/array.hpp"
+#include "base/timer.hpp"
+#include "base/workqueue.hpp"
+#include "base/library.hpp"
+#include <cstdint>
+
+namespace icinga
+{
+
+typedef std::shared_ptr<MYSQL_RES> IdoMysqlResult;
+
+typedef std::function<void (const IdoMysqlResult&)> IdoAsyncCallback;
+
+struct IdoAsyncQuery
+{
+ String Query;
+ IdoAsyncCallback Callback;
+};
+
+/**
+ * An IDO MySQL database connection.
+ *
+ * @ingroup ido
+ */
+class IdoMysqlConnection final : public ObjectImpl<IdoMysqlConnection>
+{
+public:
+ DECLARE_OBJECT(IdoMysqlConnection);
+ DECLARE_OBJECTNAME(IdoMysqlConnection);
+
+ static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata);
+
+ const char * GetLatestSchemaVersion() const noexcept override;
+ const char * GetCompatSchemaVersion() const noexcept override;
+
+ int GetPendingQueryCount() const override;
+
+protected:
+ void OnConfigLoaded() override;
+ void Resume() override;
+ void Pause() override;
+
+ void ActivateObject(const DbObject::Ptr& dbobj) override;
+ void DeactivateObject(const DbObject::Ptr& dbobj) override;
+ void ExecuteQuery(const DbQuery& query) override;
+ void ExecuteMultipleQueries(const std::vector<DbQuery>& queries) override;
+ void CleanUpExecuteQuery(const String& table, const String& time_key, double time_value) override;
+ void FillIDCache(const DbType::Ptr& type) override;
+ void NewTransaction() override;
+ void Disconnect() override;
+
+private:
+ DbReference m_InstanceID;
+
+ Library m_Library;
+ std::unique_ptr<MysqlInterface, MysqlInterfaceDeleter> m_Mysql;
+
+ MYSQL m_Connection;
+ int m_AffectedRows;
+ unsigned int m_MaxPacketSize;
+
+ std::vector<IdoAsyncQuery> m_AsyncQueries;
+ uint_fast32_t m_UncommittedAsyncQueries = 0;
+
+ Timer::Ptr m_ReconnectTimer;
+ Timer::Ptr m_TxTimer;
+
+ IdoMysqlResult Query(const String& query);
+ DbReference GetLastInsertID();
+ int GetAffectedRows();
+ String Escape(const String& s);
+ Dictionary::Ptr FetchRow(const IdoMysqlResult& result);
+ void DiscardRows(const IdoMysqlResult& result);
+
+ void AsyncQuery(const String& query, const IdoAsyncCallback& callback = IdoAsyncCallback());
+ void FinishAsyncQueries();
+
+ bool FieldToEscapedString(const String& key, const Value& value, Value *result);
+ void InternalActivateObject(const DbObject::Ptr& dbobj);
+ void InternalDeactivateObject(const DbObject::Ptr& dbobj);
+
+ void Reconnect();
+
+ void AssertOnWorkQueue();
+
+ void ReconnectTimerHandler();
+
+ bool CanExecuteQuery(const DbQuery& query);
+
+ void InternalExecuteQuery(const DbQuery& query, int typeOverride = -1);
+ void InternalExecuteMultipleQueries(const std::vector<DbQuery>& queries);
+
+ void FinishExecuteQuery(const DbQuery& query, int type, bool upsert);
+ void InternalCleanUpExecuteQuery(const String& table, const String& time_key, double time_value);
+ void InternalNewTransaction();
+
+ void ClearTableBySession(const String& table);
+ void ClearTablesBySession();
+
+ void ExceptionHandler(boost::exception_ptr exp);
+
+ void FinishConnect(double startTime);
+};
+
+}
+
+#endif /* IDOMYSQLCONNECTION_H */
diff --git a/lib/db_ido_mysql/idomysqlconnection.ti b/lib/db_ido_mysql/idomysqlconnection.ti
new file mode 100644
index 0000000..681148f
--- /dev/null
+++ b/lib/db_ido_mysql/idomysqlconnection.ti
@@ -0,0 +1,42 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbconnection.hpp"
+
+library db_ido_mysql;
+
+namespace icinga
+{
+
+class IdoMysqlConnection : DbConnection
+{
+ activation_priority 100;
+
+ [config] String host {
+ default {{{ return "localhost"; }}}
+ };
+ [config] int port {
+ default {{{ return 3306; }}}
+ };
+ [config] String socket_path;
+ [config] String user {
+ default {{{ return "icinga"; }}}
+ };
+ [config, no_user_view, no_user_modify] String password {
+ default {{{ return "icinga"; }}}
+ };
+ [config] String database {
+ default {{{ return "icinga"; }}}
+ };
+ [config] bool enable_ssl;
+ [config] String ssl_key;
+ [config] String ssl_cert;
+ [config] String ssl_ca;
+ [config] String ssl_capath;
+ [config] String ssl_cipher;
+ [config] String instance_name {
+ default {{{ return "default"; }}}
+ };
+ [config] String instance_description;
+};
+
+}
diff --git a/lib/db_ido_mysql/schema/mysql.sql b/lib/db_ido_mysql/schema/mysql.sql
new file mode 100644
index 0000000..020ba3c
--- /dev/null
+++ b/lib/db_ido_mysql/schema/mysql.sql
@@ -0,0 +1,1666 @@
+-- --------------------------------------------------------
+-- mysql.sql
+-- DB definition for IDO MySQL
+--
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- -- --------------------------------------------------------
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8 */;
+
+--
+-- Database: icinga
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_acknowledgements
+--
+
+CREATE TABLE IF NOT EXISTS icinga_acknowledgements (
+ acknowledgement_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ entry_time timestamp NULL,
+ entry_time_usec int default 0,
+ acknowledgement_type smallint default 0,
+ object_id bigint unsigned default 0,
+ state smallint default 0,
+ author_name varchar(64) character set latin1 default '',
+ comment_data TEXT character set latin1,
+ is_sticky smallint default 0,
+ persistent_comment smallint default 0,
+ notify_contacts smallint default 0,
+ end_time timestamp NULL,
+ PRIMARY KEY (acknowledgement_id)
+) ENGINE=InnoDB COMMENT='Current and historical host and service acknowledgements';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_commands
+--
+
+CREATE TABLE IF NOT EXISTS icinga_commands (
+ command_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ object_id bigint unsigned default 0,
+ command_line TEXT character set latin1,
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (command_id),
+ UNIQUE KEY instance_id (instance_id,object_id,config_type)
+) ENGINE=InnoDB COMMENT='Command definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_commenthistory
+--
+
+CREATE TABLE IF NOT EXISTS icinga_commenthistory (
+ commenthistory_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ entry_time timestamp NULL,
+ entry_time_usec int default 0,
+ comment_type smallint default 0,
+ entry_type smallint default 0,
+ object_id bigint unsigned default 0,
+ comment_time timestamp NULL,
+ internal_comment_id bigint unsigned default 0,
+ author_name varchar(64) character set latin1 default '',
+ comment_data TEXT character set latin1,
+ is_persistent smallint default 0,
+ comment_source smallint default 0,
+ expires smallint default 0,
+ expiration_time timestamp NULL,
+ deletion_time timestamp NULL,
+ deletion_time_usec int default 0,
+ name TEXT character set latin1 default NULL,
+ PRIMARY KEY (commenthistory_id)
+) ENGINE=InnoDB COMMENT='Historical host and service comments';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_comments
+--
+
+CREATE TABLE IF NOT EXISTS icinga_comments (
+ comment_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ entry_time timestamp NULL,
+ entry_time_usec int default 0,
+ comment_type smallint default 0,
+ entry_type smallint default 0,
+ object_id bigint unsigned default 0,
+ comment_time timestamp NULL,
+ internal_comment_id bigint unsigned default 0,
+ author_name varchar(64) character set latin1 default '',
+ comment_data TEXT character set latin1,
+ is_persistent smallint default 0,
+ comment_source smallint default 0,
+ expires smallint default 0,
+ expiration_time timestamp NULL,
+ name TEXT character set latin1 default NULL,
+ session_token int default NULL,
+ PRIMARY KEY (comment_id)
+) ENGINE=InnoDB COMMENT='Usercomments on Icinga objects';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_configfiles
+--
+
+CREATE TABLE IF NOT EXISTS icinga_configfiles (
+ configfile_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ configfile_type smallint default 0,
+ configfile_path varchar(255) character set latin1 default '',
+ PRIMARY KEY (configfile_id),
+ UNIQUE KEY instance_id (instance_id,configfile_type,configfile_path)
+) ENGINE=InnoDB COMMENT='Configuration files';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_configfilevariables
+--
+
+CREATE TABLE IF NOT EXISTS icinga_configfilevariables (
+ configfilevariable_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ configfile_id bigint unsigned default 0,
+ varname varchar(64) character set latin1 default '',
+ varvalue TEXT character set latin1,
+ PRIMARY KEY (configfilevariable_id)
+) ENGINE=InnoDB COMMENT='Configuration file variables';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_conninfo
+--
+
+CREATE TABLE IF NOT EXISTS icinga_conninfo (
+ conninfo_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ agent_name varchar(32) character set latin1 default '',
+ agent_version varchar(32) character set latin1 default '',
+ disposition varchar(32) character set latin1 default '',
+ connect_source varchar(32) character set latin1 default '',
+ connect_type varchar(32) character set latin1 default '',
+ connect_time timestamp NULL,
+ disconnect_time timestamp NULL,
+ last_checkin_time timestamp NULL,
+ data_start_time timestamp NULL,
+ data_end_time timestamp NULL,
+ bytes_processed bigint unsigned default '0',
+ lines_processed bigint unsigned default '0',
+ entries_processed bigint unsigned default '0',
+ PRIMARY KEY (conninfo_id)
+) ENGINE=InnoDB COMMENT='IDO2DB daemon connection information';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactgroups
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contactgroups (
+ contactgroup_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ contactgroup_object_id bigint unsigned default 0,
+ alias varchar(255) character set latin1 default '',
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (contactgroup_id),
+ UNIQUE KEY instance_id (instance_id,config_type,contactgroup_object_id)
+) ENGINE=InnoDB COMMENT='Contactgroup definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactgroup_members
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contactgroup_members (
+ contactgroup_member_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ contactgroup_id bigint unsigned default 0,
+ contact_object_id bigint unsigned default 0,
+ PRIMARY KEY (contactgroup_member_id)
+) ENGINE=InnoDB COMMENT='Contactgroup members';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactnotificationmethods
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contactnotificationmethods (
+ contactnotificationmethod_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ contactnotification_id bigint unsigned default 0,
+ start_time timestamp NULL,
+ start_time_usec int default 0,
+ end_time timestamp NULL,
+ end_time_usec int default 0,
+ command_object_id bigint unsigned default 0,
+ command_args TEXT character set latin1,
+ PRIMARY KEY (contactnotificationmethod_id),
+ UNIQUE KEY instance_id (instance_id,contactnotification_id,start_time,start_time_usec)
+) ENGINE=InnoDB COMMENT='Historical record of contact notification methods';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactnotifications
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contactnotifications (
+ contactnotification_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ notification_id bigint unsigned default 0,
+ contact_object_id bigint unsigned default 0,
+ start_time timestamp NULL,
+ start_time_usec int default 0,
+ end_time timestamp NULL,
+ end_time_usec int default 0,
+ PRIMARY KEY (contactnotification_id),
+ UNIQUE KEY instance_id (instance_id,contact_object_id,start_time,start_time_usec)
+) ENGINE=InnoDB COMMENT='Historical record of contact notifications';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contacts
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contacts (
+ contact_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ contact_object_id bigint unsigned default 0,
+ alias varchar(255) character set latin1 default '',
+ email_address varchar(255) character set latin1 default '',
+ pager_address varchar(64) character set latin1 default '',
+ host_timeperiod_object_id bigint unsigned default 0,
+ service_timeperiod_object_id bigint unsigned default 0,
+ host_notifications_enabled smallint default 0,
+ service_notifications_enabled smallint default 0,
+ can_submit_commands smallint default 0,
+ notify_service_recovery smallint default 0,
+ notify_service_warning smallint default 0,
+ notify_service_unknown smallint default 0,
+ notify_service_critical smallint default 0,
+ notify_service_flapping smallint default 0,
+ notify_service_downtime smallint default 0,
+ notify_host_recovery smallint default 0,
+ notify_host_down smallint default 0,
+ notify_host_unreachable smallint default 0,
+ notify_host_flapping smallint default 0,
+ notify_host_downtime smallint default 0,
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (contact_id),
+ UNIQUE KEY instance_id (instance_id,config_type,contact_object_id)
+) ENGINE=InnoDB COMMENT='Contact definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactstatus
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contactstatus (
+ contactstatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ contact_object_id bigint unsigned default 0,
+ status_update_time timestamp NULL,
+ host_notifications_enabled smallint default 0,
+ service_notifications_enabled smallint default 0,
+ last_host_notification timestamp NULL,
+ last_service_notification timestamp NULL,
+ modified_attributes int default 0,
+ modified_host_attributes int default 0,
+ modified_service_attributes int default 0,
+ PRIMARY KEY (contactstatus_id),
+ UNIQUE KEY contact_object_id (contact_object_id)
+) ENGINE=InnoDB COMMENT='Contact status';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contact_addresses
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contact_addresses (
+ contact_address_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ contact_id bigint unsigned default 0,
+ address_number smallint default 0,
+ address varchar(255) character set latin1 default '',
+ PRIMARY KEY (contact_address_id),
+ UNIQUE KEY contact_id (contact_id,address_number)
+) ENGINE=InnoDB COMMENT='Contact addresses';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contact_notificationcommands
+--
+
+CREATE TABLE IF NOT EXISTS icinga_contact_notificationcommands (
+ contact_notificationcommand_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ contact_id bigint unsigned default 0,
+ notification_type smallint default 0,
+ command_object_id bigint unsigned default 0,
+ command_args varchar(255) character set latin1 default '',
+ PRIMARY KEY (contact_notificationcommand_id),
+ UNIQUE KEY contact_id (contact_id,notification_type,command_object_id,command_args)
+) ENGINE=InnoDB COMMENT='Contact host and service notification commands';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_customvariables
+--
+
+CREATE TABLE IF NOT EXISTS icinga_customvariables (
+ customvariable_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ object_id bigint unsigned default 0,
+ config_type smallint default 0,
+ has_been_modified smallint default 0,
+ varname varchar(255) character set latin1 collate latin1_general_cs default NULL,
+ varvalue TEXT character set latin1,
+ is_json smallint default 0,
+ PRIMARY KEY (customvariable_id),
+ UNIQUE KEY object_id_2 (object_id,config_type,varname),
+ KEY varname (varname)
+) ENGINE=InnoDB COMMENT='Custom variables';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_customvariablestatus
+--
+
+CREATE TABLE IF NOT EXISTS icinga_customvariablestatus (
+ customvariablestatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ object_id bigint unsigned default 0,
+ status_update_time timestamp NULL,
+ has_been_modified smallint default 0,
+ varname varchar(255) character set latin1 collate latin1_general_cs default NULL,
+ varvalue TEXT character set latin1,
+ is_json smallint default 0,
+ PRIMARY KEY (customvariablestatus_id),
+ UNIQUE KEY object_id_2 (object_id,varname),
+ KEY varname (varname)
+) ENGINE=InnoDB COMMENT='Custom variable status information';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_dbversion
+--
+
+CREATE TABLE IF NOT EXISTS icinga_dbversion (
+ dbversion_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ name varchar(10) character set latin1 default '',
+ version varchar(10) character set latin1 default '',
+ create_time timestamp NULL,
+ modify_time timestamp NULL,
+ PRIMARY KEY (dbversion_id),
+ UNIQUE KEY dbversion (name)
+) ENGINE=InnoDB;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_downtimehistory
+--
+
+CREATE TABLE IF NOT EXISTS icinga_downtimehistory (
+ downtimehistory_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ downtime_type smallint default 0,
+ object_id bigint unsigned default 0,
+ entry_time timestamp NULL,
+ author_name varchar(64) character set latin1 default '',
+ comment_data TEXT character set latin1,
+ internal_downtime_id bigint unsigned default 0,
+ triggered_by_id bigint unsigned default 0,
+ is_fixed smallint default 0,
+ duration bigint(20) default 0,
+ scheduled_start_time timestamp NULL,
+ scheduled_end_time timestamp NULL,
+ was_started smallint default 0,
+ actual_start_time timestamp NULL,
+ actual_start_time_usec int default 0,
+ actual_end_time timestamp NULL,
+ actual_end_time_usec int default 0,
+ was_cancelled smallint default 0,
+ is_in_effect smallint default 0,
+ trigger_time timestamp NULL,
+ name TEXT character set latin1 default NULL,
+ PRIMARY KEY (downtimehistory_id)
+) ENGINE=InnoDB COMMENT='Historical scheduled host and service downtime';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_eventhandlers
+--
+
+CREATE TABLE IF NOT EXISTS icinga_eventhandlers (
+ eventhandler_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ eventhandler_type smallint default 0,
+ object_id bigint unsigned default 0,
+ state smallint default 0,
+ state_type smallint default 0,
+ start_time timestamp NULL,
+ start_time_usec int default 0,
+ end_time timestamp NULL,
+ end_time_usec int default 0,
+ command_object_id bigint unsigned default 0,
+ command_args TEXT character set latin1,
+ command_line TEXT character set latin1,
+ timeout smallint default 0,
+ early_timeout smallint default 0,
+ execution_time double default '0',
+ return_code smallint default 0,
+ output TEXT character set latin1,
+ long_output TEXT,
+ PRIMARY KEY (eventhandler_id),
+ UNIQUE KEY instance_id (instance_id,object_id,start_time,start_time_usec)
+) ENGINE=InnoDB COMMENT='Historical host and service event handlers';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_externalcommands
+--
+
+CREATE TABLE IF NOT EXISTS icinga_externalcommands (
+ externalcommand_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ entry_time timestamp NULL,
+ command_type smallint default 0,
+ command_name varchar(128) character set latin1 default '',
+ command_args TEXT character set latin1,
+ PRIMARY KEY (externalcommand_id)
+) ENGINE=InnoDB COMMENT='Historical record of processed external commands';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_flappinghistory
+--
+
+CREATE TABLE IF NOT EXISTS icinga_flappinghistory (
+ flappinghistory_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ event_time timestamp NULL,
+ event_time_usec int default 0,
+ event_type smallint default 0,
+ reason_type smallint default 0,
+ flapping_type smallint default 0,
+ object_id bigint unsigned default 0,
+ percent_state_change double default '0',
+ low_threshold double default '0',
+ high_threshold double default '0',
+ comment_time timestamp NULL,
+ internal_comment_id bigint unsigned default 0,
+ PRIMARY KEY (flappinghistory_id)
+) ENGINE=InnoDB COMMENT='Current and historical record of host and service flapping';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostchecks
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hostchecks (
+ hostcheck_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ host_object_id bigint unsigned default 0,
+ check_type smallint default 0,
+ is_raw_check smallint default 0,
+ current_check_attempt smallint default 0,
+ max_check_attempts smallint default 0,
+ state smallint default 0,
+ state_type smallint default 0,
+ start_time timestamp NULL,
+ start_time_usec int default 0,
+ end_time timestamp NULL,
+ end_time_usec int default 0,
+ command_object_id bigint unsigned default 0,
+ command_args TEXT character set latin1,
+ command_line TEXT character set latin1,
+ timeout smallint default 0,
+ early_timeout smallint default 0,
+ execution_time double default '0',
+ latency double default '0',
+ return_code smallint default 0,
+ output TEXT character set latin1,
+ long_output TEXT,
+ perfdata TEXT character set latin1,
+ PRIMARY KEY (hostcheck_id)
+) ENGINE=InnoDB COMMENT='Historical host checks';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostdependencies
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hostdependencies (
+ hostdependency_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ host_object_id bigint unsigned default 0,
+ dependent_host_object_id bigint unsigned default 0,
+ dependency_type smallint default 0,
+ inherits_parent smallint default 0,
+ timeperiod_object_id bigint unsigned default 0,
+ fail_on_up smallint default 0,
+ fail_on_down smallint default 0,
+ fail_on_unreachable smallint default 0,
+ PRIMARY KEY (hostdependency_id),
+ KEY instance_id (instance_id,config_type,host_object_id,dependent_host_object_id,dependency_type,inherits_parent,fail_on_up,fail_on_down,fail_on_unreachable)
+) ENGINE=InnoDB COMMENT='Host dependency definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostescalations
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hostescalations (
+ hostescalation_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ host_object_id bigint unsigned default 0,
+ timeperiod_object_id bigint unsigned default 0,
+ first_notification smallint default 0,
+ last_notification smallint default 0,
+ notification_interval double default '0',
+ escalate_on_recovery smallint default 0,
+ escalate_on_down smallint default 0,
+ escalate_on_unreachable smallint default 0,
+ PRIMARY KEY (hostescalation_id),
+ UNIQUE KEY instance_id (instance_id,config_type,host_object_id,timeperiod_object_id,first_notification,last_notification)
+) ENGINE=InnoDB COMMENT='Host escalation definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostescalation_contactgroups
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hostescalation_contactgroups (
+ hostescalation_contactgroup_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ hostescalation_id bigint unsigned default 0,
+ contactgroup_object_id bigint unsigned default 0,
+ PRIMARY KEY (hostescalation_contactgroup_id),
+ UNIQUE KEY instance_id (hostescalation_id,contactgroup_object_id)
+) ENGINE=InnoDB COMMENT='Host escalation contact groups';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostescalation_contacts
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hostescalation_contacts (
+ hostescalation_contact_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ hostescalation_id bigint unsigned default 0,
+ contact_object_id bigint unsigned default 0,
+ PRIMARY KEY (hostescalation_contact_id),
+ UNIQUE KEY instance_id (instance_id,hostescalation_id,contact_object_id)
+) ENGINE=InnoDB COMMENT='Host escalation contacts';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostgroups
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hostgroups (
+ hostgroup_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ hostgroup_object_id bigint unsigned default 0,
+ alias varchar(255) character set latin1 default '',
+ notes TEXT character set latin1 default NULL,
+ notes_url TEXT character set latin1 default NULL,
+ action_url TEXT character set latin1 default NULL,
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (hostgroup_id),
+ UNIQUE KEY instance_id (instance_id,hostgroup_object_id)
+) ENGINE=InnoDB COMMENT='Hostgroup definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostgroup_members
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hostgroup_members (
+ hostgroup_member_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ hostgroup_id bigint unsigned default 0,
+ host_object_id bigint unsigned default 0,
+ PRIMARY KEY (hostgroup_member_id)
+) ENGINE=InnoDB COMMENT='Hostgroup members';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hosts
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hosts (
+ host_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ host_object_id bigint unsigned default 0,
+ alias varchar(255) character set latin1 default '',
+ display_name varchar(255) character set latin1 collate latin1_general_cs default '',
+ address varchar(128) character set latin1 default '',
+ address6 varchar(128) character set latin1 default '',
+ check_command_object_id bigint unsigned default 0,
+ check_command_args TEXT character set latin1,
+ eventhandler_command_object_id bigint unsigned default 0,
+ eventhandler_command_args TEXT character set latin1,
+ notification_timeperiod_object_id bigint unsigned default 0,
+ check_timeperiod_object_id bigint unsigned default 0,
+ failure_prediction_options varchar(128) character set latin1 default '',
+ check_interval double default '0',
+ retry_interval double default '0',
+ max_check_attempts smallint default 0,
+ first_notification_delay double default '0',
+ notification_interval double default '0',
+ notify_on_down smallint default 0,
+ notify_on_unreachable smallint default 0,
+ notify_on_recovery smallint default 0,
+ notify_on_flapping smallint default 0,
+ notify_on_downtime smallint default 0,
+ stalk_on_up smallint default 0,
+ stalk_on_down smallint default 0,
+ stalk_on_unreachable smallint default 0,
+ flap_detection_enabled smallint default 0,
+ flap_detection_on_up smallint default 0,
+ flap_detection_on_down smallint default 0,
+ flap_detection_on_unreachable smallint default 0,
+ low_flap_threshold double default '0',
+ high_flap_threshold double default '0',
+ process_performance_data smallint default 0,
+ freshness_checks_enabled smallint default 0,
+ freshness_threshold int default 0,
+ passive_checks_enabled smallint default 0,
+ event_handler_enabled smallint default 0,
+ active_checks_enabled smallint default 0,
+ retain_status_information smallint default 0,
+ retain_nonstatus_information smallint default 0,
+ notifications_enabled smallint default 0,
+ obsess_over_host smallint default 0,
+ failure_prediction_enabled smallint default 0,
+ notes TEXT character set latin1,
+ notes_url TEXT character set latin1,
+ action_url TEXT character set latin1,
+ icon_image TEXT character set latin1,
+ icon_image_alt TEXT character set latin1,
+ vrml_image TEXT character set latin1,
+ statusmap_image TEXT character set latin1,
+ have_2d_coords smallint default 0,
+ x_2d smallint default 0,
+ y_2d smallint default 0,
+ have_3d_coords smallint default 0,
+ x_3d double default '0',
+ y_3d double default '0',
+ z_3d double default '0',
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (host_id),
+ UNIQUE KEY instance_id (instance_id,config_type,host_object_id)
+) ENGINE=InnoDB COMMENT='Host definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hoststatus
+--
+
+CREATE TABLE IF NOT EXISTS icinga_hoststatus (
+ hoststatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ host_object_id bigint unsigned default 0,
+ status_update_time timestamp NULL,
+ output TEXT character set latin1,
+ long_output TEXT,
+ perfdata TEXT character set latin1,
+ check_source varchar(255) character set latin1 default '',
+ current_state smallint default 0,
+ has_been_checked smallint default 0,
+ should_be_scheduled smallint default 0,
+ current_check_attempt smallint default 0,
+ max_check_attempts smallint default 0,
+ last_check timestamp NULL,
+ next_check timestamp NULL,
+ check_type smallint default 0,
+ last_state_change timestamp NULL,
+ last_hard_state_change timestamp NULL,
+ last_hard_state smallint default 0,
+ last_time_up timestamp NULL,
+ last_time_down timestamp NULL,
+ last_time_unreachable timestamp NULL,
+ state_type smallint default 0,
+ last_notification timestamp NULL,
+ next_notification timestamp NULL,
+ no_more_notifications smallint default 0,
+ notifications_enabled smallint default 0,
+ problem_has_been_acknowledged smallint default 0,
+ acknowledgement_type smallint default 0,
+ current_notification_number int unsigned default 0,
+ passive_checks_enabled smallint default 0,
+ active_checks_enabled smallint default 0,
+ event_handler_enabled smallint default 0,
+ flap_detection_enabled smallint default 0,
+ is_flapping smallint default 0,
+ percent_state_change double default '0',
+ latency double default '0',
+ execution_time double default '0',
+ scheduled_downtime_depth smallint default 0,
+ failure_prediction_enabled smallint default 0,
+ process_performance_data smallint default 0,
+ obsess_over_host smallint default 0,
+ modified_host_attributes int default 0,
+ original_attributes TEXT character set latin1 default NULL,
+ event_handler TEXT character set latin1,
+ check_command TEXT character set latin1,
+ normal_check_interval double default '0',
+ retry_check_interval double default '0',
+ check_timeperiod_object_id bigint unsigned default 0,
+ is_reachable smallint default 0,
+ PRIMARY KEY (hoststatus_id),
+ UNIQUE KEY object_id (host_object_id)
+) ENGINE=InnoDB COMMENT='Current host status information';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_host_contactgroups
+--
+
+CREATE TABLE IF NOT EXISTS icinga_host_contactgroups (
+ host_contactgroup_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ host_id bigint unsigned default 0,
+ contactgroup_object_id bigint unsigned default 0,
+ PRIMARY KEY (host_contactgroup_id)
+) ENGINE=InnoDB COMMENT='Host contact groups';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_host_contacts
+--
+
+CREATE TABLE IF NOT EXISTS icinga_host_contacts (
+ host_contact_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ host_id bigint unsigned default 0,
+ contact_object_id bigint unsigned default 0,
+ PRIMARY KEY (host_contact_id)
+) ENGINE=InnoDB COMMENT='Host contacts';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_host_parenthosts
+--
+
+CREATE TABLE IF NOT EXISTS icinga_host_parenthosts (
+ host_parenthost_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ host_id bigint unsigned default 0,
+ parent_host_object_id bigint unsigned default 0,
+ PRIMARY KEY (host_parenthost_id)
+) ENGINE=InnoDB COMMENT='Parent hosts';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_instances
+--
+
+CREATE TABLE IF NOT EXISTS icinga_instances (
+ instance_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_name varchar(64) character set latin1 default '',
+ instance_description varchar(128) character set latin1 default '',
+ PRIMARY KEY (instance_id)
+) ENGINE=InnoDB COMMENT='Location names of various Icinga installations';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_logentries
+--
+
+CREATE TABLE IF NOT EXISTS icinga_logentries (
+ logentry_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ logentry_time timestamp NULL,
+ entry_time timestamp NULL,
+ entry_time_usec int default 0,
+ logentry_type int default 0,
+ logentry_data TEXT character set latin1,
+ realtime_data smallint default 0,
+ inferred_data_extracted smallint default 0,
+ object_id bigint unsigned default NULL,
+ PRIMARY KEY (logentry_id)
+) ENGINE=InnoDB COMMENT='Historical record of log entries';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_notifications
+--
+
+CREATE TABLE IF NOT EXISTS icinga_notifications (
+ notification_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ notification_type smallint default 0,
+ notification_reason smallint default 0,
+ object_id bigint unsigned default 0,
+ start_time timestamp NULL,
+ start_time_usec int default 0,
+ end_time timestamp NULL,
+ end_time_usec int default 0,
+ state smallint default 0,
+ output TEXT character set latin1,
+ long_output TEXT,
+ escalated smallint default 0,
+ contacts_notified smallint default 0,
+ PRIMARY KEY (notification_id),
+ UNIQUE KEY instance_id (instance_id,object_id,start_time,start_time_usec)
+) ENGINE=InnoDB COMMENT='Historical record of host and service notifications';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_objects
+--
+
+CREATE TABLE IF NOT EXISTS icinga_objects (
+ object_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ objecttype_id bigint unsigned default 0,
+ name1 varchar(255) character set latin1 collate latin1_general_cs default '',
+ name2 varchar(255) character set latin1 collate latin1_general_cs default NULL,
+ is_active smallint default 0,
+ PRIMARY KEY (object_id),
+ KEY objecttype_id (objecttype_id,name1,name2)
+) ENGINE=InnoDB COMMENT='Current and historical objects of all kinds';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_processevents
+--
+
+CREATE TABLE IF NOT EXISTS icinga_processevents (
+ processevent_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ event_type smallint default 0,
+ event_time timestamp NULL,
+ event_time_usec int default 0,
+ process_id bigint unsigned default 0,
+ program_name varchar(16) character set latin1 default '',
+ program_version varchar(20) character set latin1 default '',
+ program_date varchar(10) character set latin1 default '',
+ PRIMARY KEY (processevent_id)
+) ENGINE=InnoDB COMMENT='Historical Icinga process events';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_programstatus
+--
+
+CREATE TABLE IF NOT EXISTS icinga_programstatus (
+ programstatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ program_version varchar(64) character set latin1 collate latin1_general_cs default NULL,
+ status_update_time timestamp NULL,
+ program_start_time timestamp NULL,
+ program_end_time timestamp NULL,
+ endpoint_name varchar(255) character set latin1 collate latin1_general_cs default NULL,
+ is_currently_running smallint default 0,
+ process_id bigint unsigned default 0,
+ daemon_mode smallint default 0,
+ last_command_check timestamp NULL,
+ last_log_rotation timestamp NULL,
+ notifications_enabled smallint default 0,
+ disable_notif_expire_time timestamp NULL,
+ active_service_checks_enabled smallint default 0,
+ passive_service_checks_enabled smallint default 0,
+ active_host_checks_enabled smallint default 0,
+ passive_host_checks_enabled smallint default 0,
+ event_handlers_enabled smallint default 0,
+ flap_detection_enabled smallint default 0,
+ failure_prediction_enabled smallint default 0,
+ process_performance_data smallint default 0,
+ obsess_over_hosts smallint default 0,
+ obsess_over_services smallint default 0,
+ modified_host_attributes int default 0,
+ modified_service_attributes int default 0,
+ global_host_event_handler TEXT character set latin1,
+ global_service_event_handler TEXT character set latin1,
+ config_dump_in_progress smallint default 0,
+ PRIMARY KEY (programstatus_id),
+ UNIQUE KEY instance_id (instance_id)
+) ENGINE=InnoDB COMMENT='Current program status information';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_runtimevariables
+--
+
+CREATE TABLE IF NOT EXISTS icinga_runtimevariables (
+ runtimevariable_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ varname varchar(64) character set latin1 default '',
+ varvalue TEXT character set latin1,
+ PRIMARY KEY (runtimevariable_id)
+) ENGINE=InnoDB COMMENT='Runtime variables from the Icinga daemon';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_scheduleddowntime
+--
+
+CREATE TABLE IF NOT EXISTS icinga_scheduleddowntime (
+ scheduleddowntime_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ downtime_type smallint default 0,
+ object_id bigint unsigned default 0,
+ entry_time timestamp NULL,
+ author_name varchar(64) character set latin1 default '',
+ comment_data TEXT character set latin1,
+ internal_downtime_id bigint unsigned default 0,
+ triggered_by_id bigint unsigned default 0,
+ is_fixed smallint default 0,
+ duration bigint(20) default 0,
+ scheduled_start_time timestamp NULL,
+ scheduled_end_time timestamp NULL,
+ was_started smallint default 0,
+ actual_start_time timestamp NULL,
+ actual_start_time_usec int default 0,
+ is_in_effect smallint default 0,
+ trigger_time timestamp NULL,
+ name TEXT character set latin1 default NULL,
+ session_token int default NULL,
+ PRIMARY KEY (scheduleddowntime_id)
+) ENGINE=InnoDB COMMENT='Current scheduled host and service downtime';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicechecks
+--
+
+CREATE TABLE IF NOT EXISTS icinga_servicechecks (
+ servicecheck_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ service_object_id bigint unsigned default 0,
+ check_type smallint default 0,
+ current_check_attempt smallint default 0,
+ max_check_attempts smallint default 0,
+ state smallint default 0,
+ state_type smallint default 0,
+ start_time timestamp NULL,
+ start_time_usec int default 0,
+ end_time timestamp NULL,
+ end_time_usec int default 0,
+ command_object_id bigint unsigned default 0,
+ command_args TEXT character set latin1,
+ command_line TEXT character set latin1,
+ timeout smallint default 0,
+ early_timeout smallint default 0,
+ execution_time double default '0',
+ latency double default '0',
+ return_code smallint default 0,
+ output TEXT character set latin1,
+ long_output TEXT,
+ perfdata TEXT character set latin1,
+ PRIMARY KEY (servicecheck_id)
+) ENGINE=InnoDB COMMENT='Historical service checks';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicedependencies
+--
+
+CREATE TABLE IF NOT EXISTS icinga_servicedependencies (
+ servicedependency_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ service_object_id bigint unsigned default 0,
+ dependent_service_object_id bigint unsigned default 0,
+ dependency_type smallint default 0,
+ inherits_parent smallint default 0,
+ timeperiod_object_id bigint unsigned default 0,
+ fail_on_ok smallint default 0,
+ fail_on_warning smallint default 0,
+ fail_on_unknown smallint default 0,
+ fail_on_critical smallint default 0,
+ PRIMARY KEY (servicedependency_id),
+ KEY instance_id (instance_id,config_type,service_object_id,dependent_service_object_id,dependency_type,inherits_parent,fail_on_ok,fail_on_warning,fail_on_unknown,fail_on_critical)
+) ENGINE=InnoDB COMMENT='Service dependency definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_serviceescalations
+--
+
+CREATE TABLE IF NOT EXISTS icinga_serviceescalations (
+ serviceescalation_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ service_object_id bigint unsigned default 0,
+ timeperiod_object_id bigint unsigned default 0,
+ first_notification smallint default 0,
+ last_notification smallint default 0,
+ notification_interval double default '0',
+ escalate_on_recovery smallint default 0,
+ escalate_on_warning smallint default 0,
+ escalate_on_unknown smallint default 0,
+ escalate_on_critical smallint default 0,
+ PRIMARY KEY (serviceescalation_id),
+ UNIQUE KEY instance_id (instance_id,config_type,service_object_id,timeperiod_object_id,first_notification,last_notification)
+) ENGINE=InnoDB COMMENT='Service escalation definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_serviceescalation_contactgroups
+--
+
+CREATE TABLE IF NOT EXISTS icinga_serviceescalation_contactgroups (
+ serviceescalation_contactgroup_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ serviceescalation_id bigint unsigned default 0,
+ contactgroup_object_id bigint unsigned default 0,
+ PRIMARY KEY (serviceescalation_contactgroup_id),
+ UNIQUE KEY instance_id (serviceescalation_id,contactgroup_object_id)
+) ENGINE=InnoDB COMMENT='Service escalation contact groups';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_serviceescalation_contacts
+--
+
+CREATE TABLE IF NOT EXISTS icinga_serviceescalation_contacts (
+ serviceescalation_contact_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ serviceescalation_id bigint unsigned default 0,
+ contact_object_id bigint unsigned default 0,
+ PRIMARY KEY (serviceescalation_contact_id),
+ UNIQUE KEY instance_id (instance_id,serviceescalation_id,contact_object_id)
+) ENGINE=InnoDB COMMENT='Service escalation contacts';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicegroups
+--
+
+CREATE TABLE IF NOT EXISTS icinga_servicegroups (
+ servicegroup_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ servicegroup_object_id bigint unsigned default 0,
+ alias varchar(255) character set latin1 default '',
+ notes TEXT character set latin1 default NULL,
+ notes_url TEXT character set latin1 default NULL,
+ action_url TEXT character set latin1 default NULL,
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (servicegroup_id),
+ UNIQUE KEY instance_id (instance_id,config_type,servicegroup_object_id)
+) ENGINE=InnoDB COMMENT='Servicegroup definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicegroup_members
+--
+
+CREATE TABLE IF NOT EXISTS icinga_servicegroup_members (
+ servicegroup_member_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ servicegroup_id bigint unsigned default 0,
+ service_object_id bigint unsigned default 0,
+ PRIMARY KEY (servicegroup_member_id)
+) ENGINE=InnoDB COMMENT='Servicegroup members';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_services
+--
+
+CREATE TABLE IF NOT EXISTS icinga_services (
+ service_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ host_object_id bigint unsigned default 0,
+ service_object_id bigint unsigned default 0,
+ display_name varchar(255) character set latin1 collate latin1_general_cs default '',
+ check_command_object_id bigint unsigned default 0,
+ check_command_args TEXT character set latin1,
+ eventhandler_command_object_id bigint unsigned default 0,
+ eventhandler_command_args TEXT character set latin1,
+ notification_timeperiod_object_id bigint unsigned default 0,
+ check_timeperiod_object_id bigint unsigned default 0,
+ failure_prediction_options varchar(64) character set latin1 default '',
+ check_interval double default '0',
+ retry_interval double default '0',
+ max_check_attempts smallint default 0,
+ first_notification_delay double default '0',
+ notification_interval double default '0',
+ notify_on_warning smallint default 0,
+ notify_on_unknown smallint default 0,
+ notify_on_critical smallint default 0,
+ notify_on_recovery smallint default 0,
+ notify_on_flapping smallint default 0,
+ notify_on_downtime smallint default 0,
+ stalk_on_ok smallint default 0,
+ stalk_on_warning smallint default 0,
+ stalk_on_unknown smallint default 0,
+ stalk_on_critical smallint default 0,
+ is_volatile smallint default 0,
+ flap_detection_enabled smallint default 0,
+ flap_detection_on_ok smallint default 0,
+ flap_detection_on_warning smallint default 0,
+ flap_detection_on_unknown smallint default 0,
+ flap_detection_on_critical smallint default 0,
+ low_flap_threshold double default '0',
+ high_flap_threshold double default '0',
+ process_performance_data smallint default 0,
+ freshness_checks_enabled smallint default 0,
+ freshness_threshold int default 0,
+ passive_checks_enabled smallint default 0,
+ event_handler_enabled smallint default 0,
+ active_checks_enabled smallint default 0,
+ retain_status_information smallint default 0,
+ retain_nonstatus_information smallint default 0,
+ notifications_enabled smallint default 0,
+ obsess_over_service smallint default 0,
+ failure_prediction_enabled smallint default 0,
+ notes TEXT character set latin1,
+ notes_url TEXT character set latin1,
+ action_url TEXT character set latin1,
+ icon_image TEXT character set latin1,
+ icon_image_alt TEXT character set latin1,
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (service_id),
+ UNIQUE KEY instance_id (instance_id,config_type,service_object_id)
+) ENGINE=InnoDB COMMENT='Service definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicestatus
+--
+
+CREATE TABLE IF NOT EXISTS icinga_servicestatus (
+ servicestatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ service_object_id bigint unsigned default 0,
+ status_update_time timestamp NULL,
+ output TEXT character set latin1,
+ long_output TEXT,
+ perfdata TEXT character set latin1,
+ check_source varchar(255) character set latin1 default '',
+ current_state smallint default 0,
+ has_been_checked smallint default 0,
+ should_be_scheduled smallint default 0,
+ current_check_attempt smallint default 0,
+ max_check_attempts smallint default 0,
+ last_check timestamp NULL,
+ next_check timestamp NULL,
+ check_type smallint default 0,
+ last_state_change timestamp NULL,
+ last_hard_state_change timestamp NULL,
+ last_hard_state smallint default 0,
+ last_time_ok timestamp NULL,
+ last_time_warning timestamp NULL,
+ last_time_unknown timestamp NULL,
+ last_time_critical timestamp NULL,
+ state_type smallint default 0,
+ last_notification timestamp NULL,
+ next_notification timestamp NULL,
+ no_more_notifications smallint default 0,
+ notifications_enabled smallint default 0,
+ problem_has_been_acknowledged smallint default 0,
+ acknowledgement_type smallint default 0,
+ current_notification_number int unsigned default 0,
+ passive_checks_enabled smallint default 0,
+ active_checks_enabled smallint default 0,
+ event_handler_enabled smallint default 0,
+ flap_detection_enabled smallint default 0,
+ is_flapping smallint default 0,
+ percent_state_change double default '0',
+ latency double default '0',
+ execution_time double default '0',
+ scheduled_downtime_depth smallint default 0,
+ failure_prediction_enabled smallint default 0,
+ process_performance_data smallint default 0,
+ obsess_over_service smallint default 0,
+ modified_service_attributes int default 0,
+ original_attributes TEXT character set latin1 default NULL,
+ event_handler TEXT character set latin1,
+ check_command TEXT character set latin1,
+ normal_check_interval double default '0',
+ retry_check_interval double default '0',
+ check_timeperiod_object_id bigint unsigned default 0,
+ is_reachable smallint default 0,
+ PRIMARY KEY (servicestatus_id),
+ UNIQUE KEY object_id (service_object_id)
+) ENGINE=InnoDB COMMENT='Current service status information';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_service_contactgroups
+--
+
+CREATE TABLE IF NOT EXISTS icinga_service_contactgroups (
+ service_contactgroup_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ service_id bigint unsigned default 0,
+ contactgroup_object_id bigint unsigned default 0,
+ PRIMARY KEY (service_contactgroup_id)
+) ENGINE=InnoDB COMMENT='Service contact groups';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_service_contacts
+--
+
+CREATE TABLE IF NOT EXISTS icinga_service_contacts (
+ service_contact_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ service_id bigint unsigned default 0,
+ contact_object_id bigint unsigned default 0,
+ PRIMARY KEY (service_contact_id)
+) ENGINE=InnoDB COMMENT='Service contacts';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_statehistory
+--
+
+CREATE TABLE IF NOT EXISTS icinga_statehistory (
+ statehistory_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ state_time timestamp NULL,
+ state_time_usec int default 0,
+ object_id bigint unsigned default 0,
+ state_change smallint default 0,
+ state smallint default 0,
+ state_type smallint default 0,
+ current_check_attempt smallint default 0,
+ max_check_attempts smallint default 0,
+ last_state smallint default 0,
+ last_hard_state smallint default 0,
+ output TEXT character set latin1,
+ long_output TEXT,
+ check_source varchar(255) character set latin1 default NULL,
+ PRIMARY KEY (statehistory_id)
+) ENGINE=InnoDB COMMENT='Historical host and service state changes';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_systemcommands
+--
+
+CREATE TABLE IF NOT EXISTS icinga_systemcommands (
+ systemcommand_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ start_time timestamp NULL,
+ start_time_usec int default 0,
+ end_time timestamp NULL,
+ end_time_usec int default 0,
+ command_line TEXT character set latin1,
+ timeout smallint default 0,
+ early_timeout smallint default 0,
+ execution_time double default '0',
+ return_code smallint default 0,
+ output TEXT character set latin1,
+ long_output TEXT,
+ PRIMARY KEY (systemcommand_id),
+ UNIQUE KEY instance_id (instance_id,start_time,start_time_usec)
+) ENGINE=InnoDB COMMENT='Historical system commands that are executed';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_timeperiods
+--
+
+CREATE TABLE IF NOT EXISTS icinga_timeperiods (
+ timeperiod_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ config_type smallint default 0,
+ timeperiod_object_id bigint unsigned default 0,
+ alias varchar(255) character set latin1 default '',
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (timeperiod_id),
+ UNIQUE KEY instance_id (instance_id,config_type,timeperiod_object_id)
+) ENGINE=InnoDB COMMENT='Timeperiod definitions';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_timeperiod_timeranges
+--
+
+CREATE TABLE IF NOT EXISTS icinga_timeperiod_timeranges (
+ timeperiod_timerange_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ timeperiod_id bigint unsigned default 0,
+ day smallint default 0,
+ start_sec int default 0,
+ end_sec int default 0,
+ PRIMARY KEY (timeperiod_timerange_id)
+) ENGINE=InnoDB COMMENT='Timeperiod definitions';
+
+
+-- --------------------------------------------------------
+-- Icinga 2 specific schema extensions
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_endpoints
+--
+
+CREATE TABLE IF NOT EXISTS icinga_endpoints (
+ endpoint_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ endpoint_object_id bigint(20) unsigned DEFAULT '0',
+ zone_object_id bigint(20) unsigned DEFAULT '0',
+ config_type smallint(6) DEFAULT '0',
+ identity varchar(255) DEFAULT NULL,
+ node varchar(255) DEFAULT NULL,
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (endpoint_id)
+) ENGINE=InnoDB COMMENT='Endpoint configuration';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_endpointstatus
+--
+
+CREATE TABLE IF NOT EXISTS icinga_endpointstatus (
+ endpointstatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ endpoint_object_id bigint(20) unsigned DEFAULT '0',
+ zone_object_id bigint(20) unsigned DEFAULT '0',
+ status_update_time timestamp NULL,
+ identity varchar(255) DEFAULT NULL,
+ node varchar(255) DEFAULT NULL,
+ is_connected smallint(6),
+ PRIMARY KEY (endpointstatus_id)
+) ENGINE=InnoDB COMMENT='Endpoint status';
+
+--
+-- Table structure for table icinga_zones
+--
+
+CREATE TABLE IF NOT EXISTS icinga_zones (
+ zone_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ zone_object_id bigint(20) unsigned DEFAULT '0',
+ config_type smallint(6) DEFAULT '0',
+ parent_zone_object_id bigint(20) unsigned DEFAULT '0',
+ is_global smallint(6),
+ config_hash varchar(64) DEFAULT NULL,
+ PRIMARY KEY (zone_id)
+) ENGINE=InnoDB COMMENT='Zone configuration';
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_zonestatus
+--
+
+CREATE TABLE IF NOT EXISTS icinga_zonestatus (
+ zonestatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ zone_object_id bigint(20) unsigned DEFAULT '0',
+ status_update_time timestamp NULL,
+ parent_zone_object_id bigint(20) unsigned DEFAULT '0',
+ PRIMARY KEY (zonestatus_id)
+) ENGINE=InnoDB COMMENT='Zone status';
+
+
+
+
+ALTER TABLE icinga_servicestatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_hoststatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_contactstatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_programstatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_comments ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_scheduleddowntime ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_runtimevariables ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_customvariablestatus ADD COLUMN endpoint_object_id bigint default NULL;
+
+ALTER TABLE icinga_acknowledgements ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_commenthistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_contactnotifications ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_downtimehistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_eventhandlers ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_externalcommands ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_flappinghistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_hostchecks ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_logentries ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_notifications ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_processevents ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_servicechecks ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_statehistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_systemcommands ADD COLUMN endpoint_object_id bigint default NULL;
+
+-- -----------------------------------------
+-- add index (delete)
+-- -----------------------------------------
+
+-- for periodic delete
+-- instance_id and
+-- SYSTEMCOMMANDS, SERVICECHECKS, HOSTCHECKS, EVENTHANDLERS => start_time
+-- EXTERNALCOMMANDS => entry_time
+
+-- instance_id
+CREATE INDEX servicechecks_i_id_idx on icinga_servicechecks(instance_id);
+CREATE INDEX hostchecks_i_id_idx on icinga_hostchecks(instance_id);
+CREATE INDEX externalcommands_i_id_idx on icinga_externalcommands(instance_id);
+
+-- time
+CREATE INDEX systemcommands_time_id_idx on icinga_systemcommands(start_time);
+CREATE INDEX servicechecks_time_id_idx on icinga_servicechecks(start_time);
+CREATE INDEX hostchecks_time_id_idx on icinga_hostchecks(start_time);
+CREATE INDEX eventhandlers_time_id_idx on icinga_eventhandlers(start_time);
+CREATE INDEX externalcommands_time_id_idx on icinga_externalcommands(entry_time);
+
+
+-- for starting cleanup - referenced in dbhandler.c:882
+-- instance_id only
+
+-- realtime data
+CREATE INDEX hoststatus_i_id_idx on icinga_hoststatus(instance_id);
+CREATE INDEX servicestatus_i_id_idx on icinga_servicestatus(instance_id);
+CREATE INDEX contactstatus_i_id_idx on icinga_contactstatus(instance_id);
+CREATE INDEX customvariablestatus_i_id_idx on icinga_customvariablestatus(instance_id);
+
+-- config data
+CREATE INDEX configfilevariables_i_id_idx on icinga_configfilevariables(instance_id);
+CREATE INDEX customvariables_i_id_idx on icinga_customvariables(instance_id);
+CREATE INDEX timeperiod_timeranges_i_id_idx on icinga_timeperiod_timeranges(instance_id);
+CREATE INDEX contactgroup_members_i_id_idx on icinga_contactgroup_members(instance_id);
+CREATE INDEX hostgroup_members_i_id_idx on icinga_hostgroup_members(instance_id);
+CREATE INDEX servicegroup_members_i_id_idx on icinga_servicegroup_members(instance_id);
+CREATE INDEX contact_addresses_i_id_idx on icinga_contact_addresses(instance_id);
+CREATE INDEX contact_notifcommands_i_id_idx on icinga_contact_notificationcommands(instance_id);
+CREATE INDEX host_parenthosts_i_id_idx on icinga_host_parenthosts(instance_id);
+CREATE INDEX host_contacts_i_id_idx on icinga_host_contacts(instance_id);
+CREATE INDEX service_contacts_i_id_idx on icinga_service_contacts(instance_id);
+CREATE INDEX service_contactgroups_i_id_idx on icinga_service_contactgroups(instance_id);
+CREATE INDEX host_contactgroups_i_id_idx on icinga_host_contactgroups(instance_id);
+CREATE INDEX hostesc_cgroups_i_id_idx on icinga_hostescalation_contactgroups(instance_id);
+CREATE INDEX serviceesc_cgroups_i_id_idx on icinga_serviceescalation_contactgroups(instance_id);
+
+-- -----------------------------------------
+-- more index stuff (WHERE clauses)
+-- -----------------------------------------
+
+-- hosts
+CREATE INDEX hosts_host_object_id_idx on icinga_hosts(host_object_id);
+
+-- hoststatus
+CREATE INDEX hoststatus_stat_upd_time_idx on icinga_hoststatus(status_update_time);
+CREATE INDEX hoststatus_current_state_idx on icinga_hoststatus(current_state);
+CREATE INDEX hoststatus_check_type_idx on icinga_hoststatus(check_type);
+CREATE INDEX hoststatus_state_type_idx on icinga_hoststatus(state_type);
+CREATE INDEX hoststatus_last_state_chg_idx on icinga_hoststatus(last_state_change);
+CREATE INDEX hoststatus_notif_enabled_idx on icinga_hoststatus(notifications_enabled);
+CREATE INDEX hoststatus_problem_ack_idx on icinga_hoststatus(problem_has_been_acknowledged);
+CREATE INDEX hoststatus_act_chks_en_idx on icinga_hoststatus(active_checks_enabled);
+CREATE INDEX hoststatus_pas_chks_en_idx on icinga_hoststatus(passive_checks_enabled);
+CREATE INDEX hoststatus_event_hdl_en_idx on icinga_hoststatus(event_handler_enabled);
+CREATE INDEX hoststatus_flap_det_en_idx on icinga_hoststatus(flap_detection_enabled);
+CREATE INDEX hoststatus_is_flapping_idx on icinga_hoststatus(is_flapping);
+CREATE INDEX hoststatus_p_state_chg_idx on icinga_hoststatus(percent_state_change);
+CREATE INDEX hoststatus_latency_idx on icinga_hoststatus(latency);
+CREATE INDEX hoststatus_ex_time_idx on icinga_hoststatus(execution_time);
+CREATE INDEX hoststatus_sch_downt_d_idx on icinga_hoststatus(scheduled_downtime_depth);
+
+-- services
+CREATE INDEX services_host_object_id_idx on icinga_services(host_object_id);
+
+-- servicestatus
+CREATE INDEX srvcstatus_stat_upd_time_idx on icinga_servicestatus(status_update_time);
+CREATE INDEX srvcstatus_current_state_idx on icinga_servicestatus(current_state);
+CREATE INDEX srvcstatus_check_type_idx on icinga_servicestatus(check_type);
+CREATE INDEX srvcstatus_state_type_idx on icinga_servicestatus(state_type);
+CREATE INDEX srvcstatus_last_state_chg_idx on icinga_servicestatus(last_state_change);
+CREATE INDEX srvcstatus_notif_enabled_idx on icinga_servicestatus(notifications_enabled);
+CREATE INDEX srvcstatus_problem_ack_idx on icinga_servicestatus(problem_has_been_acknowledged);
+CREATE INDEX srvcstatus_act_chks_en_idx on icinga_servicestatus(active_checks_enabled);
+CREATE INDEX srvcstatus_pas_chks_en_idx on icinga_servicestatus(passive_checks_enabled);
+CREATE INDEX srvcstatus_event_hdl_en_idx on icinga_servicestatus(event_handler_enabled);
+CREATE INDEX srvcstatus_flap_det_en_idx on icinga_servicestatus(flap_detection_enabled);
+CREATE INDEX srvcstatus_is_flapping_idx on icinga_servicestatus(is_flapping);
+CREATE INDEX srvcstatus_p_state_chg_idx on icinga_servicestatus(percent_state_change);
+CREATE INDEX srvcstatus_latency_idx on icinga_servicestatus(latency);
+CREATE INDEX srvcstatus_ex_time_idx on icinga_servicestatus(execution_time);
+CREATE INDEX srvcstatus_sch_downt_d_idx on icinga_servicestatus(scheduled_downtime_depth);
+
+-- hostchecks
+CREATE INDEX hostchks_h_obj_id_idx on icinga_hostchecks(host_object_id);
+
+-- servicechecks
+CREATE INDEX servicechks_s_obj_id_idx on icinga_servicechecks(service_object_id);
+
+-- objects
+CREATE INDEX objects_name1_idx ON icinga_objects(name1);
+CREATE INDEX objects_name2_idx ON icinga_objects(name2);
+CREATE INDEX objects_inst_id_idx ON icinga_objects(instance_id);
+
+-- instances
+-- CREATE INDEX instances_name_idx on icinga_instances(instance_name);
+
+-- logentries
+-- CREATE INDEX loge_instance_id_idx on icinga_logentries(instance_id);
+-- #236
+CREATE INDEX loge_time_idx on icinga_logentries(logentry_time);
+-- CREATE INDEX loge_data_idx on icinga_logentries(logentry_data);
+CREATE INDEX loge_inst_id_time_idx on icinga_logentries (instance_id ASC, logentry_time DESC);
+
+-- commenthistory
+-- CREATE INDEX c_hist_instance_id_idx on icinga_logentries(instance_id);
+-- CREATE INDEX c_hist_c_time_idx on icinga_logentries(comment_time);
+-- CREATE INDEX c_hist_i_c_id_idx on icinga_logentries(internal_comment_id);
+
+-- downtimehistory
+-- CREATE INDEX d_t_hist_nstance_id_idx on icinga_downtimehistory(instance_id);
+-- CREATE INDEX d_t_hist_type_idx on icinga_downtimehistory(downtime_type);
+-- CREATE INDEX d_t_hist_object_id_idx on icinga_downtimehistory(object_id);
+-- CREATE INDEX d_t_hist_entry_time_idx on icinga_downtimehistory(entry_time);
+-- CREATE INDEX d_t_hist_sched_start_idx on icinga_downtimehistory(scheduled_start_time);
+-- CREATE INDEX d_t_hist_sched_end_idx on icinga_downtimehistory(scheduled_end_time);
+
+-- scheduleddowntime
+-- CREATE INDEX sched_d_t_downtime_type_idx on icinga_scheduleddowntime(downtime_type);
+-- CREATE INDEX sched_d_t_object_id_idx on icinga_scheduleddowntime(object_id);
+-- CREATE INDEX sched_d_t_entry_time_idx on icinga_scheduleddowntime(entry_time);
+-- CREATE INDEX sched_d_t_start_time_idx on icinga_scheduleddowntime(scheduled_start_time);
+-- CREATE INDEX sched_d_t_end_time_idx on icinga_scheduleddowntime(scheduled_end_time);
+
+-- statehistory
+CREATE INDEX statehist_i_id_o_id_s_ty_s_ti on icinga_statehistory(instance_id, object_id, state_type, state_time);
+-- #2274
+create index statehist_state_idx on icinga_statehistory(object_id,state);
+
+
+-- Icinga Web Notifications
+CREATE INDEX notification_idx ON icinga_notifications(notification_type, object_id, start_time);
+CREATE INDEX notification_object_id_idx ON icinga_notifications(object_id);
+CREATE INDEX contact_notification_idx ON icinga_contactnotifications(notification_id, contact_object_id);
+CREATE INDEX contacts_object_id_idx ON icinga_contacts(contact_object_id);
+CREATE INDEX contact_notif_meth_notif_idx ON icinga_contactnotificationmethods(contactnotification_id, command_object_id);
+CREATE INDEX command_object_idx ON icinga_commands(object_id);
+CREATE INDEX services_combined_object_idx ON icinga_services(service_object_id, host_object_id);
+
+
+-- #2618
+CREATE INDEX cntgrpmbrs_cgid_coid ON icinga_contactgroup_members (contactgroup_id,contact_object_id);
+CREATE INDEX hstgrpmbrs_hgid_hoid ON icinga_hostgroup_members (hostgroup_id,host_object_id);
+CREATE INDEX hstcntgrps_hid_cgoid ON icinga_host_contactgroups (host_id,contactgroup_object_id);
+CREATE INDEX hstprnthsts_hid_phoid ON icinga_host_parenthosts (host_id,parent_host_object_id);
+CREATE INDEX runtimevars_iid_varn ON icinga_runtimevariables (instance_id,varname);
+CREATE INDEX sgmbrs_sgid_soid ON icinga_servicegroup_members (servicegroup_id,service_object_id);
+CREATE INDEX scgrps_sid_cgoid ON icinga_service_contactgroups (service_id,contactgroup_object_id);
+CREATE INDEX tperiod_tid_d_ss_es ON icinga_timeperiod_timeranges (timeperiod_id,day,start_sec,end_sec);
+
+-- #3649
+CREATE INDEX sla_idx_sthist ON icinga_statehistory (object_id, state_time DESC);
+CREATE INDEX sla_idx_dohist ON icinga_downtimehistory (object_id, actual_start_time, actual_end_time);
+CREATE INDEX sla_idx_obj ON icinga_objects (objecttype_id, is_active, name1);
+
+-- #4985
+CREATE INDEX commenthistory_delete_idx ON icinga_commenthistory (instance_id, comment_time, internal_comment_id);
+
+-- #10066
+CREATE INDEX idx_endpoints_object_id on icinga_endpoints(endpoint_object_id);
+CREATE INDEX idx_endpointstatus_object_id on icinga_endpointstatus(endpoint_object_id);
+
+CREATE INDEX idx_endpoints_zone_object_id on icinga_endpoints(zone_object_id);
+CREATE INDEX idx_endpointstatus_zone_object_id on icinga_endpointstatus(zone_object_id);
+
+CREATE INDEX idx_zones_object_id on icinga_zones(zone_object_id);
+CREATE INDEX idx_zonestatus_object_id on icinga_zonestatus(zone_object_id);
+
+CREATE INDEX idx_zones_parent_object_id on icinga_zones(parent_zone_object_id);
+CREATE INDEX idx_zonestatus_parent_object_id on icinga_zonestatus(parent_zone_object_id);
+
+-- #12210
+CREATE INDEX idx_comments_session_del ON icinga_comments (instance_id, session_token);
+CREATE INDEX idx_downtimes_session_del ON icinga_scheduleddowntime (instance_id, session_token);
+
+-- #12107
+CREATE INDEX idx_statehistory_cleanup on icinga_statehistory(instance_id, state_time);
+
+-- #12435
+CREATE INDEX idx_contactgroup_members_object_id on icinga_contactgroup_members(contact_object_id);
+CREATE INDEX idx_hostgroup_members_object_id on icinga_hostgroup_members(host_object_id);
+CREATE INDEX idx_servicegroup_members_object_id on icinga_servicegroup_members(service_object_id);
+CREATE INDEX idx_servicedependencies_dependent_service_object_id on icinga_servicedependencies(dependent_service_object_id);
+CREATE INDEX idx_hostdependencies_dependent_host_object_id on icinga_hostdependencies(dependent_host_object_id);
+CREATE INDEX idx_service_contacts_service_id on icinga_service_contacts(service_id);
+CREATE INDEX idx_host_contacts_host_id on icinga_host_contacts(host_id);
+
+-- #5458
+create index idx_downtimehistory_remove on icinga_downtimehistory (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+create index idx_scheduleddowntime_remove on icinga_scheduleddowntime (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+
+-- #5492
+CREATE INDEX idx_commenthistory_remove ON icinga_commenthistory (object_id, entry_time);
+CREATE INDEX idx_comments_remove ON icinga_comments (object_id, entry_time);
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.15.1', NOW(), NOW())
+ON DUPLICATE KEY UPDATE version='1.15.1', modify_time=NOW();
+
+
diff --git a/lib/db_ido_mysql/schema/upgrade/2.0.2.sql b/lib/db_ido_mysql/schema/upgrade/2.0.2.sql
new file mode 100644
index 0000000..c622ae9
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.0.2.sql
@@ -0,0 +1,20 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.0.2
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+UPDATE icinga_objects SET name2 = NULL WHERE name2 = '';
+
+ALTER TABLE `icinga_customvariables` MODIFY COLUMN `varname` varchar(255) character set latin1 collate latin1_general_cs default NULL;
+ALTER TABLE `icinga_customvariablestatus` MODIFY COLUMN `varname` varchar(255) character set latin1 collate latin1_general_cs default NULL;
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.11.6', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.11.6', modify_time=NOW();
+
diff --git a/lib/db_ido_mysql/schema/upgrade/2.1.0.sql b/lib/db_ido_mysql/schema/upgrade/2.1.0.sql
new file mode 100644
index 0000000..7bbed72
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.1.0.sql
@@ -0,0 +1,17 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.1.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+ALTER TABLE `icinga_programstatus` ADD COLUMN `endpoint_name` varchar(255) character set latin1 collate latin1_general_cs default NULL;
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.11.7', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.11.7', modify_time=NOW();
+
diff --git a/lib/db_ido_mysql/schema/upgrade/2.11.0.sql b/lib/db_ido_mysql/schema/upgrade/2.11.0.sql
new file mode 100644
index 0000000..bafa93f
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.11.0.sql
@@ -0,0 +1,89 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.11.0
+--
+-- -----------------------------------------
+-- Copyright (c) 2019 Icinga Development Team (https://icinga.com/)
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+-- --------------------------------------------------------
+-- Helper functions and procedures for DROP INDEX IF EXISTS
+-- --------------------------------------------------------
+
+DELIMITER //
+DROP FUNCTION IF EXISTS ido_index_exists //
+CREATE FUNCTION ido_index_exists(
+ f_table_name varchar(64),
+ f_index_name varchar(64)
+)
+ RETURNS BOOL
+ DETERMINISTIC
+ READS SQL DATA
+ BEGIN
+ DECLARE index_exists BOOL DEFAULT FALSE;
+ SELECT EXISTS (
+ SELECT 1
+ FROM information_schema.statistics
+ WHERE table_schema = SCHEMA()
+ AND table_name = f_table_name
+ AND index_name = f_index_name
+ ) INTO index_exists;
+ RETURN index_exists;
+ END //
+
+DROP PROCEDURE IF EXISTS ido_drop_index_if_exists //
+CREATE PROCEDURE ido_drop_index_if_exists (
+ IN p_table_name varchar(64),
+ IN p_index_name varchar(64)
+)
+ DETERMINISTIC
+ MODIFIES SQL DATA
+ BEGIN
+ IF ido_index_exists(p_table_name, p_index_name)
+ THEN
+ SET @ido_drop_index_sql = CONCAT('ALTER TABLE `', SCHEMA(), '`.`', p_table_name, '` DROP INDEX `', p_index_name, '`');
+ PREPARE stmt FROM @ido_drop_index_sql;
+ EXECUTE stmt;
+ DEALLOCATE PREPARE stmt;
+ SET @ido_drop_index_sql = NULL;
+ END IF;
+ END //
+DELIMITER ;
+
+CALL ido_drop_index_if_exists('icinga_commands', 'commands_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_comments', 'idx_comments_object_id');
+CALL ido_drop_index_if_exists('icinga_comments', 'comments_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_configfiles', 'configfiles_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_contactgroups', 'contactgroups_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_contacts', 'contacts_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_customvariables', 'idx_customvariables_object_id');
+CALL ido_drop_index_if_exists('icinga_eventhandlers', 'eventhandlers_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_hostdependencies', 'hostdependencies_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_hostescalations', 'hostesc_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_hostescalation_contacts', 'hostesc_contacts_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_hostgroups', 'hostgroups_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_hosts', 'host_object_id');
+CALL ido_drop_index_if_exists('icinga_hosts', 'hosts_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_objects', 'objects_objtype_id_idx');
+CALL ido_drop_index_if_exists('icinga_programstatus', 'programstatus_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_runtimevariables', 'runtimevariables_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_scheduleddowntime', 'scheduleddowntime_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_scheduleddowntime', 'idx_scheduleddowntime_object_id');
+CALL ido_drop_index_if_exists('icinga_serviceescalations', 'serviceesc_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_serviceescalation_contacts', 'serviceesc_contacts_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_servicegroups', 'servicegroups_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_services', 'services_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_services', 'service_object_id');
+CALL ido_drop_index_if_exists('icinga_systemcommands', 'systemcommands_i_id_idx');
+CALL ido_drop_index_if_exists('icinga_timeperiods', 'timeperiods_i_id_idx');
+
+DROP FUNCTION ido_index_exists;
+DROP PROCEDURE ido_drop_index_if_exists;
+
+-- -----------------------------------------
+-- set dbversion (same as 2.11.0)
+-- -----------------------------------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.15.0', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.15.0', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.12.7.sql b/lib/db_ido_mysql/schema/upgrade/2.12.7.sql
new file mode 100644
index 0000000..6319b37
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.12.7.sql
@@ -0,0 +1,15 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.12.7
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2021 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+-- -------------
+-- set dbversion
+-- -------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.15.0', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.15.0', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.13.0.sql b/lib/db_ido_mysql/schema/upgrade/2.13.0.sql
new file mode 100644
index 0000000..462be6f
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.13.0.sql
@@ -0,0 +1,23 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.13.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2021 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+-- ----------------------------------------
+-- #7472 Support hosts with >128 characters
+-- ----------------------------------------
+
+ALTER TABLE icinga_objects
+ MODIFY COLUMN name1 varchar(255) character set latin1 collate latin1_general_cs default '',
+ MODIFY COLUMN name2 varchar(255) character set latin1 collate latin1_general_cs default NULL;
+
+-- -------------
+-- set dbversion
+-- -------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.15.1', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.15.1', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.13.3.sql b/lib/db_ido_mysql/schema/upgrade/2.13.3.sql
new file mode 100644
index 0000000..577eb0a
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.13.3.sql
@@ -0,0 +1,15 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.13.3
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2021 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+-- -------------
+-- set dbversion
+-- -------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.15.1', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.15.1', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.2.0.sql b/lib/db_ido_mysql/schema/upgrade/2.2.0.sql
new file mode 100644
index 0000000..22a6115
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.2.0.sql
@@ -0,0 +1,23 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.2.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+ALTER TABLE `icinga_programstatus` ADD COLUMN `program_version` varchar(64) character set latin1 collate latin1_general_cs default NULL;
+
+ALTER TABLE icinga_contacts MODIFY alias TEXT character set latin1;
+ALTER TABLE icinga_hosts MODIFY alias TEXT character set latin1;
+
+ALTER TABLE icinga_customvariables ADD COLUMN is_json smallint default 0;
+ALTER TABLE icinga_customvariablestatus ADD COLUMN is_json smallint default 0;
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.12.0', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.12.0', modify_time=NOW();
+
diff --git a/lib/db_ido_mysql/schema/upgrade/2.3.0.sql b/lib/db_ido_mysql/schema/upgrade/2.3.0.sql
new file mode 100644
index 0000000..f2fe463
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.3.0.sql
@@ -0,0 +1,26 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.3.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+-- -----------------------------------------
+-- #7765 drop unique constraint
+-- -----------------------------------------
+
+ALTER TABLE icinga_servicedependencies DROP KEY instance_id;
+ALTER TABLE icinga_hostdependencies DROP KEY instance_id;
+
+ALTER TABLE icinga_servicedependencies ADD KEY instance_id (instance_id,config_type,service_object_id,dependent_service_object_id,dependency_type,inherits_parent,fail_on_ok,fail_on_warning,fail_on_unknown,fail_on_critical);
+ALTER TABLE icinga_hostdependencies ADD KEY instance_id (instance_id,config_type,host_object_id,dependent_host_object_id,dependency_type,inherits_parent,fail_on_up,fail_on_down,fail_on_unreachable);
+
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.13.0', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.13.0', modify_time=NOW();
+
diff --git a/lib/db_ido_mysql/schema/upgrade/2.4.0.sql b/lib/db_ido_mysql/schema/upgrade/2.4.0.sql
new file mode 100644
index 0000000..f6803f7
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.4.0.sql
@@ -0,0 +1,75 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.4.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+-- -----------------------------------------
+-- #9286 - zone tables
+-- -----------------------------------------
+
+ALTER TABLE icinga_endpoints ADD COLUMN zone_object_id bigint(20) unsigned DEFAULT '0';
+ALTER TABLE icinga_endpointstatus ADD COLUMN zone_object_id bigint(20) unsigned DEFAULT '0';
+
+CREATE TABLE IF NOT EXISTS icinga_zones (
+ zone_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ zone_object_id bigint(20) unsigned DEFAULT '0',
+ config_type smallint(6) DEFAULT '0',
+ parent_zone_object_id bigint(20) unsigned DEFAULT '0',
+ is_global smallint(6),
+ PRIMARY KEY (zone_id)
+) ENGINE=InnoDB COMMENT='Zone configuration';
+
+CREATE TABLE IF NOT EXISTS icinga_zonestatus (
+ zonestatus_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+ instance_id bigint unsigned default 0,
+ zone_object_id bigint(20) unsigned DEFAULT '0',
+ status_update_time timestamp NOT NULL,
+ parent_zone_object_id bigint(20) unsigned DEFAULT '0',
+ PRIMARY KEY (zonestatus_id)
+) ENGINE=InnoDB COMMENT='Zone status';
+
+
+-- -----------------------------------------
+-- #9576 - freshness_threshold
+-- -----------------------------------------
+
+ALTER TABLE icinga_services MODIFY freshness_threshold int;
+ALTER TABLE icinga_hosts MODIFY freshness_threshold int;
+
+-- -----------------------------------------
+-- #10392 - original attributes
+-- -----------------------------------------
+
+ALTER TABLE icinga_servicestatus ADD COLUMN original_attributes TEXT character set latin1 default NULL;
+ALTER TABLE icinga_hoststatus ADD COLUMN original_attributes TEXT character set latin1 default NULL;
+
+-- -----------------------------------------
+-- #10436 deleted custom vars
+-- -----------------------------------------
+
+ALTER TABLE icinga_customvariables ADD COLUMN session_token int default NULL;
+ALTER TABLE icinga_customvariablestatus ADD COLUMN session_token int default NULL;
+
+CREATE INDEX cv_session_del_idx ON icinga_customvariables (session_token);
+CREATE INDEX cvs_session_del_idx ON icinga_customvariablestatus (session_token);
+
+-- -----------------------------------------
+-- #10431 comment/downtime name
+-- -----------------------------------------
+
+ALTER TABLE icinga_comments ADD COLUMN name TEXT character set latin1 default NULL;
+ALTER TABLE icinga_commenthistory ADD COLUMN name TEXT character set latin1 default NULL;
+
+ALTER TABLE icinga_scheduleddowntime ADD COLUMN name TEXT character set latin1 default NULL;
+ALTER TABLE icinga_downtimehistory ADD COLUMN name TEXT character set latin1 default NULL;
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.14.0', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.14.0', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.5.0.sql b/lib/db_ido_mysql/schema/upgrade/2.5.0.sql
new file mode 100644
index 0000000..d5714a0
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.5.0.sql
@@ -0,0 +1,103 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.5.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+-- -----------------------------------------
+-- #10069 IDO: check_source should not be a TEXT field
+-- -----------------------------------------
+
+ALTER TABLE icinga_hoststatus MODIFY COLUMN check_source varchar(255) character set latin1 default '';
+ALTER TABLE icinga_servicestatus MODIFY COLUMN check_source varchar(255) character set latin1 default '';
+
+-- -----------------------------------------
+-- #10070
+-- -----------------------------------------
+
+CREATE INDEX idx_comments_object_id on icinga_comments(object_id);
+CREATE INDEX idx_scheduleddowntime_object_id on icinga_scheduleddowntime(object_id);
+
+-- -----------------------------------------
+-- #11962
+-- -----------------------------------------
+
+ALTER TABLE icinga_hoststatus MODIFY COLUMN current_notification_number int unsigned default 0;
+ALTER TABLE icinga_servicestatus MODIFY COLUMN current_notification_number int unsigned default 0;
+
+-- -----------------------------------------
+-- #10061
+-- -----------------------------------------
+
+ALTER TABLE icinga_contactgroups MODIFY COLUMN alias varchar(255) character set latin1 default '';
+ALTER TABLE icinga_contacts MODIFY COLUMN alias varchar(255) character set latin1 default '';
+ALTER TABLE icinga_hostgroups MODIFY COLUMN alias varchar(255) character set latin1 default '';
+ALTER TABLE icinga_hosts MODIFY COLUMN alias varchar(255) character set latin1 default '';
+ALTER TABLE icinga_servicegroups MODIFY COLUMN alias varchar(255) character set latin1 default '';
+ALTER TABLE icinga_timeperiods MODIFY COLUMN alias varchar(255) character set latin1 default '';
+
+-- -----------------------------------------
+-- #10066
+-- -----------------------------------------
+
+CREATE INDEX idx_endpoints_object_id on icinga_endpoints(endpoint_object_id);
+CREATE INDEX idx_endpointstatus_object_id on icinga_endpointstatus(endpoint_object_id);
+
+CREATE INDEX idx_endpoints_zone_object_id on icinga_endpoints(zone_object_id);
+CREATE INDEX idx_endpointstatus_zone_object_id on icinga_endpointstatus(zone_object_id);
+
+CREATE INDEX idx_zones_object_id on icinga_zones(zone_object_id);
+CREATE INDEX idx_zonestatus_object_id on icinga_zonestatus(zone_object_id);
+
+CREATE INDEX idx_zones_parent_object_id on icinga_zones(parent_zone_object_id);
+CREATE INDEX idx_zonestatus_parent_object_id on icinga_zonestatus(parent_zone_object_id);
+
+-- -----------------------------------------
+-- #12107
+-- -----------------------------------------
+CREATE INDEX idx_statehistory_cleanup on icinga_statehistory(instance_id, state_time);
+
+-- -----------------------------------------
+-- #12258
+-- -----------------------------------------
+ALTER TABLE icinga_comments ADD COLUMN session_token INTEGER default NULL;
+ALTER TABLE icinga_scheduleddowntime ADD COLUMN session_token INTEGER default NULL;
+
+CREATE INDEX idx_comments_session_del ON icinga_comments (instance_id, session_token);
+CREATE INDEX idx_downtimes_session_del ON icinga_scheduleddowntime (instance_id, session_token);
+
+-- -----------------------------------------
+-- #12435
+-- -----------------------------------------
+ALTER TABLE icinga_commands ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_contactgroups ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_contacts ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_hostgroups ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_hosts ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_servicegroups ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_services ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_timeperiods ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_endpoints ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_zones ADD config_hash VARCHAR(64) DEFAULT NULL;
+
+ALTER TABLE icinga_customvariables DROP session_token;
+ALTER TABLE icinga_customvariablestatus DROP session_token;
+
+CREATE INDEX idx_customvariables_object_id on icinga_customvariables(object_id);
+CREATE INDEX idx_contactgroup_members_object_id on icinga_contactgroup_members(contact_object_id);
+CREATE INDEX idx_hostgroup_members_object_id on icinga_hostgroup_members(host_object_id);
+CREATE INDEX idx_servicegroup_members_object_id on icinga_servicegroup_members(service_object_id);
+CREATE INDEX idx_servicedependencies_dependent_service_object_id on icinga_servicedependencies(dependent_service_object_id);
+CREATE INDEX idx_hostdependencies_dependent_host_object_id on icinga_hostdependencies(dependent_host_object_id);
+CREATE INDEX idx_service_contacts_service_id on icinga_service_contacts(service_id);
+CREATE INDEX idx_host_contacts_host_id on icinga_host_contacts(host_id);
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.14.1', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.14.1', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.6.0.sql b/lib/db_ido_mysql/schema/upgrade/2.6.0.sql
new file mode 100644
index 0000000..33dd780
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.6.0.sql
@@ -0,0 +1,151 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.6.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+-- -----------------------------------------
+-- #10502 IDO: Support NO_ZERO_DATE and NO_ZERO_IN_DATE SQL modes
+-- -----------------------------------------
+
+ALTER TABLE icinga_acknowledgements
+ MODIFY COLUMN entry_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_commenthistory
+ MODIFY COLUMN entry_time timestamp NULL,
+ MODIFY COLUMN comment_time timestamp NULL,
+ MODIFY COLUMN expiration_time timestamp NULL,
+ MODIFY COLUMN deletion_time timestamp NULL;
+
+ALTER TABLE icinga_comments
+ MODIFY COLUMN entry_time timestamp NULL,
+ MODIFY COLUMN comment_time timestamp NULL,
+ MODIFY COLUMN expiration_time timestamp NULL;
+
+ALTER TABLE icinga_conninfo
+ MODIFY COLUMN connect_time timestamp NULL,
+ MODIFY COLUMN disconnect_time timestamp NULL,
+ MODIFY COLUMN last_checkin_time timestamp NULL,
+ MODIFY COLUMN data_start_time timestamp NULL,
+ MODIFY COLUMN data_end_time timestamp NULL;
+
+ALTER TABLE icinga_contactnotificationmethods
+ MODIFY COLUMN start_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_contactnotifications
+ MODIFY COLUMN start_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_contactstatus
+ MODIFY COLUMN status_update_time timestamp NULL,
+ MODIFY COLUMN last_host_notification timestamp NULL,
+ MODIFY COLUMN last_service_notification timestamp NULL;
+
+ALTER TABLE icinga_customvariablestatus
+ MODIFY COLUMN status_update_time timestamp NULL;
+
+ALTER TABLE icinga_dbversion
+ MODIFY COLUMN create_time timestamp NULL,
+ MODIFY COLUMN modify_time timestamp NULL;
+
+ALTER TABLE icinga_downtimehistory
+ MODIFY COLUMN entry_time timestamp NULL,
+ MODIFY COLUMN scheduled_start_time timestamp NULL,
+ MODIFY COLUMN scheduled_end_time timestamp NULL,
+ MODIFY COLUMN actual_start_time timestamp NULL,
+ MODIFY COLUMN actual_end_time timestamp NULL,
+ MODIFY COLUMN trigger_time timestamp NULL;
+
+ALTER TABLE icinga_eventhandlers
+ MODIFY COLUMN start_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_externalcommands
+ MODIFY COLUMN entry_time timestamp NULL;
+
+ALTER TABLE icinga_flappinghistory
+ MODIFY COLUMN event_time timestamp NULL,
+ MODIFY COLUMN comment_time timestamp NULL;
+
+ALTER TABLE icinga_hostchecks
+ MODIFY COLUMN start_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_hoststatus
+ MODIFY COLUMN status_update_time timestamp NULL,
+ MODIFY COLUMN last_check timestamp NULL,
+ MODIFY COLUMN next_check timestamp NULL,
+ MODIFY COLUMN last_state_change timestamp NULL,
+ MODIFY COLUMN last_hard_state_change timestamp NULL,
+ MODIFY COLUMN last_time_up timestamp NULL,
+ MODIFY COLUMN last_time_down timestamp NULL,
+ MODIFY COLUMN last_time_unreachable timestamp NULL,
+ MODIFY COLUMN last_notification timestamp NULL,
+ MODIFY COLUMN next_notification timestamp NULL;
+
+ALTER TABLE icinga_logentries
+ MODIFY COLUMN logentry_time timestamp NULL,
+ MODIFY COLUMN entry_time timestamp NULL;
+
+ALTER TABLE icinga_notifications
+ MODIFY COLUMN start_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_processevents
+ MODIFY COLUMN event_time timestamp NULL;
+
+ALTER TABLE icinga_programstatus
+ MODIFY COLUMN status_update_time timestamp NULL,
+ MODIFY COLUMN program_start_time timestamp NULL,
+ MODIFY COLUMN program_end_time timestamp NULL,
+ MODIFY COLUMN last_command_check timestamp NULL,
+ MODIFY COLUMN last_log_rotation timestamp NULL,
+ MODIFY COLUMN disable_notif_expire_time timestamp NULL;
+
+ALTER TABLE icinga_scheduleddowntime
+ MODIFY COLUMN entry_time timestamp NULL,
+ MODIFY COLUMN scheduled_start_time timestamp NULL,
+ MODIFY COLUMN scheduled_end_time timestamp NULL,
+ MODIFY COLUMN actual_start_time timestamp NULL,
+ MODIFY COLUMN trigger_time timestamp NULL;
+
+ALTER TABLE icinga_servicechecks
+ MODIFY COLUMN start_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_servicestatus
+ MODIFY COLUMN status_update_time timestamp NULL,
+ MODIFY COLUMN last_check timestamp NULL,
+ MODIFY COLUMN next_check timestamp NULL,
+ MODIFY COLUMN last_state_change timestamp NULL,
+ MODIFY COLUMN last_hard_state_change timestamp NULL,
+ MODIFY COLUMN last_time_ok timestamp NULL,
+ MODIFY COLUMN last_time_warning timestamp NULL,
+ MODIFY COLUMN last_time_unknown timestamp NULL,
+ MODIFY COLUMN last_time_critical timestamp NULL,
+ MODIFY COLUMN last_notification timestamp NULL,
+ MODIFY COLUMN next_notification timestamp NULL;
+
+ALTER TABLE icinga_statehistory
+ MODIFY COLUMN state_time timestamp NULL;
+
+ALTER TABLE icinga_systemcommands
+ MODIFY COLUMN start_time timestamp NULL,
+ MODIFY COLUMN end_time timestamp NULL;
+
+ALTER TABLE icinga_endpointstatus
+ MODIFY COLUMN status_update_time timestamp NULL;
+
+ALTER TABLE icinga_zonestatus
+ MODIFY COLUMN status_update_time timestamp NULL;
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.14.2', NOW(), NOW())
+ON DUPLICATE KEY UPDATE version='1.14.2', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.8.0.sql b/lib/db_ido_mysql/schema/upgrade/2.8.0.sql
new file mode 100644
index 0000000..8d511a7
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.8.0.sql
@@ -0,0 +1,81 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.8.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+-- --------------------------------------------------------
+-- Helper functions and procedures for DROP INDEX IF EXISTS
+-- --------------------------------------------------------
+
+DELIMITER //
+DROP FUNCTION IF EXISTS ido_index_exists //
+CREATE FUNCTION ido_index_exists(
+ f_table_name varchar(64),
+ f_index_name varchar(64)
+)
+ RETURNS BOOL
+ DETERMINISTIC
+ READS SQL DATA
+ BEGIN
+ DECLARE index_exists BOOL DEFAULT FALSE;
+ SELECT EXISTS (
+ SELECT 1
+ FROM information_schema.statistics
+ WHERE table_schema = SCHEMA()
+ AND table_name = f_table_name
+ AND index_name = f_index_name
+ ) INTO index_exists;
+ RETURN index_exists;
+ END //
+
+DROP PROCEDURE IF EXISTS ido_drop_index_if_exists //
+CREATE PROCEDURE ido_drop_index_if_exists (
+ IN p_table_name varchar(64),
+ IN p_index_name varchar(64)
+)
+ DETERMINISTIC
+ MODIFIES SQL DATA
+ BEGIN
+ IF ido_index_exists(p_table_name, p_index_name)
+ THEN
+ SET @ido_drop_index_sql = CONCAT('ALTER TABLE `', SCHEMA(), '`.`', p_table_name, '` DROP INDEX `', p_index_name, '`');
+ PREPARE stmt FROM @ido_drop_index_sql;
+ EXECUTE stmt;
+ DEALLOCATE PREPARE stmt;
+ SET @ido_drop_index_sql = NULL;
+ END IF;
+ END //
+DELIMITER ;
+
+CALL ido_drop_index_if_exists('icinga_downtimehistory', 'instance_id');
+CALL ido_drop_index_if_exists('icinga_scheduleddowntime', 'instance_id');
+CALL ido_drop_index_if_exists('icinga_commenthistory', 'instance_id');
+CALL ido_drop_index_if_exists('icinga_comments', 'instance_id');
+
+DROP FUNCTION ido_index_exists;
+DROP PROCEDURE ido_drop_index_if_exists;
+
+-- -----------------------------------------
+-- #5458 IDO: Improve downtime removal/cancel
+-- -----------------------------------------
+
+CREATE INDEX idx_downtimehistory_remove ON icinga_downtimehistory (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+CREATE INDEX idx_scheduleddowntime_remove ON icinga_scheduleddowntime (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+
+-- -----------------------------------------
+-- #5492 IDO: Improve comment removal
+-- -----------------------------------------
+
+CREATE INDEX idx_commenthistory_remove ON icinga_commenthistory (object_id, entry_time);
+CREATE INDEX idx_comments_remove ON icinga_comments (object_id, entry_time);
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.14.3', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.14.3', modify_time=NOW();
diff --git a/lib/db_ido_mysql/schema/upgrade/2.8.1.sql b/lib/db_ido_mysql/schema/upgrade/2.8.1.sql
new file mode 100644
index 0000000..98f8511
--- /dev/null
+++ b/lib/db_ido_mysql/schema/upgrade/2.8.1.sql
@@ -0,0 +1,67 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.8.1 (fix for fresh 2.8.0 installation only)
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+SET SQL_MODE="NO_AUTO_VALUE_ON_ZERO";
+
+-- --------------------------------------------------------
+-- Helper functions and procedures for DROP INDEX IF EXISTS
+-- --------------------------------------------------------
+
+DELIMITER //
+DROP FUNCTION IF EXISTS ido_index_exists //
+CREATE FUNCTION ido_index_exists(
+ f_table_name varchar(64),
+ f_index_name varchar(64)
+)
+ RETURNS BOOL
+ DETERMINISTIC
+ READS SQL DATA
+ BEGIN
+ DECLARE index_exists BOOL DEFAULT FALSE;
+ SELECT EXISTS (
+ SELECT 1
+ FROM information_schema.statistics
+ WHERE table_schema = SCHEMA()
+ AND table_name = f_table_name
+ AND index_name = f_index_name
+ ) INTO index_exists;
+ RETURN index_exists;
+ END //
+
+DROP PROCEDURE IF EXISTS ido_drop_index_if_exists //
+CREATE PROCEDURE ido_drop_index_if_exists (
+ IN p_table_name varchar(64),
+ IN p_index_name varchar(64)
+)
+ DETERMINISTIC
+ MODIFIES SQL DATA
+ BEGIN
+ IF ido_index_exists(p_table_name, p_index_name)
+ THEN
+ SET @ido_drop_index_sql = CONCAT('ALTER TABLE `', SCHEMA(), '`.`', p_table_name, '` DROP INDEX `', p_index_name, '`');
+ PREPARE stmt FROM @ido_drop_index_sql;
+ EXECUTE stmt;
+ DEALLOCATE PREPARE stmt;
+ SET @ido_drop_index_sql = NULL;
+ END IF;
+ END //
+DELIMITER ;
+
+CALL ido_drop_index_if_exists('icinga_downtimehistory', 'instance_id');
+CALL ido_drop_index_if_exists('icinga_scheduleddowntime', 'instance_id');
+CALL ido_drop_index_if_exists('icinga_commenthistory', 'instance_id');
+CALL ido_drop_index_if_exists('icinga_comments', 'instance_id');
+
+DROP FUNCTION ido_index_exists;
+DROP PROCEDURE ido_drop_index_if_exists;
+
+-- -----------------------------------------
+-- set dbversion (same as 2.8.0)
+-- -----------------------------------------
+INSERT INTO icinga_dbversion (name, version, create_time, modify_time) VALUES ('idoutils', '1.14.3', NOW(), NOW()) ON DUPLICATE KEY UPDATE version='1.14.3', modify_time=NOW();
diff --git a/lib/db_ido_pgsql/CMakeLists.txt b/lib/db_ido_pgsql/CMakeLists.txt
new file mode 100644
index 0000000..e081a62
--- /dev/null
+++ b/lib/db_ido_pgsql/CMakeLists.txt
@@ -0,0 +1,41 @@
+# Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+
+mkclass_target(idopgsqlconnection.ti idopgsqlconnection-ti.cpp idopgsqlconnection-ti.hpp)
+
+set(db_ido_pgsql_SOURCES
+ idopgsqlconnection.cpp idopgsqlconnection.hpp idopgsqlconnection-ti.hpp
+)
+
+if(ICINGA2_UNITY_BUILD)
+ mkunity_target(db_ido_pgsql db_ido_pgsql db_ido_pgsql_SOURCES)
+endif()
+
+add_library(db_ido_pgsql OBJECT ${db_ido_pgsql_SOURCES})
+
+include_directories(${PostgreSQL_INCLUDE_DIRS})
+
+add_dependencies(db_ido_pgsql base config icinga db_ido)
+
+set_target_properties (
+ db_ido_pgsql PROPERTIES
+ FOLDER Components
+)
+
+install_if_not_exists(
+ ${PROJECT_SOURCE_DIR}/etc/icinga2/features-available/ido-pgsql.conf
+ ${ICINGA2_CONFIGDIR}/features-available
+)
+
+install(
+ DIRECTORY schema
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-pgsql
+ FILES_MATCHING PATTERN "*.sql"
+)
+
+install(
+ DIRECTORY schema/upgrade
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/icinga2-ido-pgsql/schema
+ FILES_MATCHING PATTERN "*.sql"
+)
+
+set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${CPACK_NSIS_EXTRA_INSTALL_COMMANDS}" PARENT_SCOPE)
diff --git a/lib/db_ido_pgsql/idopgsqlconnection.cpp b/lib/db_ido_pgsql/idopgsqlconnection.cpp
new file mode 100644
index 0000000..9a20b95
--- /dev/null
+++ b/lib/db_ido_pgsql/idopgsqlconnection.cpp
@@ -0,0 +1,1028 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido_pgsql/idopgsqlconnection.hpp"
+#include "db_ido_pgsql/idopgsqlconnection-ti.cpp"
+#include "db_ido/dbtype.hpp"
+#include "db_ido/dbvalue.hpp"
+#include "base/logger.hpp"
+#include "base/objectlock.hpp"
+#include "base/convert.hpp"
+#include "base/utility.hpp"
+#include "base/perfdatavalue.hpp"
+#include "base/application.hpp"
+#include "base/configtype.hpp"
+#include "base/exception.hpp"
+#include "base/context.hpp"
+#include "base/statsfunction.hpp"
+#include "base/defer.hpp"
+#include <utility>
+
+using namespace icinga;
+
+REGISTER_TYPE(IdoPgsqlConnection);
+
+REGISTER_STATSFUNCTION(IdoPgsqlConnection, &IdoPgsqlConnection::StatsFunc);
+
+const char * IdoPgsqlConnection::GetLatestSchemaVersion() const noexcept
+{
+ return "1.14.3";
+}
+
+const char * IdoPgsqlConnection::GetCompatSchemaVersion() const noexcept
+{
+ return "1.14.3";
+}
+
+IdoPgsqlConnection::IdoPgsqlConnection()
+{
+ m_QueryQueue.SetName("IdoPgsqlConnection, " + GetName());
+}
+
+void IdoPgsqlConnection::OnConfigLoaded()
+{
+ ObjectImpl<IdoPgsqlConnection>::OnConfigLoaded();
+
+ m_QueryQueue.SetName("IdoPgsqlConnection, " + GetName());
+
+ Library shimLibrary{"pgsql_shim"};
+
+ auto create_pgsql_shim = shimLibrary.GetSymbolAddress<create_pgsql_shim_ptr>("create_pgsql_shim");
+
+ m_Pgsql.reset(create_pgsql_shim());
+
+ std::swap(m_Library, shimLibrary);
+}
+
+void IdoPgsqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)
+{
+ DictionaryData nodes;
+
+ for (const IdoPgsqlConnection::Ptr& idopgsqlconnection : ConfigType::GetObjectsByType<IdoPgsqlConnection>()) {
+ size_t queryQueueItems = idopgsqlconnection->m_QueryQueue.GetLength();
+ double queryQueueItemRate = idopgsqlconnection->m_QueryQueue.GetTaskCount(60) / 60.0;
+
+ nodes.emplace_back(idopgsqlconnection->GetName(), new Dictionary({
+ { "version", idopgsqlconnection->GetSchemaVersion() },
+ { "instance_name", idopgsqlconnection->GetInstanceName() },
+ { "connected", idopgsqlconnection->GetConnected() },
+ { "query_queue_items", queryQueueItems },
+ { "query_queue_item_rate", queryQueueItemRate }
+ }));
+
+ perfdata->Add(new PerfdataValue("idopgsqlconnection_" + idopgsqlconnection->GetName() + "_queries_rate", idopgsqlconnection->GetQueryCount(60) / 60.0));
+ perfdata->Add(new PerfdataValue("idopgsqlconnection_" + idopgsqlconnection->GetName() + "_queries_1min", idopgsqlconnection->GetQueryCount(60)));
+ perfdata->Add(new PerfdataValue("idopgsqlconnection_" + idopgsqlconnection->GetName() + "_queries_5mins", idopgsqlconnection->GetQueryCount(5 * 60)));
+ perfdata->Add(new PerfdataValue("idopgsqlconnection_" + idopgsqlconnection->GetName() + "_queries_15mins", idopgsqlconnection->GetQueryCount(15 * 60)));
+ perfdata->Add(new PerfdataValue("idopgsqlconnection_" + idopgsqlconnection->GetName() + "_query_queue_items", queryQueueItems));
+ perfdata->Add(new PerfdataValue("idopgsqlconnection_" + idopgsqlconnection->GetName() + "_query_queue_item_rate", queryQueueItemRate));
+ }
+
+ status->Set("idopgsqlconnection", new Dictionary(std::move(nodes)));
+}
+
+void IdoPgsqlConnection::Resume()
+{
+ Log(LogInformation, "IdoPgsqlConnection")
+ << "'" << GetName() << "' resumed.";
+
+ SetConnected(false);
+
+ m_QueryQueue.SetExceptionCallback([this](boost::exception_ptr exp) { ExceptionHandler(std::move(exp)); });
+
+ /* Immediately try to connect on Resume() without timer. */
+ m_QueryQueue.Enqueue([this]() { Reconnect(); }, PriorityImmediate);
+
+ m_TxTimer = new Timer();
+ m_TxTimer->SetInterval(1);
+ m_TxTimer->OnTimerExpired.connect([this](const Timer * const&) { NewTransaction(); });
+ m_TxTimer->Start();
+
+ m_ReconnectTimer = new Timer();
+ m_ReconnectTimer->SetInterval(10);
+ m_ReconnectTimer->OnTimerExpired.connect([this](const Timer * const&) { ReconnectTimerHandler(); });
+ m_ReconnectTimer->Start();
+
+ /* Start with queries after connect. */
+ DbConnection::Resume();
+
+ ASSERT(m_Pgsql->isthreadsafe());
+}
+
+void IdoPgsqlConnection::Pause()
+{
+ DbConnection::Pause();
+
+ m_ReconnectTimer.reset();
+
+ Log(LogInformation, "IdoPgsqlConnection")
+ << "'" << GetName() << "' paused.";
+}
+
+void IdoPgsqlConnection::ExceptionHandler(boost::exception_ptr exp)
+{
+ Log(LogWarning, "IdoPgsqlConnection", "Exception during database operation: Verify that your database is operational!");
+
+ Log(LogDebug, "IdoPgsqlConnection")
+ << "Exception during database operation: " << DiagnosticInformation(std::move(exp));
+
+ if (GetConnected()) {
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+ }
+}
+
+void IdoPgsqlConnection::AssertOnWorkQueue()
+{
+ ASSERT(m_QueryQueue.IsWorkerThread());
+}
+
+void IdoPgsqlConnection::Disconnect()
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected())
+ return;
+
+ IncreasePendingQueries(1);
+ Query("COMMIT");
+
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+
+ Log(LogInformation, "IdoPgsqlConnection")
+ << "Disconnected from '" << GetName() << "' database '" << GetDatabase() << "'.";
+}
+
+void IdoPgsqlConnection::NewTransaction()
+{
+ if (IsPaused())
+ return;
+
+ m_QueryQueue.Enqueue([this]() { InternalNewTransaction(); }, PriorityNormal, true);
+}
+
+void IdoPgsqlConnection::InternalNewTransaction()
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected())
+ return;
+
+ IncreasePendingQueries(2);
+ Query("COMMIT");
+ Query("BEGIN");
+}
+
+void IdoPgsqlConnection::ReconnectTimerHandler()
+{
+ /* Only allow Reconnect events with high priority. */
+ m_QueryQueue.Enqueue([this]() { Reconnect(); }, PriorityHigh);
+}
+
+void IdoPgsqlConnection::Reconnect()
+{
+ AssertOnWorkQueue();
+
+ CONTEXT("Reconnecting to PostgreSQL IDO database '" + GetName() + "'");
+
+ double startTime = Utility::GetTime();
+
+ SetShouldConnect(true);
+
+ bool reconnect = false;
+
+ if (GetConnected()) {
+ /* Check if we're really still connected */
+ try {
+ IncreasePendingQueries(1);
+ Query("SELECT 1");
+ return;
+ } catch (const std::exception&) {
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+ reconnect = true;
+ }
+ }
+
+ ClearIDCache();
+
+ String host = GetHost();
+ String port = GetPort();
+ String user = GetUser();
+ String password = GetPassword();
+ String database = GetDatabase();
+
+ String sslMode = GetSslMode();
+ String sslKey = GetSslKey();
+ String sslCert = GetSslCert();
+ String sslCa = GetSslCa();
+
+ String conninfo;
+
+ if (!host.IsEmpty())
+ conninfo += " host=" + host;
+ if (!port.IsEmpty())
+ conninfo += " port=" + port;
+ if (!user.IsEmpty())
+ conninfo += " user=" + user;
+ if (!password.IsEmpty())
+ conninfo += " password=" + password;
+ if (!database.IsEmpty())
+ conninfo += " dbname=" + database;
+
+ if (!sslMode.IsEmpty())
+ conninfo += " sslmode=" + sslMode;
+ if (!sslKey.IsEmpty())
+ conninfo += " sslkey=" + sslKey;
+ if (!sslCert.IsEmpty())
+ conninfo += " sslcert=" + sslCert;
+ if (!sslCa.IsEmpty())
+ conninfo += " sslrootcert=" + sslCa;
+
+ /* connection */
+ m_Connection = m_Pgsql->connectdb(conninfo.CStr());
+
+ if (!m_Connection)
+ return;
+
+ if (m_Pgsql->status(m_Connection) != CONNECTION_OK) {
+ String message = m_Pgsql->errorMessage(m_Connection);
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+
+ Log(LogCritical, "IdoPgsqlConnection")
+ << "Connection to database '" << database << "' with user '" << user << "' on '" << host << ":" << port
+ << "' failed: \"" << message << "\"";
+
+ BOOST_THROW_EXCEPTION(std::runtime_error(message));
+ }
+
+ SetConnected(true);
+
+ IdoPgsqlResult result;
+
+ String dbVersionName = "idoutils";
+ IncreasePendingQueries(1);
+ result = Query("SELECT version FROM " + GetTablePrefix() + "dbversion WHERE name='" + Escape(dbVersionName) + "'");
+
+ Dictionary::Ptr row = FetchRow(result, 0);
+
+ if (!row) {
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+
+ Log(LogCritical, "IdoPgsqlConnection", "Schema does not provide any valid version! Verify your schema installation.");
+
+ BOOST_THROW_EXCEPTION(std::runtime_error("Invalid schema."));
+ }
+
+ String version = row->Get("version");
+
+ SetSchemaVersion(version);
+
+ if (Utility::CompareVersion(GetCompatSchemaVersion(), version) < 0) {
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+
+ Log(LogCritical, "IdoPgsqlConnection")
+ << "Schema version '" << version << "' does not match the required version '"
+ << GetCompatSchemaVersion() << "' (or newer)! Please check the upgrade documentation at "
+ << "https://icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/#upgrading-postgresql-db";
+
+ BOOST_THROW_EXCEPTION(std::runtime_error("Schema version mismatch."));
+ }
+
+ String instanceName = GetInstanceName();
+
+ IncreasePendingQueries(1);
+ result = Query("SELECT instance_id FROM " + GetTablePrefix() + "instances WHERE instance_name = '" + Escape(instanceName) + "'");
+ row = FetchRow(result, 0);
+
+ if (!row) {
+ IncreasePendingQueries(1);
+ Query("INSERT INTO " + GetTablePrefix() + "instances (instance_name, instance_description) VALUES ('" + Escape(instanceName) + "', '" + Escape(GetInstanceDescription()) + "')");
+ m_InstanceID = GetSequenceValue(GetTablePrefix() + "instances", "instance_id");
+ } else {
+ m_InstanceID = DbReference(row->Get("instance_id"));
+ }
+
+ Endpoint::Ptr my_endpoint = Endpoint::GetLocalEndpoint();
+
+ /* we have an endpoint in a cluster setup, so decide if we can proceed here */
+ if (my_endpoint && GetHAMode() == HARunOnce) {
+ /* get the current endpoint writing to programstatus table */
+ IncreasePendingQueries(1);
+ result = Query("SELECT UNIX_TIMESTAMP(status_update_time) AS status_update_time, endpoint_name FROM " +
+ GetTablePrefix() + "programstatus WHERE instance_id = " + Convert::ToString(m_InstanceID));
+ row = FetchRow(result, 0);
+
+ String endpoint_name;
+
+ if (row)
+ endpoint_name = row->Get("endpoint_name");
+ else
+ Log(LogNotice, "IdoPgsqlConnection", "Empty program status table");
+
+ /* if we did not write into the database earlier, another instance is active */
+ if (endpoint_name != my_endpoint->GetName()) {
+ double status_update_time;
+
+ if (row)
+ status_update_time = row->Get("status_update_time");
+ else
+ status_update_time = 0;
+
+ double now = Utility::GetTime();
+
+ double status_update_age = now - status_update_time;
+ double failoverTimeout = GetFailoverTimeout();
+
+ if (status_update_age < GetFailoverTimeout()) {
+ Log(LogInformation, "IdoPgsqlConnection")
+ << "Last update by endpoint '" << endpoint_name << "' was "
+ << status_update_age << "s ago (< failover timeout of " << failoverTimeout << "s). Retrying.";
+
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+ SetShouldConnect(false);
+
+ return;
+ }
+
+ /* activate the IDO only, if we're authoritative in this zone */
+ if (IsPaused()) {
+ Log(LogNotice, "IdoPgsqlConnection")
+ << "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out.";
+
+ m_Pgsql->finish(m_Connection);
+ SetConnected(false);
+
+ return;
+ }
+
+ SetLastFailover(now);
+
+ Log(LogInformation, "IdoPgsqlConnection")
+ << "Last update by endpoint '" << endpoint_name << "' was "
+ << status_update_age << "s ago. Taking over '" << GetName() << "' in HA zone '" << Zone::GetLocalZone()->GetName() << "'.";
+ }
+
+ Log(LogNotice, "IdoPgsqlConnection", "Enabling IDO connection.");
+ }
+
+ Log(LogInformation, "IdoPgsqlConnection")
+ << "PGSQL IDO instance id: " << static_cast<long>(m_InstanceID) << " (schema version: '" + version + "')"
+ << (!sslMode.IsEmpty() ? ", sslmode='" + sslMode + "'" : "");
+
+ IncreasePendingQueries(1);
+ Query("BEGIN");
+
+ /* update programstatus table */
+ UpdateProgramStatus();
+
+ /* record connection */
+ IncreasePendingQueries(1);
+ Query("INSERT INTO " + GetTablePrefix() + "conninfo " +
+ "(instance_id, connect_time, last_checkin_time, agent_name, agent_version, connect_type, data_start_time) VALUES ("
+ + Convert::ToString(static_cast<long>(m_InstanceID)) + ", NOW(), NOW(), 'icinga2 db_ido_pgsql', '" + Escape(Application::GetAppVersion())
+ + "', '" + (reconnect ? "RECONNECT" : "INITIAL") + "', NOW())");
+
+ /* clear config tables for the initial config dump */
+ PrepareDatabase();
+
+ std::ostringstream q1buf;
+ q1buf << "SELECT object_id, objecttype_id, name1, name2, is_active FROM " + GetTablePrefix() + "objects WHERE instance_id = " << static_cast<long>(m_InstanceID);
+ IncreasePendingQueries(1);
+ result = Query(q1buf.str());
+
+ std::vector<DbObject::Ptr> activeDbObjs;
+
+ int index = 0;
+ while ((row = FetchRow(result, index))) {
+ index++;
+
+ DbType::Ptr dbtype = DbType::GetByID(row->Get("objecttype_id"));
+
+ if (!dbtype)
+ continue;
+
+ DbObject::Ptr dbobj = dbtype->GetOrCreateObjectByName(row->Get("name1"), row->Get("name2"));
+ SetObjectID(dbobj, DbReference(row->Get("object_id")));
+ bool active = row->Get("is_active");
+ SetObjectActive(dbobj, active);
+
+ if (active)
+ activeDbObjs.push_back(dbobj);
+ }
+
+ SetIDCacheValid(true);
+
+ EnableActiveChangedHandler();
+
+ for (const DbObject::Ptr& dbobj : activeDbObjs) {
+ if (dbobj->GetObject())
+ continue;
+
+ Log(LogNotice, "IdoPgsqlConnection")
+ << "Deactivate deleted object name1: '" << dbobj->GetName1()
+ << "' name2: '" << dbobj->GetName2() + "'.";
+ DeactivateObject(dbobj);
+ }
+
+ UpdateAllObjects();
+
+ m_QueryQueue.Enqueue([this]() { ClearTablesBySession(); }, PriorityNormal);
+
+ m_QueryQueue.Enqueue([this, startTime]() { FinishConnect(startTime); }, PriorityNormal);
+}
+
+void IdoPgsqlConnection::FinishConnect(double startTime)
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected())
+ return;
+
+ Log(LogInformation, "IdoPgsqlConnection")
+ << "Finished reconnecting to '" << GetName() << "' database '" << GetDatabase() << "' in "
+ << std::setw(2) << Utility::GetTime() - startTime << " second(s).";
+
+ IncreasePendingQueries(2);
+ Query("COMMIT");
+ Query("BEGIN");
+}
+
+void IdoPgsqlConnection::ClearTablesBySession()
+{
+ /* delete all comments and downtimes without current session token */
+ ClearTableBySession("comments");
+ ClearTableBySession("scheduleddowntime");
+}
+
+void IdoPgsqlConnection::ClearTableBySession(const String& table)
+{
+ IncreasePendingQueries(1);
+ Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
+ Convert::ToString(static_cast<long>(m_InstanceID)) + " AND session_token <> " +
+ Convert::ToString(GetSessionToken()));
+}
+
+IdoPgsqlResult IdoPgsqlConnection::Query(const String& query)
+{
+ AssertOnWorkQueue();
+
+ Defer decreaseQueries ([this]() { DecreasePendingQueries(1); });
+
+ Log(LogDebug, "IdoPgsqlConnection")
+ << "Query: " << query;
+
+ IncreaseQueryCount();
+
+ PGresult *result = m_Pgsql->exec(m_Connection, query.CStr());
+
+ if (!result) {
+ String message = m_Pgsql->errorMessage(m_Connection);
+ Log(LogCritical, "IdoPgsqlConnection")
+ << "Error \"" << message << "\" when executing query \"" << query << "\"";
+
+ BOOST_THROW_EXCEPTION(
+ database_error()
+ << errinfo_message(message)
+ << errinfo_database_query(query)
+ );
+ }
+
+ char *rowCount = m_Pgsql->cmdTuples(result);
+ m_AffectedRows = atoi(rowCount);
+
+ if (m_Pgsql->resultStatus(result) == PGRES_COMMAND_OK) {
+ m_Pgsql->clear(result);
+ return IdoPgsqlResult();
+ }
+
+ if (m_Pgsql->resultStatus(result) != PGRES_TUPLES_OK) {
+ String message = m_Pgsql->resultErrorMessage(result);
+ m_Pgsql->clear(result);
+
+ Log(LogCritical, "IdoPgsqlConnection")
+ << "Error \"" << message << "\" when executing query \"" << query << "\"";
+
+ BOOST_THROW_EXCEPTION(
+ database_error()
+ << errinfo_message(message)
+ << errinfo_database_query(query)
+ );
+ }
+
+ return IdoPgsqlResult(result, [this](PGresult* result) { m_Pgsql->clear(result); });
+}
+
+DbReference IdoPgsqlConnection::GetSequenceValue(const String& table, const String& column)
+{
+ AssertOnWorkQueue();
+
+ IncreasePendingQueries(1);
+ IdoPgsqlResult result = Query("SELECT CURRVAL(pg_get_serial_sequence('" + Escape(table) + "', '" + Escape(column) + "')) AS id");
+
+ Dictionary::Ptr row = FetchRow(result, 0);
+
+ ASSERT(row);
+
+ Log(LogDebug, "IdoPgsqlConnection")
+ << "Sequence Value: " << row->Get("id");
+
+ return {Convert::ToLong(row->Get("id"))};
+}
+
+int IdoPgsqlConnection::GetAffectedRows()
+{
+ AssertOnWorkQueue();
+
+ return m_AffectedRows;
+}
+
+String IdoPgsqlConnection::Escape(const String& s)
+{
+ AssertOnWorkQueue();
+
+ String utf8s = Utility::ValidateUTF8(s);
+
+ size_t length = utf8s.GetLength();
+ auto *to = new char[utf8s.GetLength() * 2 + 1];
+
+ m_Pgsql->escapeStringConn(m_Connection, to, utf8s.CStr(), length, nullptr);
+
+ String result = String(to);
+
+ delete [] to;
+
+ return result;
+}
+
+Dictionary::Ptr IdoPgsqlConnection::FetchRow(const IdoPgsqlResult& result, int row)
+{
+ AssertOnWorkQueue();
+
+ if (row >= m_Pgsql->ntuples(result.get()))
+ return nullptr;
+
+ int columns = m_Pgsql->nfields(result.get());
+
+ DictionaryData dict;
+
+ for (int column = 0; column < columns; column++) {
+ Value value;
+
+ if (!m_Pgsql->getisnull(result.get(), row, column))
+ value = m_Pgsql->getvalue(result.get(), row, column);
+
+ dict.emplace_back(m_Pgsql->fname(result.get(), column), value);
+ }
+
+ return new Dictionary(std::move(dict));
+}
+
+void IdoPgsqlConnection::ActivateObject(const DbObject::Ptr& dbobj)
+{
+ if (IsPaused())
+ return;
+
+ m_QueryQueue.Enqueue([this, dbobj]() { InternalActivateObject(dbobj); }, PriorityNormal);
+}
+
+void IdoPgsqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj)
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected())
+ return;
+
+ DbReference dbref = GetObjectID(dbobj);
+ std::ostringstream qbuf;
+
+ if (!dbref.IsValid()) {
+ if (!dbobj->GetName2().IsEmpty()) {
+ qbuf << "INSERT INTO " + GetTablePrefix() + "objects (instance_id, objecttype_id, name1, name2, is_active) VALUES ("
+ << static_cast<long>(m_InstanceID) << ", " << dbobj->GetType()->GetTypeID() << ", "
+ << "'" << Escape(dbobj->GetName1()) << "', '" << Escape(dbobj->GetName2()) << "', 1)";
+ } else {
+ qbuf << "INSERT INTO " + GetTablePrefix() + "objects (instance_id, objecttype_id, name1, is_active) VALUES ("
+ << static_cast<long>(m_InstanceID) << ", " << dbobj->GetType()->GetTypeID() << ", "
+ << "'" << Escape(dbobj->GetName1()) << "', 1)";
+ }
+
+ IncreasePendingQueries(1);
+ Query(qbuf.str());
+ SetObjectID(dbobj, GetSequenceValue(GetTablePrefix() + "objects", "object_id"));
+ } else {
+ qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 1 WHERE object_id = " << static_cast<long>(dbref);
+ IncreasePendingQueries(1);
+ Query(qbuf.str());
+ }
+}
+
+void IdoPgsqlConnection::DeactivateObject(const DbObject::Ptr& dbobj)
+{
+ if (IsPaused())
+ return;
+
+ m_QueryQueue.Enqueue([this, dbobj]() { InternalDeactivateObject(dbobj); }, PriorityNormal);
+}
+
+void IdoPgsqlConnection::InternalDeactivateObject(const DbObject::Ptr& dbobj)
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected())
+ return;
+
+ DbReference dbref = GetObjectID(dbobj);
+
+ if (!dbref.IsValid())
+ return;
+
+ std::ostringstream qbuf;
+ qbuf << "UPDATE " + GetTablePrefix() + "objects SET is_active = 0 WHERE object_id = " << static_cast<long>(dbref);
+ IncreasePendingQueries(1);
+ Query(qbuf.str());
+
+ /* Note that we're _NOT_ clearing the db refs via SetReference/SetConfigUpdate/SetStatusUpdate
+ * because the object is still in the database. */
+
+ SetObjectActive(dbobj, false);
+}
+
+bool IdoPgsqlConnection::FieldToEscapedString(const String& key, const Value& value, Value *result)
+{
+ if (key == "instance_id") {
+ *result = static_cast<long>(m_InstanceID);
+ return true;
+ } else if (key == "session_token") {
+ *result = GetSessionToken();
+ return true;
+ }
+
+ Value rawvalue = DbValue::ExtractValue(value);
+
+ if (rawvalue.GetType() == ValueEmpty) {
+ *result = "NULL";
+ } else if (rawvalue.IsObjectType<ConfigObject>()) {
+ DbObject::Ptr dbobjcol = DbObject::GetOrCreateByObject(rawvalue);
+
+ if (!dbobjcol) {
+ *result = 0;
+ return true;
+ }
+
+ if (!IsIDCacheValid())
+ return false;
+
+ DbReference dbrefcol;
+
+ if (DbValue::IsObjectInsertID(value)) {
+ dbrefcol = GetInsertID(dbobjcol);
+
+ if (!dbrefcol.IsValid())
+ return false;
+ } else {
+ dbrefcol = GetObjectID(dbobjcol);
+
+ if (!dbrefcol.IsValid()) {
+ InternalActivateObject(dbobjcol);
+
+ dbrefcol = GetObjectID(dbobjcol);
+
+ if (!dbrefcol.IsValid())
+ return false;
+ }
+ }
+
+ *result = static_cast<long>(dbrefcol);
+ } else if (DbValue::IsTimestamp(value)) {
+ long ts = rawvalue;
+ std::ostringstream msgbuf;
+ msgbuf << "TO_TIMESTAMP(" << ts << ") AT TIME ZONE 'UTC'";
+ *result = Value(msgbuf.str());
+ } else if (DbValue::IsObjectInsertID(value)) {
+ auto id = static_cast<long>(rawvalue);
+
+ if (id <= 0)
+ return false;
+
+ *result = id;
+ return true;
+ } else {
+ Value fvalue;
+
+ if (rawvalue.IsBoolean())
+ fvalue = Convert::ToLong(rawvalue);
+ else
+ fvalue = rawvalue;
+
+ *result = "'" + Escape(fvalue) + "'";
+ }
+
+ return true;
+}
+
+void IdoPgsqlConnection::ExecuteQuery(const DbQuery& query)
+{
+ if (IsPaused() && GetPauseCalled())
+ return;
+
+ ASSERT(query.Category != DbCatInvalid);
+
+ IncreasePendingQueries(1);
+ m_QueryQueue.Enqueue([this, query]() { InternalExecuteQuery(query, -1); }, query.Priority, true);
+}
+
+void IdoPgsqlConnection::ExecuteMultipleQueries(const std::vector<DbQuery>& queries)
+{
+ if (IsPaused())
+ return;
+
+ if (queries.empty())
+ return;
+
+ IncreasePendingQueries(queries.size());
+ m_QueryQueue.Enqueue([this, queries]() { InternalExecuteMultipleQueries(queries); }, queries[0].Priority, true);
+}
+
+bool IdoPgsqlConnection::CanExecuteQuery(const DbQuery& query)
+{
+ if (query.Object && !IsIDCacheValid())
+ return false;
+
+ if (query.WhereCriteria) {
+ ObjectLock olock(query.WhereCriteria);
+ Value value;
+
+ for (const Dictionary::Pair& kv : query.WhereCriteria) {
+ if (!FieldToEscapedString(kv.first, kv.second, &value))
+ return false;
+ }
+ }
+
+ if (query.Fields) {
+ ObjectLock olock(query.Fields);
+
+ for (const Dictionary::Pair& kv : query.Fields) {
+ Value value;
+
+ if (!FieldToEscapedString(kv.first, kv.second, &value))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void IdoPgsqlConnection::InternalExecuteMultipleQueries(const std::vector<DbQuery>& queries)
+{
+ AssertOnWorkQueue();
+
+ if (IsPaused()) {
+ DecreasePendingQueries(queries.size());
+ return;
+ }
+
+ if (!GetConnected()) {
+ DecreasePendingQueries(queries.size());
+ return;
+ }
+
+ for (const DbQuery& query : queries) {
+ ASSERT(query.Type == DbQueryNewTransaction || query.Category != DbCatInvalid);
+
+ if (!CanExecuteQuery(query)) {
+ m_QueryQueue.Enqueue([this, queries]() { InternalExecuteMultipleQueries(queries); }, query.Priority);
+ return;
+ }
+ }
+
+ for (const DbQuery& query : queries) {
+ InternalExecuteQuery(query);
+ }
+}
+
+void IdoPgsqlConnection::InternalExecuteQuery(const DbQuery& query, int typeOverride)
+{
+ AssertOnWorkQueue();
+
+ if (IsPaused() && GetPauseCalled()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ if (!GetConnected()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ if (query.Type == DbQueryNewTransaction) {
+ DecreasePendingQueries(1);
+ InternalNewTransaction();
+ return;
+ }
+
+ /* check whether we're allowed to execute the query first */
+ if (GetCategoryFilter() != DbCatEverything && (query.Category & GetCategoryFilter()) == 0) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ if (query.Object && query.Object->GetObject()->GetExtension("agent_check").ToBool()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ /* check if there are missing object/insert ids and re-enqueue the query */
+ if (!CanExecuteQuery(query)) {
+ m_QueryQueue.Enqueue([this, query, typeOverride]() { InternalExecuteQuery(query, typeOverride); }, query.Priority);
+ return;
+ }
+
+ std::ostringstream qbuf, where;
+ int type;
+
+ if (query.WhereCriteria) {
+ where << " WHERE ";
+
+ ObjectLock olock(query.WhereCriteria);
+ Value value;
+ bool first = true;
+
+ for (const Dictionary::Pair& kv : query.WhereCriteria) {
+ if (!FieldToEscapedString(kv.first, kv.second, &value)) {
+ m_QueryQueue.Enqueue([this, query]() { InternalExecuteQuery(query, -1); }, query.Priority);
+ return;
+ }
+
+ if (!first)
+ where << " AND ";
+
+ where << kv.first << " = " << value;
+
+ if (first)
+ first = false;
+ }
+ }
+
+ type = (typeOverride != -1) ? typeOverride : query.Type;
+
+ bool upsert = false;
+
+ if ((type & DbQueryInsert) && (type & DbQueryUpdate)) {
+ bool hasid = false;
+
+ if (query.Object) {
+ if (query.ConfigUpdate)
+ hasid = GetConfigUpdate(query.Object);
+ else if (query.StatusUpdate)
+ hasid = GetStatusUpdate(query.Object);
+ }
+
+ if (!hasid)
+ upsert = true;
+
+ type = DbQueryUpdate;
+ }
+
+ if ((type & DbQueryInsert) && (type & DbQueryDelete)) {
+ std::ostringstream qdel;
+ qdel << "DELETE FROM " << GetTablePrefix() << query.Table << where.str();
+ IncreasePendingQueries(1);
+ Query(qdel.str());
+
+ type = DbQueryInsert;
+ }
+
+ switch (type) {
+ case DbQueryInsert:
+ qbuf << "INSERT INTO " << GetTablePrefix() << query.Table;
+ break;
+ case DbQueryUpdate:
+ qbuf << "UPDATE " << GetTablePrefix() << query.Table << " SET";
+ break;
+ case DbQueryDelete:
+ qbuf << "DELETE FROM " << GetTablePrefix() << query.Table;
+ break;
+ default:
+ VERIFY(!"Invalid query type.");
+ }
+
+ if (type == DbQueryInsert || type == DbQueryUpdate) {
+ std::ostringstream colbuf, valbuf;
+
+ if (type == DbQueryUpdate && query.Fields->GetLength() == 0)
+ return;
+
+ ObjectLock olock(query.Fields);
+
+ Value value;
+ bool first = true;
+ for (const Dictionary::Pair& kv : query.Fields) {
+ if (!FieldToEscapedString(kv.first, kv.second, &value)) {
+ m_QueryQueue.Enqueue([this, query]() { InternalExecuteQuery(query, -1); }, query.Priority);
+ return;
+ }
+
+ if (type == DbQueryInsert) {
+ if (!first) {
+ colbuf << ", ";
+ valbuf << ", ";
+ }
+
+ colbuf << kv.first;
+ valbuf << value;
+ } else {
+ if (!first)
+ qbuf << ", ";
+
+ qbuf << " " << kv.first << " = " << value;
+ }
+
+ if (first)
+ first = false;
+ }
+
+ if (type == DbQueryInsert)
+ qbuf << " (" << colbuf.str() << ") VALUES (" << valbuf.str() << ")";
+ }
+
+ if (type != DbQueryInsert)
+ qbuf << where.str();
+
+ Query(qbuf.str());
+
+ if (upsert && GetAffectedRows() == 0) {
+ IncreasePendingQueries(1);
+ InternalExecuteQuery(query, DbQueryDelete | DbQueryInsert);
+
+ return;
+ }
+
+ if (type == DbQueryInsert && query.Object) {
+ if (query.ConfigUpdate) {
+ String idField = query.IdColumn;
+
+ if (idField.IsEmpty())
+ idField = query.Table.SubStr(0, query.Table.GetLength() - 1) + "_id";
+
+ SetInsertID(query.Object, GetSequenceValue(GetTablePrefix() + query.Table, idField));
+
+ SetConfigUpdate(query.Object, true);
+ } else if (query.StatusUpdate)
+ SetStatusUpdate(query.Object, true);
+ }
+
+ if (type == DbQueryInsert && query.Table == "notifications" && query.NotificationInsertID) {
+ DbReference seqval = GetSequenceValue(GetTablePrefix() + query.Table, "notification_id");
+ query.NotificationInsertID->SetValue(static_cast<long>(seqval));
+ }
+}
+
+void IdoPgsqlConnection::CleanUpExecuteQuery(const String& table, const String& time_column, double max_age)
+{
+ if (IsPaused())
+ return;
+
+ IncreasePendingQueries(1);
+ m_QueryQueue.Enqueue([this, table, time_column, max_age]() { InternalCleanUpExecuteQuery(table, time_column, max_age); }, PriorityLow, true);
+}
+
+void IdoPgsqlConnection::InternalCleanUpExecuteQuery(const String& table, const String& time_column, double max_age)
+{
+ AssertOnWorkQueue();
+
+ if (!GetConnected()) {
+ DecreasePendingQueries(1);
+ return;
+ }
+
+ Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +
+ Convert::ToString(static_cast<long>(m_InstanceID)) + " AND " + time_column +
+ " < TO_TIMESTAMP(" + Convert::ToString(static_cast<long>(max_age)) + ") AT TIME ZONE 'UTC'");
+}
+
+void IdoPgsqlConnection::FillIDCache(const DbType::Ptr& type)
+{
+ String query = "SELECT " + type->GetIDColumn() + " AS object_id, " + type->GetTable() + "_id, config_hash FROM " + GetTablePrefix() + type->GetTable() + "s";
+ IncreasePendingQueries(1);
+ IdoPgsqlResult result = Query(query);
+
+ Dictionary::Ptr row;
+
+ int index = 0;
+ while ((row = FetchRow(result, index))) {
+ index++;
+ DbReference dbref(row->Get("object_id"));
+ SetInsertID(type, dbref, DbReference(row->Get(type->GetTable() + "_id")));
+ SetConfigHash(type, dbref, row->Get("config_hash"));
+ }
+}
+
+int IdoPgsqlConnection::GetPendingQueryCount() const
+{
+ return m_QueryQueue.GetLength();
+}
diff --git a/lib/db_ido_pgsql/idopgsqlconnection.hpp b/lib/db_ido_pgsql/idopgsqlconnection.hpp
new file mode 100644
index 0000000..dc06a93
--- /dev/null
+++ b/lib/db_ido_pgsql/idopgsqlconnection.hpp
@@ -0,0 +1,99 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#ifndef IDOPGSQLCONNECTION_H
+#define IDOPGSQLCONNECTION_H
+
+#include "db_ido_pgsql/idopgsqlconnection-ti.hpp"
+#include "pgsql_shim/pgsqlinterface.hpp"
+#include "base/array.hpp"
+#include "base/timer.hpp"
+#include "base/workqueue.hpp"
+#include "base/library.hpp"
+
+namespace icinga
+{
+
+typedef std::shared_ptr<PGresult> IdoPgsqlResult;
+
+/**
+ * An IDO pgSQL database connection.
+ *
+ * @ingroup ido
+ */
+class IdoPgsqlConnection final : public ObjectImpl<IdoPgsqlConnection>
+{
+public:
+ DECLARE_OBJECT(IdoPgsqlConnection);
+ DECLARE_OBJECTNAME(IdoPgsqlConnection);
+
+ IdoPgsqlConnection();
+
+ static void StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata);
+
+ const char * GetLatestSchemaVersion() const noexcept override;
+ const char * GetCompatSchemaVersion() const noexcept override;
+
+ int GetPendingQueryCount() const override;
+
+protected:
+ void OnConfigLoaded() override;
+ void Resume() override;
+ void Pause() override;
+
+ void ActivateObject(const DbObject::Ptr& dbobj) override;
+ void DeactivateObject(const DbObject::Ptr& dbobj) override;
+ void ExecuteQuery(const DbQuery& query) override;
+ void ExecuteMultipleQueries(const std::vector<DbQuery>& queries) override;
+ void CleanUpExecuteQuery(const String& table, const String& time_key, double time_value) override;
+ void FillIDCache(const DbType::Ptr& type) override;
+ void NewTransaction() override;
+ void Disconnect() override;
+
+private:
+ DbReference m_InstanceID;
+
+ Library m_Library;
+ std::unique_ptr<PgsqlInterface, PgsqlInterfaceDeleter> m_Pgsql;
+
+ PGconn *m_Connection;
+ int m_AffectedRows;
+
+ Timer::Ptr m_ReconnectTimer;
+ Timer::Ptr m_TxTimer;
+
+ IdoPgsqlResult Query(const String& query);
+ DbReference GetSequenceValue(const String& table, const String& column);
+ int GetAffectedRows();
+ String Escape(const String& s);
+ Dictionary::Ptr FetchRow(const IdoPgsqlResult& result, int row);
+
+ bool FieldToEscapedString(const String& key, const Value& value, Value *result);
+ void InternalActivateObject(const DbObject::Ptr& dbobj);
+ void InternalDeactivateObject(const DbObject::Ptr& dbobj);
+
+ void InternalNewTransaction();
+ void Reconnect();
+
+ void AssertOnWorkQueue();
+
+ void ReconnectTimerHandler();
+
+ void StatsLoggerTimerHandler();
+
+ bool CanExecuteQuery(const DbQuery& query);
+
+ void InternalExecuteQuery(const DbQuery& query, int typeOverride = -1);
+ void InternalExecuteMultipleQueries(const std::vector<DbQuery>& queries);
+ void InternalCleanUpExecuteQuery(const String& table, const String& time_key, double time_value);
+
+ void ClearTableBySession(const String& table);
+ void ClearTablesBySession();
+
+ void ExceptionHandler(boost::exception_ptr exp);
+
+ void FinishConnect(double startTime);
+};
+
+}
+
+#endif /* IDOPGSQLCONNECTION_H */
diff --git a/lib/db_ido_pgsql/idopgsqlconnection.ti b/lib/db_ido_pgsql/idopgsqlconnection.ti
new file mode 100644
index 0000000..bc4deff
--- /dev/null
+++ b/lib/db_ido_pgsql/idopgsqlconnection.ti
@@ -0,0 +1,39 @@
+/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
+
+#include "db_ido/dbconnection.hpp"
+
+library db_ido_pgsql;
+
+namespace icinga
+{
+
+class IdoPgsqlConnection : DbConnection
+{
+ activation_priority 100;
+
+ [config] String host {
+ default {{{ return "localhost"; }}}
+ };
+ [config] String port {
+ default {{{ return "5432"; }}}
+ };
+ [config] String user {
+ default {{{ return "icinga"; }}}
+ };
+ [config, no_user_view, no_user_modify] String password {
+ default {{{ return "icinga"; }}}
+ };
+ [config] String database {
+ default {{{ return "icinga"; }}}
+ };
+ [config] String instance_name {
+ default {{{ return "default"; }}}
+ };
+ [config] String instance_description;
+ [config] String ssl_mode;
+ [config] String ssl_key;
+ [config] String ssl_cert;
+ [config] String ssl_ca;
+};
+
+}
diff --git a/lib/db_ido_pgsql/schema/pgsql.sql b/lib/db_ido_pgsql/schema/pgsql.sql
new file mode 100644
index 0000000..242b6db
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/pgsql.sql
@@ -0,0 +1,1733 @@
+-- --------------------------------------------------------
+-- pgsql.sql
+-- DB definition for IDO Postgresql
+--
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- --------------------------------------------------------
+
+--
+-- Functions
+--
+
+DROP FUNCTION IF EXISTS from_unixtime(bigint);
+CREATE FUNCTION from_unixtime(bigint) RETURNS timestamp AS $$
+ SELECT to_timestamp($1) AT TIME ZONE 'UTC' AS result
+$$ LANGUAGE sql;
+
+DROP FUNCTION IF EXISTS unix_timestamp(timestamp WITH TIME ZONE);
+CREATE OR REPLACE FUNCTION unix_timestamp(timestamp) RETURNS bigint AS '
+ SELECT CAST(EXTRACT(EPOCH FROM $1) AS bigint) AS result;
+' LANGUAGE sql;
+
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+
+CREATE OR REPLACE FUNCTION updatedbversion(version_i TEXT) RETURNS void AS $$
+BEGIN
+ IF EXISTS( SELECT * FROM icinga_dbversion WHERE name='idoutils')
+ THEN
+ UPDATE icinga_dbversion
+ SET version=version_i, modify_time=NOW()
+ WHERE name='idoutils';
+ ELSE
+ INSERT INTO icinga_dbversion (dbversion_id, name, version, create_time, modify_time) VALUES ('1', 'idoutils', version_i, NOW(), NOW());
+ END IF;
+
+ RETURN;
+END;
+$$ LANGUAGE plpgsql;
+-- HINT: su - postgres; createlang plpgsql icinga;
+
+
+
+--
+-- Database: icinga
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_acknowledgements
+--
+
+CREATE TABLE icinga_acknowledgements (
+ acknowledgement_id bigserial,
+ instance_id bigint default 0,
+ entry_time timestamp,
+ entry_time_usec INTEGER default 0,
+ acknowledgement_type INTEGER default 0,
+ object_id bigint default 0,
+ state INTEGER default 0,
+ author_name TEXT default '',
+ comment_data TEXT default '',
+ is_sticky INTEGER default 0,
+ persistent_comment INTEGER default 0,
+ notify_contacts INTEGER default 0,
+ end_time timestamp,
+ CONSTRAINT PK_acknowledgement_id PRIMARY KEY (acknowledgement_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_commands
+--
+
+CREATE TABLE icinga_commands (
+ command_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ object_id bigint default 0,
+ command_line TEXT default '',
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_command_id PRIMARY KEY (command_id) ,
+ CONSTRAINT UQ_commands UNIQUE (instance_id,object_id,config_type)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_commenthistory
+--
+
+CREATE TABLE icinga_commenthistory (
+ commenthistory_id bigserial,
+ instance_id bigint default 0,
+ entry_time timestamp,
+ entry_time_usec INTEGER default 0,
+ comment_type INTEGER default 0,
+ entry_type INTEGER default 0,
+ object_id bigint default 0,
+ comment_time timestamp,
+ internal_comment_id bigint default 0,
+ author_name TEXT default '',
+ comment_data TEXT default '',
+ is_persistent INTEGER default 0,
+ comment_source INTEGER default 0,
+ expires INTEGER default 0,
+ expiration_time timestamp,
+ deletion_time timestamp,
+ deletion_time_usec INTEGER default 0,
+ name TEXT default NULL,
+ CONSTRAINT PK_commenthistory_id PRIMARY KEY (commenthistory_id)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_comments
+--
+
+CREATE TABLE icinga_comments (
+ comment_id bigserial,
+ instance_id bigint default 0,
+ entry_time timestamp,
+ entry_time_usec INTEGER default 0,
+ comment_type INTEGER default 0,
+ entry_type INTEGER default 0,
+ object_id bigint default 0,
+ comment_time timestamp,
+ internal_comment_id bigint default 0,
+ author_name TEXT default '',
+ comment_data TEXT default '',
+ is_persistent INTEGER default 0,
+ comment_source INTEGER default 0,
+ expires INTEGER default 0,
+ expiration_time timestamp,
+ name TEXT default NULL,
+ session_token INTEGER default NULL,
+ CONSTRAINT PK_comment_id PRIMARY KEY (comment_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_configfiles
+--
+
+CREATE TABLE icinga_configfiles (
+ configfile_id bigserial,
+ instance_id bigint default 0,
+ configfile_type INTEGER default 0,
+ configfile_path TEXT default '',
+ CONSTRAINT PK_configfile_id PRIMARY KEY (configfile_id) ,
+ CONSTRAINT UQ_configfiles UNIQUE (instance_id,configfile_type,configfile_path)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_configfilevariables
+--
+
+CREATE TABLE icinga_configfilevariables (
+ configfilevariable_id bigserial,
+ instance_id bigint default 0,
+ configfile_id bigint default 0,
+ varname TEXT default '',
+ varvalue TEXT default '',
+ CONSTRAINT PK_configfilevariable_id PRIMARY KEY (configfilevariable_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_conninfo
+--
+
+CREATE TABLE icinga_conninfo (
+ conninfo_id bigserial,
+ instance_id bigint default 0,
+ agent_name TEXT default '',
+ agent_version TEXT default '',
+ disposition TEXT default '',
+ connect_source TEXT default '',
+ connect_type TEXT default '',
+ connect_time timestamp,
+ disconnect_time timestamp,
+ last_checkin_time timestamp,
+ data_start_time timestamp,
+ data_end_time timestamp,
+ bytes_processed bigint default 0,
+ lines_processed bigint default 0,
+ entries_processed bigint default 0,
+ CONSTRAINT PK_conninfo_id PRIMARY KEY (conninfo_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactgroups
+--
+
+CREATE TABLE icinga_contactgroups (
+ contactgroup_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ contactgroup_object_id bigint default 0,
+ alias TEXT default '',
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_contactgroup_id PRIMARY KEY (contactgroup_id) ,
+ CONSTRAINT UQ_contactgroups UNIQUE (instance_id,config_type,contactgroup_object_id)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactgroup_members
+--
+
+CREATE TABLE icinga_contactgroup_members (
+ contactgroup_member_id bigserial,
+ instance_id bigint default 0,
+ contactgroup_id bigint default 0,
+ contact_object_id bigint default 0,
+ session_token INTEGER default NULL,
+ CONSTRAINT PK_contactgroup_member_id PRIMARY KEY (contactgroup_member_id)
+);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactnotificationmethods
+--
+
+CREATE TABLE icinga_contactnotificationmethods (
+ contactnotificationmethod_id bigserial,
+ instance_id bigint default 0,
+ contactnotification_id bigint default 0,
+ start_time timestamp,
+ start_time_usec INTEGER default 0,
+ end_time timestamp,
+ end_time_usec INTEGER default 0,
+ command_object_id bigint default 0,
+ command_args TEXT default '',
+ CONSTRAINT PK_contactnotificationmethod_id PRIMARY KEY (contactnotificationmethod_id) ,
+ CONSTRAINT UQ_contactnotificationmethods UNIQUE (instance_id,contactnotification_id,start_time,start_time_usec)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactnotifications
+--
+
+CREATE TABLE icinga_contactnotifications (
+ contactnotification_id bigserial,
+ instance_id bigint default 0,
+ notification_id bigint default 0,
+ contact_object_id bigint default 0,
+ start_time timestamp,
+ start_time_usec INTEGER default 0,
+ end_time timestamp,
+ end_time_usec INTEGER default 0,
+ CONSTRAINT PK_contactnotification_id PRIMARY KEY (contactnotification_id) ,
+ CONSTRAINT UQ_contactnotifications UNIQUE (instance_id,contact_object_id,start_time,start_time_usec)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contacts
+--
+
+CREATE TABLE icinga_contacts (
+ contact_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ contact_object_id bigint default 0,
+ alias TEXT default '',
+ email_address TEXT default '',
+ pager_address TEXT default '',
+ host_timeperiod_object_id bigint default 0,
+ service_timeperiod_object_id bigint default 0,
+ host_notifications_enabled INTEGER default 0,
+ service_notifications_enabled INTEGER default 0,
+ can_submit_commands INTEGER default 0,
+ notify_service_recovery INTEGER default 0,
+ notify_service_warning INTEGER default 0,
+ notify_service_unknown INTEGER default 0,
+ notify_service_critical INTEGER default 0,
+ notify_service_flapping INTEGER default 0,
+ notify_service_downtime INTEGER default 0,
+ notify_host_recovery INTEGER default 0,
+ notify_host_down INTEGER default 0,
+ notify_host_unreachable INTEGER default 0,
+ notify_host_flapping INTEGER default 0,
+ notify_host_downtime INTEGER default 0,
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_contact_id PRIMARY KEY (contact_id) ,
+ CONSTRAINT UQ_contacts UNIQUE (instance_id,config_type,contact_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contactstatus
+--
+
+CREATE TABLE icinga_contactstatus (
+ contactstatus_id bigserial,
+ instance_id bigint default 0,
+ contact_object_id bigint default 0,
+ status_update_time timestamp,
+ host_notifications_enabled INTEGER default 0,
+ service_notifications_enabled INTEGER default 0,
+ last_host_notification timestamp,
+ last_service_notification timestamp,
+ modified_attributes INTEGER default 0,
+ modified_host_attributes INTEGER default 0,
+ modified_service_attributes INTEGER default 0,
+ CONSTRAINT PK_contactstatus_id PRIMARY KEY (contactstatus_id) ,
+ CONSTRAINT UQ_contactstatus UNIQUE (contact_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contact_addresses
+--
+
+CREATE TABLE icinga_contact_addresses (
+ contact_address_id bigserial,
+ instance_id bigint default 0,
+ contact_id bigint default 0,
+ address_number INTEGER default 0,
+ address TEXT default '',
+ CONSTRAINT PK_contact_address_id PRIMARY KEY (contact_address_id) ,
+ CONSTRAINT UQ_contact_addresses UNIQUE (contact_id,address_number)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_contact_notificationcommands
+--
+
+CREATE TABLE icinga_contact_notificationcommands (
+ contact_notificationcommand_id bigserial,
+ instance_id bigint default 0,
+ contact_id bigint default 0,
+ notification_type INTEGER default 0,
+ command_object_id bigint default 0,
+ command_args TEXT default '',
+ CONSTRAINT PK_contact_notificationcommand_id PRIMARY KEY (contact_notificationcommand_id) ,
+ CONSTRAINT UQ_contact_notificationcommands UNIQUE (contact_id,notification_type,command_object_id,command_args)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_customvariables
+--
+
+CREATE TABLE icinga_customvariables (
+ customvariable_id bigserial,
+ instance_id bigint default 0,
+ object_id bigint default 0,
+ config_type INTEGER default 0,
+ has_been_modified INTEGER default 0,
+ varname TEXT default '',
+ varvalue TEXT default '',
+ is_json INTEGER default 0,
+ session_token INTEGER default NULL,
+ CONSTRAINT PK_customvariable_id PRIMARY KEY (customvariable_id) ,
+ CONSTRAINT UQ_customvariables UNIQUE (object_id,config_type,varname)
+) ;
+CREATE INDEX icinga_customvariables_i ON icinga_customvariables(varname);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_customvariablestatus
+--
+
+CREATE TABLE icinga_customvariablestatus (
+ customvariablestatus_id bigserial,
+ instance_id bigint default 0,
+ object_id bigint default 0,
+ status_update_time timestamp,
+ has_been_modified INTEGER default 0,
+ varname TEXT default '',
+ varvalue TEXT default '',
+ is_json INTEGER default 0,
+ session_token INTEGER default NULL,
+ CONSTRAINT PK_customvariablestatus_id PRIMARY KEY (customvariablestatus_id) ,
+ CONSTRAINT UQ_customvariablestatus UNIQUE (object_id,varname)
+) ;
+CREATE INDEX icinga_customvariablestatus_i ON icinga_customvariablestatus(varname);
+
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_dbversion
+--
+
+CREATE TABLE icinga_dbversion (
+ dbversion_id bigserial,
+ name TEXT default '',
+ version TEXT default '',
+ create_time timestamp,
+ modify_time timestamp,
+ CONSTRAINT PK_dbversion_id PRIMARY KEY (dbversion_id) ,
+ CONSTRAINT UQ_dbversion UNIQUE (name)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_downtimehistory
+--
+
+CREATE TABLE icinga_downtimehistory (
+ downtimehistory_id bigserial,
+ instance_id bigint default 0,
+ downtime_type INTEGER default 0,
+ object_id bigint default 0,
+ entry_time timestamp,
+ author_name TEXT default '',
+ comment_data TEXT default '',
+ internal_downtime_id bigint default 0,
+ triggered_by_id bigint default 0,
+ is_fixed INTEGER default 0,
+ duration BIGINT default 0,
+ scheduled_start_time timestamp,
+ scheduled_end_time timestamp,
+ was_started INTEGER default 0,
+ actual_start_time timestamp,
+ actual_start_time_usec INTEGER default 0,
+ actual_end_time timestamp,
+ actual_end_time_usec INTEGER default 0,
+ was_cancelled INTEGER default 0,
+ is_in_effect INTEGER default 0,
+ trigger_time timestamp,
+ name TEXT default NULL,
+ CONSTRAINT PK_downtimehistory_id PRIMARY KEY (downtimehistory_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_eventhandlers
+--
+
+CREATE TABLE icinga_eventhandlers (
+ eventhandler_id bigserial,
+ instance_id bigint default 0,
+ eventhandler_type INTEGER default 0,
+ object_id bigint default 0,
+ state INTEGER default 0,
+ state_type INTEGER default 0,
+ start_time timestamp,
+ start_time_usec INTEGER default 0,
+ end_time timestamp,
+ end_time_usec INTEGER default 0,
+ command_object_id bigint default 0,
+ command_args TEXT default '',
+ command_line TEXT default '',
+ timeout INTEGER default 0,
+ early_timeout INTEGER default 0,
+ execution_time double precision default 0,
+ return_code INTEGER default 0,
+ output TEXT default '',
+ long_output TEXT default '',
+ CONSTRAINT PK_eventhandler_id PRIMARY KEY (eventhandler_id) ,
+ CONSTRAINT UQ_eventhandlers UNIQUE (instance_id,object_id,start_time,start_time_usec)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_externalcommands
+--
+
+CREATE TABLE icinga_externalcommands (
+ externalcommand_id bigserial,
+ instance_id bigint default 0,
+ entry_time timestamp,
+ command_type INTEGER default 0,
+ command_name TEXT default '',
+ command_args TEXT default '',
+ CONSTRAINT PK_externalcommand_id PRIMARY KEY (externalcommand_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_flappinghistory
+--
+
+CREATE TABLE icinga_flappinghistory (
+ flappinghistory_id bigserial,
+ instance_id bigint default 0,
+ event_time timestamp,
+ event_time_usec INTEGER default 0,
+ event_type INTEGER default 0,
+ reason_type INTEGER default 0,
+ flapping_type INTEGER default 0,
+ object_id bigint default 0,
+ percent_state_change double precision default 0,
+ low_threshold double precision default 0,
+ high_threshold double precision default 0,
+ comment_time timestamp,
+ internal_comment_id bigint default 0,
+ CONSTRAINT PK_flappinghistory_id PRIMARY KEY (flappinghistory_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostchecks
+--
+
+CREATE TABLE icinga_hostchecks (
+ hostcheck_id bigserial,
+ instance_id bigint default 0,
+ host_object_id bigint default 0,
+ check_type INTEGER default 0,
+ is_raw_check INTEGER default 0,
+ current_check_attempt INTEGER default 0,
+ max_check_attempts INTEGER default 0,
+ state INTEGER default 0,
+ state_type INTEGER default 0,
+ start_time timestamp,
+ start_time_usec INTEGER default 0,
+ end_time timestamp,
+ end_time_usec INTEGER default 0,
+ command_object_id bigint default 0,
+ command_args TEXT default '',
+ command_line TEXT default '',
+ timeout INTEGER default 0,
+ early_timeout INTEGER default 0,
+ execution_time double precision default 0,
+ latency double precision default 0,
+ return_code INTEGER default 0,
+ output TEXT default '',
+ long_output TEXT default '',
+ perfdata TEXT default '',
+ CONSTRAINT PK_hostcheck_id PRIMARY KEY (hostcheck_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostdependencies
+--
+
+CREATE TABLE icinga_hostdependencies (
+ hostdependency_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ host_object_id bigint default 0,
+ dependent_host_object_id bigint default 0,
+ dependency_type INTEGER default 0,
+ inherits_parent INTEGER default 0,
+ timeperiod_object_id bigint default 0,
+ fail_on_up INTEGER default 0,
+ fail_on_down INTEGER default 0,
+ fail_on_unreachable INTEGER default 0,
+ CONSTRAINT PK_hostdependency_id PRIMARY KEY (hostdependency_id)
+) ;
+CREATE INDEX idx_hostdependencies ON icinga_hostdependencies(instance_id,config_type,host_object_id,dependent_host_object_id,dependency_type,inherits_parent,fail_on_up,fail_on_down,fail_on_unreachable);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostescalations
+--
+
+CREATE TABLE icinga_hostescalations (
+ hostescalation_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ host_object_id bigint default 0,
+ timeperiod_object_id bigint default 0,
+ first_notification INTEGER default 0,
+ last_notification INTEGER default 0,
+ notification_interval double precision default 0,
+ escalate_on_recovery INTEGER default 0,
+ escalate_on_down INTEGER default 0,
+ escalate_on_unreachable INTEGER default 0,
+ CONSTRAINT PK_hostescalation_id PRIMARY KEY (hostescalation_id) ,
+ CONSTRAINT UQ_hostescalations UNIQUE (instance_id,config_type,host_object_id,timeperiod_object_id,first_notification,last_notification)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostescalation_contactgroups
+--
+
+CREATE TABLE icinga_hostescalation_contactgroups (
+ hostescalation_contactgroup_id bigserial,
+ instance_id bigint default 0,
+ hostescalation_id bigint default 0,
+ contactgroup_object_id bigint default 0,
+ CONSTRAINT PK_hostescalation_contactgroup_id PRIMARY KEY (hostescalation_contactgroup_id) ,
+ CONSTRAINT UQ_hostescalation_contactgroups UNIQUE (hostescalation_id,contactgroup_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostescalation_contacts
+--
+
+CREATE TABLE icinga_hostescalation_contacts (
+ hostescalation_contact_id bigserial,
+ instance_id bigint default 0,
+ hostescalation_id bigint default 0,
+ contact_object_id bigint default 0,
+ CONSTRAINT PK_hostescalation_contact_id PRIMARY KEY (hostescalation_contact_id) ,
+ CONSTRAINT UQ_hostescalation_contacts UNIQUE (instance_id,hostescalation_id,contact_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostgroups
+--
+
+CREATE TABLE icinga_hostgroups (
+ hostgroup_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ hostgroup_object_id bigint default 0,
+ alias TEXT default '',
+ notes TEXT default NULL,
+ notes_url TEXT default NULL,
+ action_url TEXT default NULL,
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_hostgroup_id PRIMARY KEY (hostgroup_id) ,
+ CONSTRAINT UQ_hostgroups UNIQUE (instance_id,hostgroup_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hostgroup_members
+--
+
+CREATE TABLE icinga_hostgroup_members (
+ hostgroup_member_id bigserial,
+ instance_id bigint default 0,
+ hostgroup_id bigint default 0,
+ host_object_id bigint default 0,
+ session_token INTEGER default NULL,
+ CONSTRAINT PK_hostgroup_member_id PRIMARY KEY (hostgroup_member_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hosts
+--
+
+CREATE TABLE icinga_hosts (
+ host_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ host_object_id bigint default 0,
+ alias TEXT default '',
+ display_name TEXT default '',
+ address TEXT default '',
+ address6 TEXT default '',
+ check_command_object_id bigint default 0,
+ check_command_args TEXT default '',
+ eventhandler_command_object_id bigint default 0,
+ eventhandler_command_args TEXT default '',
+ notification_timeperiod_object_id bigint default 0,
+ check_timeperiod_object_id bigint default 0,
+ failure_prediction_options TEXT default '',
+ check_interval double precision default 0,
+ retry_interval double precision default 0,
+ max_check_attempts INTEGER default 0,
+ first_notification_delay double precision default 0,
+ notification_interval double precision default 0,
+ notify_on_down INTEGER default 0,
+ notify_on_unreachable INTEGER default 0,
+ notify_on_recovery INTEGER default 0,
+ notify_on_flapping INTEGER default 0,
+ notify_on_downtime INTEGER default 0,
+ stalk_on_up INTEGER default 0,
+ stalk_on_down INTEGER default 0,
+ stalk_on_unreachable INTEGER default 0,
+ flap_detection_enabled INTEGER default 0,
+ flap_detection_on_up INTEGER default 0,
+ flap_detection_on_down INTEGER default 0,
+ flap_detection_on_unreachable INTEGER default 0,
+ low_flap_threshold double precision default 0,
+ high_flap_threshold double precision default 0,
+ process_performance_data INTEGER default 0,
+ freshness_checks_enabled INTEGER default 0,
+ freshness_threshold INTEGER default 0,
+ passive_checks_enabled INTEGER default 0,
+ event_handler_enabled INTEGER default 0,
+ active_checks_enabled INTEGER default 0,
+ retain_status_information INTEGER default 0,
+ retain_nonstatus_information INTEGER default 0,
+ notifications_enabled INTEGER default 0,
+ obsess_over_host INTEGER default 0,
+ failure_prediction_enabled INTEGER default 0,
+ notes TEXT default '',
+ notes_url TEXT default '',
+ action_url TEXT default '',
+ icon_image TEXT default '',
+ icon_image_alt TEXT default '',
+ vrml_image TEXT default '',
+ statusmap_image TEXT default '',
+ have_2d_coords INTEGER default 0,
+ x_2d INTEGER default 0,
+ y_2d INTEGER default 0,
+ have_3d_coords INTEGER default 0,
+ x_3d double precision default 0,
+ y_3d double precision default 0,
+ z_3d double precision default 0,
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_host_id PRIMARY KEY (host_id) ,
+ CONSTRAINT UQ_hosts UNIQUE (instance_id,config_type,host_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_hoststatus
+--
+
+CREATE TABLE icinga_hoststatus (
+ hoststatus_id bigserial,
+ instance_id bigint default 0,
+ host_object_id bigint default 0,
+ status_update_time timestamp,
+ output TEXT default '',
+ long_output TEXT default '',
+ perfdata TEXT default '',
+ check_source varchar(255) default '',
+ current_state INTEGER default 0,
+ has_been_checked INTEGER default 0,
+ should_be_scheduled INTEGER default 0,
+ current_check_attempt INTEGER default 0,
+ max_check_attempts INTEGER default 0,
+ last_check timestamp,
+ next_check timestamp,
+ check_type INTEGER default 0,
+ last_state_change timestamp,
+ last_hard_state_change timestamp,
+ last_hard_state INTEGER default 0,
+ last_time_up timestamp,
+ last_time_down timestamp,
+ last_time_unreachable timestamp,
+ state_type INTEGER default 0,
+ last_notification timestamp,
+ next_notification timestamp,
+ no_more_notifications INTEGER default 0,
+ notifications_enabled INTEGER default 0,
+ problem_has_been_acknowledged INTEGER default 0,
+ acknowledgement_type INTEGER default 0,
+ current_notification_number INTEGER default 0,
+ passive_checks_enabled INTEGER default 0,
+ active_checks_enabled INTEGER default 0,
+ event_handler_enabled INTEGER default 0,
+ flap_detection_enabled INTEGER default 0,
+ is_flapping INTEGER default 0,
+ percent_state_change double precision default 0,
+ latency double precision default 0,
+ execution_time double precision default 0,
+ scheduled_downtime_depth INTEGER default 0,
+ failure_prediction_enabled INTEGER default 0,
+ process_performance_data INTEGER default 0,
+ obsess_over_host INTEGER default 0,
+ modified_host_attributes INTEGER default 0,
+ original_attributes TEXT default NULL,
+ event_handler TEXT default '',
+ check_command TEXT default '',
+ normal_check_interval double precision default 0,
+ retry_check_interval double precision default 0,
+ check_timeperiod_object_id bigint default 0,
+ is_reachable INTEGER default 0,
+ CONSTRAINT PK_hoststatus_id PRIMARY KEY (hoststatus_id) ,
+ CONSTRAINT UQ_hoststatus UNIQUE (host_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_host_contactgroups
+--
+
+CREATE TABLE icinga_host_contactgroups (
+ host_contactgroup_id bigserial,
+ instance_id bigint default 0,
+ host_id bigint default 0,
+ contactgroup_object_id bigint default 0,
+ CONSTRAINT PK_host_contactgroup_id PRIMARY KEY (host_contactgroup_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_host_contacts
+--
+
+CREATE TABLE icinga_host_contacts (
+ host_contact_id bigserial,
+ instance_id bigint default 0,
+ host_id bigint default 0,
+ contact_object_id bigint default 0,
+ CONSTRAINT PK_host_contact_id PRIMARY KEY (host_contact_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_host_parenthosts
+--
+
+CREATE TABLE icinga_host_parenthosts (
+ host_parenthost_id bigserial,
+ instance_id bigint default 0,
+ host_id bigint default 0,
+ parent_host_object_id bigint default 0,
+ CONSTRAINT PK_host_parenthost_id PRIMARY KEY (host_parenthost_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_instances
+--
+
+CREATE TABLE icinga_instances (
+ instance_id bigserial,
+ instance_name TEXT default '',
+ instance_description TEXT default '',
+ CONSTRAINT PK_instance_id PRIMARY KEY (instance_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_logentries
+--
+
+CREATE TABLE icinga_logentries (
+ logentry_id bigserial,
+ instance_id bigint default 0,
+ logentry_time timestamp,
+ entry_time timestamp,
+ entry_time_usec INTEGER default 0,
+ logentry_type INTEGER default 0,
+ logentry_data TEXT default '',
+ realtime_data INTEGER default 0,
+ inferred_data_extracted INTEGER default 0,
+ object_id bigint default NULL,
+ CONSTRAINT PK_logentry_id PRIMARY KEY (logentry_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_notifications
+--
+
+CREATE TABLE icinga_notifications (
+ notification_id bigserial,
+ instance_id bigint default 0,
+ notification_type INTEGER default 0,
+ notification_reason INTEGER default 0,
+ object_id bigint default 0,
+ start_time timestamp,
+ start_time_usec INTEGER default 0,
+ end_time timestamp,
+ end_time_usec INTEGER default 0,
+ state INTEGER default 0,
+ output TEXT default '',
+ long_output TEXT default '',
+ escalated INTEGER default 0,
+ contacts_notified INTEGER default 0,
+ CONSTRAINT PK_notification_id PRIMARY KEY (notification_id) ,
+ CONSTRAINT UQ_notifications UNIQUE (instance_id,object_id,start_time,start_time_usec)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_objects
+--
+
+CREATE TABLE icinga_objects (
+ object_id bigserial,
+ instance_id bigint default 0,
+ objecttype_id bigint default 0,
+ name1 TEXT,
+ name2 TEXT,
+ is_active INTEGER default 0,
+ CONSTRAINT PK_object_id PRIMARY KEY (object_id)
+-- UNIQUE (objecttype_id,name1,name2)
+) ;
+CREATE INDEX icinga_objects_i ON icinga_objects(objecttype_id,name1,name2);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_processevents
+--
+
+CREATE TABLE icinga_processevents (
+ processevent_id bigserial,
+ instance_id bigint default 0,
+ event_type INTEGER default 0,
+ event_time timestamp,
+ event_time_usec INTEGER default 0,
+ process_id bigint default 0,
+ program_name TEXT default '',
+ program_version TEXT default '',
+ program_date TEXT default '',
+ CONSTRAINT PK_processevent_id PRIMARY KEY (processevent_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_programstatus
+--
+
+CREATE TABLE icinga_programstatus (
+ programstatus_id bigserial,
+ instance_id bigint default 0,
+ program_version TEXT default NULL,
+ status_update_time timestamp,
+ program_start_time timestamp,
+ program_end_time timestamp,
+ is_currently_running INTEGER default 0,
+ endpoint_name TEXT default '',
+ process_id bigint default 0,
+ daemon_mode INTEGER default 0,
+ last_command_check timestamp,
+ last_log_rotation timestamp,
+ notifications_enabled INTEGER default 0,
+ disable_notif_expire_time timestamp,
+ active_service_checks_enabled INTEGER default 0,
+ passive_service_checks_enabled INTEGER default 0,
+ active_host_checks_enabled INTEGER default 0,
+ passive_host_checks_enabled INTEGER default 0,
+ event_handlers_enabled INTEGER default 0,
+ flap_detection_enabled INTEGER default 0,
+ failure_prediction_enabled INTEGER default 0,
+ process_performance_data INTEGER default 0,
+ obsess_over_hosts INTEGER default 0,
+ obsess_over_services INTEGER default 0,
+ modified_host_attributes INTEGER default 0,
+ modified_service_attributes INTEGER default 0,
+ global_host_event_handler TEXT default '',
+ global_service_event_handler TEXT default '',
+ config_dump_in_progress INTEGER default 0,
+ CONSTRAINT PK_programstatus_id PRIMARY KEY (programstatus_id) ,
+ CONSTRAINT UQ_programstatus UNIQUE (instance_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_runtimevariables
+--
+
+CREATE TABLE icinga_runtimevariables (
+ runtimevariable_id bigserial,
+ instance_id bigint default 0,
+ varname TEXT default '',
+ varvalue TEXT default '',
+ CONSTRAINT PK_runtimevariable_id PRIMARY KEY (runtimevariable_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_scheduleddowntime
+--
+
+CREATE TABLE icinga_scheduleddowntime (
+ scheduleddowntime_id bigserial,
+ instance_id bigint default 0,
+ downtime_type INTEGER default 0,
+ object_id bigint default 0,
+ entry_time timestamp,
+ author_name TEXT default '',
+ comment_data TEXT default '',
+ internal_downtime_id bigint default 0,
+ triggered_by_id bigint default 0,
+ is_fixed INTEGER default 0,
+ duration BIGINT default 0,
+ scheduled_start_time timestamp,
+ scheduled_end_time timestamp,
+ was_started INTEGER default 0,
+ actual_start_time timestamp,
+ actual_start_time_usec INTEGER default 0,
+ is_in_effect INTEGER default 0,
+ trigger_time timestamp,
+ name TEXT default NULL,
+ session_token INTEGER default NULL,
+ CONSTRAINT PK_scheduleddowntime_id PRIMARY KEY (scheduleddowntime_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicechecks
+--
+
+CREATE TABLE icinga_servicechecks (
+ servicecheck_id bigserial,
+ instance_id bigint default 0,
+ service_object_id bigint default 0,
+ check_type INTEGER default 0,
+ current_check_attempt INTEGER default 0,
+ max_check_attempts INTEGER default 0,
+ state INTEGER default 0,
+ state_type INTEGER default 0,
+ start_time timestamp,
+ start_time_usec INTEGER default 0,
+ end_time timestamp,
+ end_time_usec INTEGER default 0,
+ command_object_id bigint default 0,
+ command_args TEXT default '',
+ command_line TEXT default '',
+ timeout INTEGER default 0,
+ early_timeout INTEGER default 0,
+ execution_time double precision default 0,
+ latency double precision default 0,
+ return_code INTEGER default 0,
+ output TEXT default '',
+ long_output TEXT default '',
+ perfdata TEXT default '',
+ CONSTRAINT PK_servicecheck_id PRIMARY KEY (servicecheck_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicedependencies
+--
+
+CREATE TABLE icinga_servicedependencies (
+ servicedependency_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ service_object_id bigint default 0,
+ dependent_service_object_id bigint default 0,
+ dependency_type INTEGER default 0,
+ inherits_parent INTEGER default 0,
+ timeperiod_object_id bigint default 0,
+ fail_on_ok INTEGER default 0,
+ fail_on_warning INTEGER default 0,
+ fail_on_unknown INTEGER default 0,
+ fail_on_critical INTEGER default 0,
+ CONSTRAINT PK_servicedependency_id PRIMARY KEY (servicedependency_id)
+) ;
+CREATE INDEX idx_servicedependencies ON icinga_servicedependencies(instance_id,config_type,service_object_id,dependent_service_object_id,dependency_type,inherits_parent,fail_on_ok,fail_on_warning,fail_on_unknown,fail_on_critical);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_serviceescalations
+--
+
+CREATE TABLE icinga_serviceescalations (
+ serviceescalation_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ service_object_id bigint default 0,
+ timeperiod_object_id bigint default 0,
+ first_notification INTEGER default 0,
+ last_notification INTEGER default 0,
+ notification_interval double precision default 0,
+ escalate_on_recovery INTEGER default 0,
+ escalate_on_warning INTEGER default 0,
+ escalate_on_unknown INTEGER default 0,
+ escalate_on_critical INTEGER default 0,
+ CONSTRAINT PK_serviceescalation_id PRIMARY KEY (serviceescalation_id) ,
+ CONSTRAINT UQ_serviceescalations UNIQUE (instance_id,config_type,service_object_id,timeperiod_object_id,first_notification,last_notification)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_serviceescalation_contactgroups
+--
+
+CREATE TABLE icinga_serviceescalation_contactgroups (
+ serviceescalation_contactgroup_id bigserial,
+ instance_id bigint default 0,
+ serviceescalation_id bigint default 0,
+ contactgroup_object_id bigint default 0,
+ CONSTRAINT PK_serviceescalation_contactgroup_id PRIMARY KEY (serviceescalation_contactgroup_id) ,
+ CONSTRAINT UQ_serviceescalation_contactgro UNIQUE (serviceescalation_id,contactgroup_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_serviceescalation_contacts
+--
+
+CREATE TABLE icinga_serviceescalation_contacts (
+ serviceescalation_contact_id bigserial,
+ instance_id bigint default 0,
+ serviceescalation_id bigint default 0,
+ contact_object_id bigint default 0,
+ CONSTRAINT PK_serviceescalation_contact_id PRIMARY KEY (serviceescalation_contact_id) ,
+ CONSTRAINT UQ_serviceescalation_contacts UNIQUE (instance_id,serviceescalation_id,contact_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicegroups
+--
+
+CREATE TABLE icinga_servicegroups (
+ servicegroup_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ servicegroup_object_id bigint default 0,
+ alias TEXT default '',
+ notes TEXT default NULL,
+ notes_url TEXT default NULL,
+ action_url TEXT default NULL,
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_servicegroup_id PRIMARY KEY (servicegroup_id) ,
+ CONSTRAINT UQ_servicegroups UNIQUE (instance_id,config_type,servicegroup_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicegroup_members
+--
+
+CREATE TABLE icinga_servicegroup_members (
+ servicegroup_member_id bigserial,
+ instance_id bigint default 0,
+ servicegroup_id bigint default 0,
+ service_object_id bigint default 0,
+ session_token INTEGER default NULL,
+ CONSTRAINT PK_servicegroup_member_id PRIMARY KEY (servicegroup_member_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_services
+--
+
+CREATE TABLE icinga_services (
+ service_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ host_object_id bigint default 0,
+ service_object_id bigint default 0,
+ display_name TEXT default '',
+ check_command_object_id bigint default 0,
+ check_command_args TEXT default '',
+ eventhandler_command_object_id bigint default 0,
+ eventhandler_command_args TEXT default '',
+ notification_timeperiod_object_id bigint default 0,
+ check_timeperiod_object_id bigint default 0,
+ failure_prediction_options TEXT default '',
+ check_interval double precision default 0,
+ retry_interval double precision default 0,
+ max_check_attempts INTEGER default 0,
+ first_notification_delay double precision default 0,
+ notification_interval double precision default 0,
+ notify_on_warning INTEGER default 0,
+ notify_on_unknown INTEGER default 0,
+ notify_on_critical INTEGER default 0,
+ notify_on_recovery INTEGER default 0,
+ notify_on_flapping INTEGER default 0,
+ notify_on_downtime INTEGER default 0,
+ stalk_on_ok INTEGER default 0,
+ stalk_on_warning INTEGER default 0,
+ stalk_on_unknown INTEGER default 0,
+ stalk_on_critical INTEGER default 0,
+ is_volatile INTEGER default 0,
+ flap_detection_enabled INTEGER default 0,
+ flap_detection_on_ok INTEGER default 0,
+ flap_detection_on_warning INTEGER default 0,
+ flap_detection_on_unknown INTEGER default 0,
+ flap_detection_on_critical INTEGER default 0,
+ low_flap_threshold double precision default 0,
+ high_flap_threshold double precision default 0,
+ process_performance_data INTEGER default 0,
+ freshness_checks_enabled INTEGER default 0,
+ freshness_threshold INTEGER default 0,
+ passive_checks_enabled INTEGER default 0,
+ event_handler_enabled INTEGER default 0,
+ active_checks_enabled INTEGER default 0,
+ retain_status_information INTEGER default 0,
+ retain_nonstatus_information INTEGER default 0,
+ notifications_enabled INTEGER default 0,
+ obsess_over_service INTEGER default 0,
+ failure_prediction_enabled INTEGER default 0,
+ notes TEXT default '',
+ notes_url TEXT default '',
+ action_url TEXT default '',
+ icon_image TEXT default '',
+ icon_image_alt TEXT default '',
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_service_id PRIMARY KEY (service_id) ,
+ CONSTRAINT UQ_services UNIQUE (instance_id,config_type,service_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_servicestatus
+--
+
+CREATE TABLE icinga_servicestatus (
+ servicestatus_id bigserial,
+ instance_id bigint default 0,
+ service_object_id bigint default 0,
+ status_update_time timestamp,
+ output TEXT default '',
+ long_output TEXT default '',
+ perfdata TEXT default '',
+ check_source varchar(255) default '',
+ current_state INTEGER default 0,
+ has_been_checked INTEGER default 0,
+ should_be_scheduled INTEGER default 0,
+ current_check_attempt INTEGER default 0,
+ max_check_attempts INTEGER default 0,
+ last_check timestamp,
+ next_check timestamp,
+ check_type INTEGER default 0,
+ last_state_change timestamp,
+ last_hard_state_change timestamp,
+ last_hard_state INTEGER default 0,
+ last_time_ok timestamp,
+ last_time_warning timestamp,
+ last_time_unknown timestamp,
+ last_time_critical timestamp,
+ state_type INTEGER default 0,
+ last_notification timestamp,
+ next_notification timestamp,
+ no_more_notifications INTEGER default 0,
+ notifications_enabled INTEGER default 0,
+ problem_has_been_acknowledged INTEGER default 0,
+ acknowledgement_type INTEGER default 0,
+ current_notification_number INTEGER default 0,
+ passive_checks_enabled INTEGER default 0,
+ active_checks_enabled INTEGER default 0,
+ event_handler_enabled INTEGER default 0,
+ flap_detection_enabled INTEGER default 0,
+ is_flapping INTEGER default 0,
+ percent_state_change double precision default 0,
+ latency double precision default 0,
+ execution_time double precision default 0,
+ scheduled_downtime_depth INTEGER default 0,
+ failure_prediction_enabled INTEGER default 0,
+ process_performance_data INTEGER default 0,
+ obsess_over_service INTEGER default 0,
+ modified_service_attributes INTEGER default 0,
+ original_attributes TEXT default NULL,
+ event_handler TEXT default '',
+ check_command TEXT default '',
+ normal_check_interval double precision default 0,
+ retry_check_interval double precision default 0,
+ check_timeperiod_object_id bigint default 0,
+ is_reachable INTEGER default 0,
+ CONSTRAINT PK_servicestatus_id PRIMARY KEY (servicestatus_id) ,
+ CONSTRAINT UQ_servicestatus UNIQUE (service_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_service_contactgroups
+--
+
+CREATE TABLE icinga_service_contactgroups (
+ service_contactgroup_id bigserial,
+ instance_id bigint default 0,
+ service_id bigint default 0,
+ contactgroup_object_id bigint default 0,
+ CONSTRAINT PK_service_contactgroup_id PRIMARY KEY (service_contactgroup_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_service_contacts
+--
+
+CREATE TABLE icinga_service_contacts (
+ service_contact_id bigserial,
+ instance_id bigint default 0,
+ service_id bigint default 0,
+ contact_object_id bigint default 0,
+ CONSTRAINT PK_service_contact_id PRIMARY KEY (service_contact_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_statehistory
+--
+
+CREATE TABLE icinga_statehistory (
+ statehistory_id bigserial,
+ instance_id bigint default 0,
+ state_time timestamp,
+ state_time_usec INTEGER default 0,
+ object_id bigint default 0,
+ state_change INTEGER default 0,
+ state INTEGER default 0,
+ state_type INTEGER default 0,
+ current_check_attempt INTEGER default 0,
+ max_check_attempts INTEGER default 0,
+ last_state INTEGER default '-1',
+ last_hard_state INTEGER default '-1',
+ output TEXT default '',
+ long_output TEXT default '',
+ check_source varchar(255) default '',
+ CONSTRAINT PK_statehistory_id PRIMARY KEY (statehistory_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_systemcommands
+--
+
+CREATE TABLE icinga_systemcommands (
+ systemcommand_id bigserial,
+ instance_id bigint default 0,
+ start_time timestamp,
+ start_time_usec INTEGER default 0,
+ end_time timestamp,
+ end_time_usec INTEGER default 0,
+ command_line TEXT default '',
+ timeout INTEGER default 0,
+ early_timeout INTEGER default 0,
+ execution_time double precision default 0,
+ return_code INTEGER default 0,
+ output TEXT default '',
+ long_output TEXT default '',
+ CONSTRAINT PK_systemcommand_id PRIMARY KEY (systemcommand_id) ,
+ CONSTRAINT UQ_systemcommands UNIQUE (instance_id,start_time,start_time_usec)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_timeperiods
+--
+
+CREATE TABLE icinga_timeperiods (
+ timeperiod_id bigserial,
+ instance_id bigint default 0,
+ config_type INTEGER default 0,
+ timeperiod_object_id bigint default 0,
+ alias TEXT default '',
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_timeperiod_id PRIMARY KEY (timeperiod_id) ,
+ CONSTRAINT UQ_timeperiods UNIQUE (instance_id,config_type,timeperiod_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_timeperiod_timeranges
+--
+
+CREATE TABLE icinga_timeperiod_timeranges (
+ timeperiod_timerange_id bigserial,
+ instance_id bigint default 0,
+ timeperiod_id bigint default 0,
+ day INTEGER default 0,
+ start_sec INTEGER default 0,
+ end_sec INTEGER default 0,
+ CONSTRAINT PK_timeperiod_timerange_id PRIMARY KEY (timeperiod_timerange_id)
+) ;
+
+
+-- --------------------------------------------------------
+-- Icinga 2 specific schema extensions
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_endpoints
+--
+
+CREATE TABLE icinga_endpoints (
+ endpoint_id bigserial,
+ instance_id bigint default 0,
+ endpoint_object_id bigint default 0,
+ zone_object_id bigint default 0,
+ config_type integer default 0,
+ identity text DEFAULT NULL,
+ node text DEFAULT NULL,
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_endpoint_id PRIMARY KEY (endpoint_id) ,
+ CONSTRAINT UQ_endpoints UNIQUE (instance_id,config_type,endpoint_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_endpointstatus
+--
+
+CREATE TABLE icinga_endpointstatus (
+ endpointstatus_id bigserial,
+ instance_id bigint default 0,
+ endpoint_object_id bigint default 0,
+ zone_object_id bigint default 0,
+ status_update_time timestamp,
+ identity text DEFAULT NULL,
+ node text DEFAULT NULL,
+ is_connected integer default 0,
+ CONSTRAINT PK_endpointstatus_id PRIMARY KEY (endpointstatus_id) ,
+ CONSTRAINT UQ_endpointstatus UNIQUE (endpoint_object_id)
+) ;
+
+--
+-- Table structure for table icinga_zones
+--
+
+CREATE TABLE icinga_zones (
+ zone_id bigserial,
+ instance_id bigint default 0,
+ zone_object_id bigint default 0,
+ parent_zone_object_id bigint default 0,
+ config_type integer default 0,
+ is_global integer default 0,
+ config_hash varchar(64) DEFAULT NULL,
+ CONSTRAINT PK_zone_id PRIMARY KEY (zone_id) ,
+ CONSTRAINT UQ_zones UNIQUE (instance_id,config_type,zone_object_id)
+) ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table icinga_zonestatus
+--
+
+CREATE TABLE icinga_zonestatus (
+ zonestatus_id bigserial,
+ instance_id bigint default 0,
+ zone_object_id bigint default 0,
+ parent_zone_object_id bigint default 0,
+ status_update_time timestamp,
+ CONSTRAINT PK_zonestatus_id PRIMARY KEY (zonestatus_id) ,
+ CONSTRAINT UQ_zonestatus UNIQUE (zone_object_id)
+) ;
+
+
+ALTER TABLE icinga_servicestatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_hoststatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_contactstatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_programstatus ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_comments ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_scheduleddowntime ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_runtimevariables ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_customvariablestatus ADD COLUMN endpoint_object_id bigint default NULL;
+
+ALTER TABLE icinga_acknowledgements ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_commenthistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_contactnotifications ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_downtimehistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_eventhandlers ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_externalcommands ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_flappinghistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_hostchecks ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_logentries ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_notifications ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_processevents ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_servicechecks ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_statehistory ADD COLUMN endpoint_object_id bigint default NULL;
+ALTER TABLE icinga_systemcommands ADD COLUMN endpoint_object_id bigint default NULL;
+
+
+-- -----------------------------------------
+-- add index (delete)
+-- -----------------------------------------
+
+-- for periodic delete
+-- instance_id and
+-- TIMEDEVENTS => scheduled_time
+-- SYSTEMCOMMANDS, SERVICECHECKS, HOSTCHECKS, EVENTHANDLERS => start_time
+-- EXTERNALCOMMANDS => entry_time
+
+-- instance_id
+CREATE INDEX systemcommands_i_id_idx on icinga_systemcommands(instance_id);
+CREATE INDEX servicechecks_i_id_idx on icinga_servicechecks(instance_id);
+CREATE INDEX hostchecks_i_id_idx on icinga_hostchecks(instance_id);
+CREATE INDEX eventhandlers_i_id_idx on icinga_eventhandlers(instance_id);
+CREATE INDEX externalcommands_i_id_idx on icinga_externalcommands(instance_id);
+
+-- time
+CREATE INDEX systemcommands_time_id_idx on icinga_systemcommands(start_time);
+CREATE INDEX servicechecks_time_id_idx on icinga_servicechecks(start_time);
+CREATE INDEX hostchecks_time_id_idx on icinga_hostchecks(start_time);
+CREATE INDEX eventhandlers_time_id_idx on icinga_eventhandlers(start_time);
+CREATE INDEX externalcommands_time_id_idx on icinga_externalcommands(entry_time);
+
+
+-- for starting cleanup - referenced in dbhandler.c:882
+-- instance_id only
+
+-- realtime data
+CREATE INDEX programstatus_i_id_idx on icinga_programstatus(instance_id);
+CREATE INDEX hoststatus_i_id_idx on icinga_hoststatus(instance_id);
+CREATE INDEX servicestatus_i_id_idx on icinga_servicestatus(instance_id);
+CREATE INDEX contactstatus_i_id_idx on icinga_contactstatus(instance_id);
+CREATE INDEX comments_i_id_idx on icinga_comments(instance_id);
+CREATE INDEX scheduleddowntime_i_id_idx on icinga_scheduleddowntime(instance_id);
+CREATE INDEX runtimevariables_i_id_idx on icinga_runtimevariables(instance_id);
+CREATE INDEX customvariablestatus_i_id_idx on icinga_customvariablestatus(instance_id);
+
+-- config data
+CREATE INDEX configfiles_i_id_idx on icinga_configfiles(instance_id);
+CREATE INDEX configfilevariables_i_id_idx on icinga_configfilevariables(instance_id);
+CREATE INDEX customvariables_i_id_idx on icinga_customvariables(instance_id);
+CREATE INDEX commands_i_id_idx on icinga_commands(instance_id);
+CREATE INDEX timeperiods_i_id_idx on icinga_timeperiods(instance_id);
+CREATE INDEX timeperiod_timeranges_i_id_idx on icinga_timeperiod_timeranges(instance_id);
+CREATE INDEX contactgroups_i_id_idx on icinga_contactgroups(instance_id);
+CREATE INDEX contactgroup_members_i_id_idx on icinga_contactgroup_members(instance_id);
+CREATE INDEX hostgroups_i_id_idx on icinga_hostgroups(instance_id);
+CREATE INDEX hostgroup_members_i_id_idx on icinga_hostgroup_members(instance_id);
+CREATE INDEX servicegroups_i_id_idx on icinga_servicegroups(instance_id);
+CREATE INDEX servicegroup_members_i_id_idx on icinga_servicegroup_members(instance_id);
+CREATE INDEX hostesc_i_id_idx on icinga_hostescalations(instance_id);
+CREATE INDEX hostesc_contacts_i_id_idx on icinga_hostescalation_contacts(instance_id);
+CREATE INDEX serviceesc_i_id_idx on icinga_serviceescalations(instance_id);
+CREATE INDEX serviceesc_contacts_i_id_idx on icinga_serviceescalation_contacts(instance_id);
+CREATE INDEX hostdependencies_i_id_idx on icinga_hostdependencies(instance_id);
+CREATE INDEX contacts_i_id_idx on icinga_contacts(instance_id);
+CREATE INDEX contact_addresses_i_id_idx on icinga_contact_addresses(instance_id);
+CREATE INDEX contact_notifcommands_i_id_idx on icinga_contact_notificationcommands(instance_id);
+CREATE INDEX hosts_i_id_idx on icinga_hosts(instance_id);
+CREATE INDEX host_parenthosts_i_id_idx on icinga_host_parenthosts(instance_id);
+CREATE INDEX host_contacts_i_id_idx on icinga_host_contacts(instance_id);
+CREATE INDEX services_i_id_idx on icinga_services(instance_id);
+CREATE INDEX service_contacts_i_id_idx on icinga_service_contacts(instance_id);
+CREATE INDEX service_contactgroups_i_id_idx on icinga_service_contactgroups(instance_id);
+CREATE INDEX host_contactgroups_i_id_idx on icinga_host_contactgroups(instance_id);
+CREATE INDEX hostesc_cgroups_i_id_idx on icinga_hostescalation_contactgroups(instance_id);
+CREATE INDEX serviceesc_cgroups_i_id_idx on icinga_serviceescalation_contactgroups(instance_id);
+
+-- -----------------------------------------
+-- more index stuff (WHERE clauses)
+-- -----------------------------------------
+
+-- hosts
+CREATE INDEX hosts_host_object_id_idx on icinga_hosts(host_object_id);
+
+-- hoststatus
+CREATE INDEX hoststatus_stat_upd_time_idx on icinga_hoststatus(status_update_time);
+CREATE INDEX hoststatus_current_state_idx on icinga_hoststatus(current_state);
+CREATE INDEX hoststatus_check_type_idx on icinga_hoststatus(check_type);
+CREATE INDEX hoststatus_state_type_idx on icinga_hoststatus(state_type);
+CREATE INDEX hoststatus_last_state_chg_idx on icinga_hoststatus(last_state_change);
+CREATE INDEX hoststatus_notif_enabled_idx on icinga_hoststatus(notifications_enabled);
+CREATE INDEX hoststatus_problem_ack_idx on icinga_hoststatus(problem_has_been_acknowledged);
+CREATE INDEX hoststatus_act_chks_en_idx on icinga_hoststatus(active_checks_enabled);
+CREATE INDEX hoststatus_pas_chks_en_idx on icinga_hoststatus(passive_checks_enabled);
+CREATE INDEX hoststatus_event_hdl_en_idx on icinga_hoststatus(event_handler_enabled);
+CREATE INDEX hoststatus_flap_det_en_idx on icinga_hoststatus(flap_detection_enabled);
+CREATE INDEX hoststatus_is_flapping_idx on icinga_hoststatus(is_flapping);
+CREATE INDEX hoststatus_p_state_chg_idx on icinga_hoststatus(percent_state_change);
+CREATE INDEX hoststatus_latency_idx on icinga_hoststatus(latency);
+CREATE INDEX hoststatus_ex_time_idx on icinga_hoststatus(execution_time);
+CREATE INDEX hoststatus_sch_downt_d_idx on icinga_hoststatus(scheduled_downtime_depth);
+
+-- services
+CREATE INDEX services_host_object_id_idx on icinga_services(host_object_id);
+
+--servicestatus
+CREATE INDEX srvcstatus_stat_upd_time_idx on icinga_servicestatus(status_update_time);
+CREATE INDEX srvcstatus_current_state_idx on icinga_servicestatus(current_state);
+CREATE INDEX srvcstatus_check_type_idx on icinga_servicestatus(check_type);
+CREATE INDEX srvcstatus_state_type_idx on icinga_servicestatus(state_type);
+CREATE INDEX srvcstatus_last_state_chg_idx on icinga_servicestatus(last_state_change);
+CREATE INDEX srvcstatus_notif_enabled_idx on icinga_servicestatus(notifications_enabled);
+CREATE INDEX srvcstatus_problem_ack_idx on icinga_servicestatus(problem_has_been_acknowledged);
+CREATE INDEX srvcstatus_act_chks_en_idx on icinga_servicestatus(active_checks_enabled);
+CREATE INDEX srvcstatus_pas_chks_en_idx on icinga_servicestatus(passive_checks_enabled);
+CREATE INDEX srvcstatus_event_hdl_en_idx on icinga_servicestatus(event_handler_enabled);
+CREATE INDEX srvcstatus_flap_det_en_idx on icinga_servicestatus(flap_detection_enabled);
+CREATE INDEX srvcstatus_is_flapping_idx on icinga_servicestatus(is_flapping);
+CREATE INDEX srvcstatus_p_state_chg_idx on icinga_servicestatus(percent_state_change);
+CREATE INDEX srvcstatus_latency_idx on icinga_servicestatus(latency);
+CREATE INDEX srvcstatus_ex_time_idx on icinga_servicestatus(execution_time);
+CREATE INDEX srvcstatus_sch_downt_d_idx on icinga_servicestatus(scheduled_downtime_depth);
+
+-- hostchecks
+CREATE INDEX hostchks_h_obj_id_idx on icinga_hostchecks(host_object_id);
+
+-- servicechecks
+CREATE INDEX servicechks_s_obj_id_idx on icinga_servicechecks(service_object_id);
+
+-- objects
+CREATE INDEX objects_objtype_id_idx ON icinga_objects(objecttype_id);
+CREATE INDEX objects_name1_idx ON icinga_objects(name1);
+CREATE INDEX objects_name2_idx ON icinga_objects(name2);
+CREATE INDEX objects_inst_id_idx ON icinga_objects(instance_id);
+
+-- instances
+-- CREATE INDEX instances_name_idx on icinga_instances(instance_name);
+
+-- logentries
+-- CREATE INDEX loge_instance_id_idx on icinga_logentries(instance_id);
+-- #236
+CREATE INDEX loge_time_idx on icinga_logentries(logentry_time);
+-- CREATE INDEX loge_data_idx on icinga_logentries(logentry_data);
+CREATE INDEX loge_inst_id_time_idx on icinga_logentries (instance_id, logentry_time);
+
+
+-- commenthistory
+-- CREATE INDEX c_hist_instance_id_idx on icinga_logentries(instance_id);
+-- CREATE INDEX c_hist_c_time_idx on icinga_logentries(comment_time);
+-- CREATE INDEX c_hist_i_c_id_idx on icinga_logentries(internal_comment_id);
+
+-- downtimehistory
+-- CREATE INDEX d_t_hist_nstance_id_idx on icinga_downtimehistory(instance_id);
+-- CREATE INDEX d_t_hist_type_idx on icinga_downtimehistory(downtime_type);
+-- CREATE INDEX d_t_hist_object_id_idx on icinga_downtimehistory(object_id);
+-- CREATE INDEX d_t_hist_entry_time_idx on icinga_downtimehistory(entry_time);
+-- CREATE INDEX d_t_hist_sched_start_idx on icinga_downtimehistory(scheduled_start_time);
+-- CREATE INDEX d_t_hist_sched_end_idx on icinga_downtimehistory(scheduled_end_time);
+
+-- scheduleddowntime
+-- CREATE INDEX sched_d_t_downtime_type_idx on icinga_scheduleddowntime(downtime_type);
+-- CREATE INDEX sched_d_t_object_id_idx on icinga_scheduleddowntime(object_id);
+-- CREATE INDEX sched_d_t_entry_time_idx on icinga_scheduleddowntime(entry_time);
+-- CREATE INDEX sched_d_t_start_time_idx on icinga_scheduleddowntime(scheduled_start_time);
+-- CREATE INDEX sched_d_t_end_time_idx on icinga_scheduleddowntime(scheduled_end_time);
+
+-- Icinga Web Notifications
+CREATE INDEX notification_idx ON icinga_notifications(notification_type, object_id, start_time);
+CREATE INDEX notification_object_id_idx ON icinga_notifications(object_id);
+CREATE INDEX contact_notification_idx ON icinga_contactnotifications(notification_id, contact_object_id);
+CREATE INDEX contacts_object_id_idx ON icinga_contacts(contact_object_id);
+CREATE INDEX contact_notif_meth_notif_idx ON icinga_contactnotificationmethods(contactnotification_id, command_object_id);
+CREATE INDEX command_object_idx ON icinga_commands(object_id);
+CREATE INDEX services_combined_object_idx ON icinga_services(service_object_id, host_object_id);
+
+-- statehistory
+CREATE INDEX statehist_i_id_o_id_s_ty_s_ti on icinga_statehistory(instance_id, object_id, state_type, state_time);
+--#2274
+create index statehist_state_idx on icinga_statehistory(object_id,state);
+
+-- #2618
+CREATE INDEX cntgrpmbrs_cgid_coid ON icinga_contactgroup_members (contactgroup_id,contact_object_id);
+CREATE INDEX hstgrpmbrs_hgid_hoid ON icinga_hostgroup_members (hostgroup_id,host_object_id);
+CREATE INDEX hstcntgrps_hid_cgoid ON icinga_host_contactgroups (host_id,contactgroup_object_id);
+CREATE INDEX hstprnthsts_hid_phoid ON icinga_host_parenthosts (host_id,parent_host_object_id);
+CREATE INDEX runtimevars_iid_varn ON icinga_runtimevariables (instance_id,varname);
+CREATE INDEX sgmbrs_sgid_soid ON icinga_servicegroup_members (servicegroup_id,service_object_id);
+CREATE INDEX scgrps_sid_cgoid ON icinga_service_contactgroups (service_id,contactgroup_object_id);
+CREATE INDEX tperiod_tid_d_ss_es ON icinga_timeperiod_timeranges (timeperiod_id,day,start_sec,end_sec);
+
+-- #3649
+CREATE INDEX sla_idx_sthist ON icinga_statehistory (object_id, state_time DESC);
+CREATE INDEX sla_idx_dohist ON icinga_downtimehistory (object_id, actual_start_time, actual_end_time);
+CREATE INDEX sla_idx_obj ON icinga_objects (objecttype_id, is_active, name1);
+
+-- #4985
+CREATE INDEX commenthistory_delete_idx ON icinga_commenthistory (instance_id, comment_time, internal_comment_id);
+
+-- #10070
+CREATE INDEX idx_comments_object_id on icinga_comments(object_id);
+CREATE INDEX idx_scheduleddowntime_object_id on icinga_scheduleddowntime(object_id);
+
+-- #10066
+CREATE INDEX idx_endpoints_object_id on icinga_endpoints(endpoint_object_id);
+CREATE INDEX idx_endpointstatus_object_id on icinga_endpointstatus(endpoint_object_id);
+
+CREATE INDEX idx_endpoints_zone_object_id on icinga_endpoints(zone_object_id);
+CREATE INDEX idx_endpointstatus_zone_object_id on icinga_endpointstatus(zone_object_id);
+
+CREATE INDEX idx_zones_object_id on icinga_zones(zone_object_id);
+CREATE INDEX idx_zonestatus_object_id on icinga_zonestatus(zone_object_id);
+
+CREATE INDEX idx_zones_parent_object_id on icinga_zones(parent_zone_object_id);
+CREATE INDEX idx_zonestatus_parent_object_id on icinga_zonestatus(parent_zone_object_id);
+
+-- #12210
+CREATE INDEX idx_comments_session_del ON icinga_comments (instance_id, session_token);
+CREATE INDEX idx_downtimes_session_del ON icinga_scheduleddowntime (instance_id, session_token);
+
+-- #12107
+CREATE INDEX idx_statehistory_cleanup on icinga_statehistory(instance_id, state_time);
+
+-- #12435
+CREATE INDEX idx_customvariables_object_id on icinga_customvariables(object_id);
+CREATE INDEX idx_contactgroup_members_object_id on icinga_contactgroup_members(contact_object_id);
+CREATE INDEX idx_hostgroup_members_object_id on icinga_hostgroup_members(host_object_id);
+CREATE INDEX idx_servicegroup_members_object_id on icinga_servicegroup_members(service_object_id);
+CREATE INDEX idx_servicedependencies_dependent_service_object_id on icinga_servicedependencies(dependent_service_object_id);
+CREATE INDEX idx_hostdependencies_dependent_host_object_id on icinga_hostdependencies(dependent_host_object_id);
+CREATE INDEX idx_service_contacts_service_id on icinga_service_contacts(service_id);
+CREATE INDEX idx_host_contacts_host_id on icinga_host_contacts(host_id);
+
+-- #5458
+CREATE INDEX idx_downtimehistory_remove ON icinga_downtimehistory (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+CREATE INDEX idx_scheduleddowntime_remove ON icinga_scheduleddowntime (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+
+-- #5492
+CREATE INDEX idx_commenthistory_remove ON icinga_commenthistory (object_id, entry_time);
+CREATE INDEX idx_comments_remove ON icinga_comments (object_id, entry_time);
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.14.3');
+
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.0.2.sql b/lib/db_ido_pgsql/schema/upgrade/2.0.2.sql
new file mode 100644
index 0000000..60710ef
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.0.2.sql
@@ -0,0 +1,17 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.0.2
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+UPDATE icinga_objects SET name2 = NULL WHERE name2 = '';
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.11.6');
+
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.1.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.1.0.sql
new file mode 100644
index 0000000..a32ecea
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.1.0.sql
@@ -0,0 +1,17 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.1.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+ALTER TABLE icinga_programstatus ADD COLUMN endpoint_name TEXT default NULL;
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.11.7');
+
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.2.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.2.0.sql
new file mode 100644
index 0000000..d105a34
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.2.0.sql
@@ -0,0 +1,21 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.2.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+ALTER TABLE icinga_programstatus ADD COLUMN program_version TEXT default NULL;
+
+ALTER TABLE icinga_customvariables ADD COLUMN is_json INTEGER default 0;
+ALTER TABLE icinga_customvariablestatus ADD COLUMN is_json INTEGER default 0;
+
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.12.0');
+
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.3.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.3.0.sql
new file mode 100644
index 0000000..91764de
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.3.0.sql
@@ -0,0 +1,26 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.3.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+-- -----------------------------------------
+-- #7765 drop unique constraint
+-- -----------------------------------------
+
+ALTER TABLE icinga_servicedependencies DROP CONSTRAINT uq_servicedependencies;
+ALTER TABLE icinga_hostdependencies DROP CONSTRAINT uq_hostdependencies;
+
+CREATE INDEX idx_servicedependencies ON icinga_servicedependencies(instance_id,config_type,service_object_id,dependent_service_object_id,dependency_type,inherits_parent,fail_on_ok,fail_on_warning,fail_on_unknown,fail_on_critical);
+CREATE INDEX idx_hostdependencies ON icinga_hostdependencies(instance_id,config_type,host_object_id,dependent_host_object_id,dependency_type,inherits_parent,fail_on_up,fail_on_down,fail_on_unreachable);
+
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.13.0');
+
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.4.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.4.0.sql
new file mode 100644
index 0000000..4a6e45e
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.4.0.sql
@@ -0,0 +1,185 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.4.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+-- -----------------------------------------
+-- #9027 Default timestamps lack time zone
+-- -----------------------------------------
+
+ALTER TABLE icinga_acknowledgements ALTER COLUMN entry_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_acknowledgements ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_commenthistory ALTER COLUMN entry_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_commenthistory ALTER COLUMN comment_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_commenthistory ALTER COLUMN expiration_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_commenthistory ALTER COLUMN deletion_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_comments ALTER COLUMN entry_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_comments ALTER COLUMN comment_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_comments ALTER COLUMN expiration_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_conninfo ALTER COLUMN connect_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_conninfo ALTER COLUMN disconnect_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_conninfo ALTER COLUMN last_checkin_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_conninfo ALTER COLUMN data_start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_conninfo ALTER COLUMN data_end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_contactnotificationmethods ALTER COLUMN start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_contactnotificationmethods ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_contactnotifications ALTER COLUMN start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_contactnotifications ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_contactstatus ALTER COLUMN status_update_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_contactstatus ALTER COLUMN last_host_notification SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_contactstatus ALTER COLUMN last_service_notification SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_customvariablestatus ALTER COLUMN status_update_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_dbversion ALTER COLUMN create_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_dbversion ALTER COLUMN modify_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_downtimehistory ALTER COLUMN entry_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_downtimehistory ALTER COLUMN scheduled_start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_downtimehistory ALTER COLUMN scheduled_end_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_downtimehistory ALTER COLUMN actual_start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_downtimehistory ALTER COLUMN actual_end_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_downtimehistory ALTER COLUMN trigger_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_eventhandlers ALTER COLUMN start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_eventhandlers ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_externalcommands ALTER COLUMN entry_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_flappinghistory ALTER COLUMN event_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_flappinghistory ALTER COLUMN comment_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_hostchecks ALTER COLUMN start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hostchecks ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_hoststatus ALTER COLUMN status_update_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN last_check SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN next_check SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN last_state_change SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN last_hard_state_change SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN last_time_up SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN last_time_down SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN last_time_unreachable SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN last_notification SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_hoststatus ALTER COLUMN next_notification SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_logentries ALTER COLUMN logentry_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_logentries ALTER COLUMN entry_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_notifications ALTER COLUMN start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_notifications ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_processevents ALTER COLUMN event_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_programstatus ALTER COLUMN status_update_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_programstatus ALTER COLUMN program_start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_programstatus ALTER COLUMN program_end_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_programstatus ALTER COLUMN last_command_check SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_programstatus ALTER COLUMN last_log_rotation SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_programstatus ALTER COLUMN disable_notif_expire_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_scheduleddowntime ALTER COLUMN entry_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_scheduleddowntime ALTER COLUMN scheduled_start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_scheduleddowntime ALTER COLUMN scheduled_end_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_scheduleddowntime ALTER COLUMN actual_start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_scheduleddowntime ALTER COLUMN trigger_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_servicechecks ALTER COLUMN start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicechecks ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_servicestatus ALTER COLUMN status_update_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_check SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN next_check SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_state_change SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_hard_state_change SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_time_ok SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_time_warning SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_time_unknown SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_time_critical SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN last_notification SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_servicestatus ALTER COLUMN next_notification SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_statehistory ALTER COLUMN state_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_systemcommands ALTER COLUMN start_time SET DEFAULT '1970-01-01 00:00:00+00';
+ALTER TABLE icinga_systemcommands ALTER COLUMN end_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+ALTER TABLE icinga_endpointstatus ALTER COLUMN status_update_time SET DEFAULT '1970-01-01 00:00:00+00';
+
+-- -----------------------------------------
+-- #9455 check_source data type
+-- -----------------------------------------
+
+ALTER TABLE icinga_statehistory ALTER COLUMN check_source TYPE TEXT;
+ALTER TABLE icinga_statehistory ALTER COLUMN check_source SET default '';
+
+-- -----------------------------------------
+-- #9286 zones table
+-- -----------------------------------------
+
+ALTER TABLE icinga_endpoints ADD COLUMN zone_object_id bigint default 0;
+ALTER TABLE icinga_endpointstatus ADD COLUMN zone_object_id bigint default 0;
+
+CREATE TABLE icinga_zones (
+ zone_id bigserial,
+ instance_id bigint default 0,
+ zone_object_id bigint default 0,
+ parent_zone_object_id bigint default 0,
+ config_type integer default 0,
+ is_global integer default 0,
+ CONSTRAINT PK_zone_id PRIMARY KEY (zone_id) ,
+ CONSTRAINT UQ_zones UNIQUE (instance_id,config_type,zone_object_id)
+) ;
+
+CREATE TABLE icinga_zonestatus (
+ zonestatus_id bigserial,
+ instance_id bigint default 0,
+ zone_object_id bigint default 0,
+ parent_zone_object_id bigint default 0,
+ status_update_time timestamp with time zone default '1970-01-01 00:00:00+00',
+ CONSTRAINT PK_zonestatus_id PRIMARY KEY (zonestatus_id) ,
+ CONSTRAINT UQ_zonestatus UNIQUE (zone_object_id)
+) ;
+
+-- -----------------------------------------
+-- #10392 original attributes
+-- -----------------------------------------
+
+ALTER TABLE icinga_servicestatus ADD COLUMN original_attributes TEXT default NULL;
+ALTER TABLE icinga_hoststatus ADD COLUMN original_attributes TEXT default NULL;
+
+-- -----------------------------------------
+-- #10436 deleted custom vars
+-- -----------------------------------------
+
+ALTER TABLE icinga_customvariables ADD COLUMN session_token INTEGER default NULL;
+ALTER TABLE icinga_customvariablestatus ADD COLUMN session_token INTEGER default NULL;
+
+CREATE INDEX cv_session_del_idx ON icinga_customvariables (session_token);
+CREATE INDEX cvs_session_del_idx ON icinga_customvariablestatus (session_token);
+
+-- -----------------------------------------
+-- #10431 comment/downtime name
+-- -----------------------------------------
+
+ALTER TABLE icinga_comments ADD COLUMN name TEXT default NULL;
+ALTER TABLE icinga_commenthistory ADD COLUMN name TEXT default NULL;
+
+ALTER TABLE icinga_scheduleddowntime ADD COLUMN name TEXT default NULL;
+ALTER TABLE icinga_downtimehistory ADD COLUMN name TEXT default NULL;
+
+-- -----------------------------------------
+-- update dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.14.0');
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.5.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.5.0.sql
new file mode 100644
index 0000000..063a812
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.5.0.sql
@@ -0,0 +1,85 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.5.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+-- -----------------------------------------
+-- #10069 IDO: check_source should not be a TEXT field
+-- -----------------------------------------
+
+ALTER TABLE icinga_hoststatus ALTER COLUMN check_source TYPE varchar(255);
+ALTER TABLE icinga_servicestatus ALTER COLUMN check_source TYPE varchar(255);
+ALTER TABLE icinga_statehistory ALTER COLUMN check_source TYPE varchar(255);
+
+-- -----------------------------------------
+-- #10070
+-- -----------------------------------------
+
+CREATE INDEX idx_comments_object_id on icinga_comments(object_id);
+CREATE INDEX idx_scheduleddowntime_object_id on icinga_scheduleddowntime(object_id);
+
+-- -----------------------------------------
+-- #10066
+-- -----------------------------------------
+
+CREATE INDEX idx_endpoints_object_id on icinga_endpoints(endpoint_object_id);
+CREATE INDEX idx_endpointstatus_object_id on icinga_endpointstatus(endpoint_object_id);
+
+CREATE INDEX idx_endpoints_zone_object_id on icinga_endpoints(zone_object_id);
+CREATE INDEX idx_endpointstatus_zone_object_id on icinga_endpointstatus(zone_object_id);
+
+CREATE INDEX idx_zones_object_id on icinga_zones(zone_object_id);
+CREATE INDEX idx_zonestatus_object_id on icinga_zonestatus(zone_object_id);
+
+CREATE INDEX idx_zones_parent_object_id on icinga_zones(parent_zone_object_id);
+CREATE INDEX idx_zonestatus_parent_object_id on icinga_zonestatus(parent_zone_object_id);
+
+-- -----------------------------------------
+-- #12258
+-- -----------------------------------------
+ALTER TABLE icinga_comments ADD COLUMN session_token INTEGER default NULL;
+ALTER TABLE icinga_scheduleddowntime ADD COLUMN session_token INTEGER default NULL;
+
+CREATE INDEX idx_comments_session_del ON icinga_comments (instance_id, session_token);
+CREATE INDEX idx_downtimes_session_del ON icinga_scheduleddowntime (instance_id, session_token);
+
+-- -----------------------------------------
+-- #12107
+-- -----------------------------------------
+CREATE INDEX idx_statehistory_cleanup on icinga_statehistory(instance_id, state_time);
+
+-- -----------------------------------------
+-- #12435
+-- -----------------------------------------
+ALTER TABLE icinga_commands ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_contactgroups ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_contacts ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_hostgroups ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_hosts ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_servicegroups ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_services ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_timeperiods ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_endpoints ADD config_hash VARCHAR(64) DEFAULT NULL;
+ALTER TABLE icinga_zones ADD config_hash VARCHAR(64) DEFAULT NULL;
+
+ALTER TABLE icinga_customvariables DROP session_token;
+ALTER TABLE icinga_customvariablestatus DROP session_token;
+
+CREATE INDEX idx_customvariables_object_id on icinga_customvariables(object_id);
+CREATE INDEX idx_contactgroup_members_object_id on icinga_contactgroup_members(contact_object_id);
+CREATE INDEX idx_hostgroup_members_object_id on icinga_hostgroup_members(host_object_id);
+CREATE INDEX idx_servicegroup_members_object_id on icinga_servicegroup_members(service_object_id);
+CREATE INDEX idx_servicedependencies_dependent_service_object_id on icinga_servicedependencies(dependent_service_object_id);
+CREATE INDEX idx_hostdependencies_dependent_host_object_id on icinga_hostdependencies(dependent_host_object_id);
+CREATE INDEX idx_service_contacts_service_id on icinga_service_contacts(service_id);
+CREATE INDEX idx_host_contacts_host_id on icinga_host_contacts(host_id);
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.14.1');
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.6.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.6.0.sql
new file mode 100644
index 0000000..aa538a6
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.6.0.sql
@@ -0,0 +1,161 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.6.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+-- -----------------------------------------
+-- #13221 IDO: PostgreSQL: Don't use timestamp with timezone for unix timestamp columns
+-- -----------------------------------------
+
+DROP FUNCTION IF EXISTS from_unixtime(bigint);
+CREATE FUNCTION from_unixtime(bigint) RETURNS timestamp AS $$
+ SELECT to_timestamp($1) AT TIME ZONE 'UTC' AS result
+$$ LANGUAGE sql;
+
+DROP FUNCTION IF EXISTS unix_timestamp(timestamp WITH TIME ZONE);
+CREATE OR REPLACE FUNCTION unix_timestamp(timestamp) RETURNS bigint AS '
+ SELECT CAST(EXTRACT(EPOCH FROM $1) AS bigint) AS result;
+' LANGUAGE sql;
+
+ALTER TABLE icinga_acknowledgements
+ ALTER COLUMN entry_time DROP DEFAULT, ALTER COLUMN entry_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_commenthistory
+ ALTER COLUMN entry_time DROP DEFAULT, ALTER COLUMN entry_time TYPE timestamp,
+ ALTER COLUMN comment_time DROP DEFAULT, ALTER COLUMN comment_time TYPE timestamp,
+ ALTER COLUMN expiration_time DROP DEFAULT, ALTER COLUMN expiration_time TYPE timestamp,
+ ALTER COLUMN deletion_time DROP DEFAULT, ALTER COLUMN deletion_time TYPE timestamp;
+
+ALTER TABLE icinga_comments
+ ALTER COLUMN entry_time DROP DEFAULT, ALTER COLUMN entry_time TYPE timestamp,
+ ALTER COLUMN comment_time DROP DEFAULT, ALTER COLUMN comment_time TYPE timestamp,
+ ALTER COLUMN expiration_time DROP DEFAULT, ALTER COLUMN expiration_time TYPE timestamp;
+
+ALTER TABLE icinga_conninfo
+ ALTER COLUMN connect_time DROP DEFAULT, ALTER COLUMN connect_time TYPE timestamp,
+ ALTER COLUMN disconnect_time DROP DEFAULT, ALTER COLUMN disconnect_time TYPE timestamp,
+ ALTER COLUMN last_checkin_time DROP DEFAULT, ALTER COLUMN last_checkin_time TYPE timestamp,
+ ALTER COLUMN data_start_time DROP DEFAULT, ALTER COLUMN data_start_time TYPE timestamp,
+ ALTER COLUMN data_end_time DROP DEFAULT, ALTER COLUMN data_end_time TYPE timestamp;
+
+ALTER TABLE icinga_contactnotificationmethods
+ ALTER COLUMN start_time DROP DEFAULT, ALTER COLUMN start_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_contactnotifications
+ ALTER COLUMN start_time DROP DEFAULT, ALTER COLUMN start_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_contactstatus
+ ALTER COLUMN status_update_time DROP DEFAULT, ALTER COLUMN status_update_time TYPE timestamp,
+ ALTER COLUMN last_host_notification DROP DEFAULT, ALTER COLUMN last_host_notification TYPE timestamp,
+ ALTER COLUMN last_service_notification DROP DEFAULT, ALTER COLUMN last_service_notification TYPE timestamp;
+
+ALTER TABLE icinga_customvariablestatus
+ ALTER COLUMN status_update_time DROP DEFAULT, ALTER COLUMN status_update_time TYPE timestamp;
+
+ALTER TABLE icinga_dbversion
+ ALTER COLUMN create_time DROP DEFAULT, ALTER COLUMN create_time TYPE timestamp,
+ ALTER COLUMN modify_time DROP DEFAULT, ALTER COLUMN modify_time TYPE timestamp;
+
+ALTER TABLE icinga_downtimehistory
+ ALTER COLUMN entry_time DROP DEFAULT, ALTER COLUMN entry_time TYPE timestamp,
+ ALTER COLUMN scheduled_start_time DROP DEFAULT, ALTER COLUMN scheduled_start_time TYPE timestamp,
+ ALTER COLUMN scheduled_end_time DROP DEFAULT, ALTER COLUMN scheduled_end_time TYPE timestamp,
+ ALTER COLUMN actual_start_time DROP DEFAULT, ALTER COLUMN actual_start_time TYPE timestamp,
+ ALTER COLUMN actual_end_time DROP DEFAULT, ALTER COLUMN actual_end_time TYPE timestamp,
+ ALTER COLUMN trigger_time DROP DEFAULT, ALTER COLUMN trigger_time TYPE timestamp;
+
+ALTER TABLE icinga_eventhandlers
+ ALTER COLUMN start_time DROP DEFAULT, ALTER COLUMN start_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_externalcommands
+ ALTER COLUMN entry_time DROP DEFAULT, ALTER COLUMN entry_time TYPE timestamp;
+
+ALTER TABLE icinga_flappinghistory
+ ALTER COLUMN event_time DROP DEFAULT, ALTER COLUMN event_time TYPE timestamp,
+ ALTER COLUMN comment_time DROP DEFAULT, ALTER COLUMN comment_time TYPE timestamp;
+
+ALTER TABLE icinga_hostchecks
+ ALTER COLUMN start_time DROP DEFAULT, ALTER COLUMN start_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_hoststatus
+ ALTER COLUMN status_update_time DROP DEFAULT, ALTER COLUMN status_update_time TYPE timestamp,
+ ALTER COLUMN last_check DROP DEFAULT, ALTER COLUMN last_check TYPE timestamp,
+ ALTER COLUMN next_check DROP DEFAULT, ALTER COLUMN next_check TYPE timestamp,
+ ALTER COLUMN last_state_change DROP DEFAULT, ALTER COLUMN last_state_change TYPE timestamp,
+ ALTER COLUMN last_hard_state_change DROP DEFAULT, ALTER COLUMN last_hard_state_change TYPE timestamp,
+ ALTER COLUMN last_time_up DROP DEFAULT, ALTER COLUMN last_time_up TYPE timestamp,
+ ALTER COLUMN last_time_down DROP DEFAULT, ALTER COLUMN last_time_down TYPE timestamp,
+ ALTER COLUMN last_time_unreachable DROP DEFAULT, ALTER COLUMN last_time_unreachable TYPE timestamp,
+ ALTER COLUMN last_notification DROP DEFAULT, ALTER COLUMN last_notification TYPE timestamp,
+ ALTER COLUMN next_notification DROP DEFAULT, ALTER COLUMN next_notification TYPE timestamp;
+
+ALTER TABLE icinga_logentries
+ ALTER COLUMN logentry_time DROP DEFAULT, ALTER COLUMN logentry_time TYPE timestamp,
+ ALTER COLUMN entry_time DROP DEFAULT, ALTER COLUMN entry_time TYPE timestamp;
+
+ALTER TABLE icinga_notifications
+ ALTER COLUMN start_time DROP DEFAULT, ALTER COLUMN start_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_processevents
+ ALTER COLUMN event_time DROP DEFAULT, ALTER COLUMN event_time TYPE timestamp;
+
+ALTER TABLE icinga_programstatus
+ ALTER COLUMN status_update_time DROP DEFAULT, ALTER COLUMN status_update_time TYPE timestamp,
+ ALTER COLUMN program_start_time DROP DEFAULT, ALTER COLUMN program_start_time TYPE timestamp,
+ ALTER COLUMN program_end_time DROP DEFAULT, ALTER COLUMN program_end_time TYPE timestamp,
+ ALTER COLUMN last_command_check DROP DEFAULT, ALTER COLUMN last_command_check TYPE timestamp,
+ ALTER COLUMN last_log_rotation DROP DEFAULT, ALTER COLUMN last_log_rotation TYPE timestamp,
+ ALTER COLUMN disable_notif_expire_time DROP DEFAULT, ALTER COLUMN disable_notif_expire_time TYPE timestamp;
+
+ALTER TABLE icinga_scheduleddowntime
+ ALTER COLUMN entry_time DROP DEFAULT, ALTER COLUMN entry_time TYPE timestamp,
+ ALTER COLUMN scheduled_start_time DROP DEFAULT, ALTER COLUMN scheduled_start_time TYPE timestamp,
+ ALTER COLUMN scheduled_end_time DROP DEFAULT, ALTER COLUMN scheduled_end_time TYPE timestamp,
+ ALTER COLUMN actual_start_time DROP DEFAULT, ALTER COLUMN actual_start_time TYPE timestamp,
+ ALTER COLUMN trigger_time DROP DEFAULT, ALTER COLUMN trigger_time TYPE timestamp;
+
+ALTER TABLE icinga_servicechecks
+ ALTER COLUMN start_time DROP DEFAULT, ALTER COLUMN start_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_servicestatus
+ ALTER COLUMN status_update_time DROP DEFAULT, ALTER COLUMN status_update_time TYPE timestamp,
+ ALTER COLUMN last_check DROP DEFAULT, ALTER COLUMN last_check TYPE timestamp,
+ ALTER COLUMN next_check DROP DEFAULT, ALTER COLUMN next_check TYPE timestamp,
+ ALTER COLUMN last_state_change DROP DEFAULT, ALTER COLUMN last_state_change TYPE timestamp,
+ ALTER COLUMN last_hard_state_change DROP DEFAULT, ALTER COLUMN last_hard_state_change TYPE timestamp,
+ ALTER COLUMN last_time_ok DROP DEFAULT, ALTER COLUMN last_time_ok TYPE timestamp,
+ ALTER COLUMN last_time_warning DROP DEFAULT, ALTER COLUMN last_time_warning TYPE timestamp,
+ ALTER COLUMN last_time_unknown DROP DEFAULT, ALTER COLUMN last_time_unknown TYPE timestamp,
+ ALTER COLUMN last_time_critical DROP DEFAULT, ALTER COLUMN last_time_critical TYPE timestamp,
+ ALTER COLUMN last_notification DROP DEFAULT, ALTER COLUMN last_notification TYPE timestamp,
+ ALTER COLUMN next_notification DROP DEFAULT, ALTER COLUMN next_notification TYPE timestamp;
+
+ALTER TABLE icinga_statehistory
+ ALTER COLUMN state_time DROP DEFAULT, ALTER COLUMN state_time TYPE timestamp;
+
+ALTER TABLE icinga_systemcommands
+ ALTER COLUMN start_time DROP DEFAULT, ALTER COLUMN start_time TYPE timestamp,
+ ALTER COLUMN end_time DROP DEFAULT, ALTER COLUMN end_time TYPE timestamp;
+
+ALTER TABLE icinga_endpointstatus
+ ALTER COLUMN status_update_time DROP DEFAULT, ALTER COLUMN status_update_time TYPE timestamp;
+
+ALTER TABLE icinga_zonestatus
+ ALTER COLUMN status_update_time DROP DEFAULT, ALTER COLUMN status_update_time TYPE timestamp;
+
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.14.2');
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.8.0.sql b/lib/db_ido_pgsql/schema/upgrade/2.8.0.sql
new file mode 100644
index 0000000..31ab324
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.8.0.sql
@@ -0,0 +1,32 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.8.0
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+ALTER TABLE icinga_downtimehistory DROP CONSTRAINT IF EXISTS UQ_downtimehistory;
+ALTER TABLE icinga_scheduleddowntime DROP CONSTRAINT IF EXISTS UQ_scheduleddowntime;
+ALTER TABLE icinga_commenthistory DROP CONSTRAINT IF EXISTS UQ_commenthistory;
+ALTER TABLE icinga_comments DROP CONSTRAINT IF EXISTS UQ_comments;
+
+-- -----------------------------------------
+-- #5458 IDO: Improve downtime removal/cancel
+-- -----------------------------------------
+
+CREATE INDEX idx_downtimehistory_remove ON icinga_downtimehistory (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+CREATE INDEX idx_scheduleddowntime_remove ON icinga_scheduleddowntime (object_id, entry_time, scheduled_start_time, scheduled_end_time);
+
+-- -----------------------------------------
+-- #5492
+-- -----------------------------------------
+
+CREATE INDEX idx_commenthistory_remove ON icinga_commenthistory (object_id, entry_time);
+CREATE INDEX idx_comments_remove ON icinga_comments (object_id, entry_time);
+-- -----------------------------------------
+-- set dbversion
+-- -----------------------------------------
+
+SELECT updatedbversion('1.14.3');
diff --git a/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql b/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql
new file mode 100644
index 0000000..05202c0
--- /dev/null
+++ b/lib/db_ido_pgsql/schema/upgrade/2.8.1.sql
@@ -0,0 +1,19 @@
+-- -----------------------------------------
+-- upgrade path for Icinga 2.8.1 (fix for fresh 2.8.0 installation only)
+--
+-- -----------------------------------------
+-- Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+
+--
+-- Please check https://docs.icinga.com for upgrading information!
+-- -----------------------------------------
+
+ALTER TABLE icinga_downtimehistory DROP CONSTRAINT IF EXISTS UQ_downtimehistory;
+ALTER TABLE icinga_scheduleddowntime DROP CONSTRAINT IF EXISTS UQ_scheduleddowntime;
+ALTER TABLE icinga_commenthistory DROP CONSTRAINT IF EXISTS UQ_commenthistory;
+ALTER TABLE icinga_comments DROP CONSTRAINT IF EXISTS UQ_comments;
+
+-- -----------------------------------------
+-- set dbversion (same as 2.8.0)
+-- -----------------------------------------
+
+SELECT updatedbversion('1.14.3');