diff options
Diffstat (limited to '')
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'); |