Implement a check for IDO database connections

fixes #8688
This commit is contained in:
Gunnar Beutner 2015-03-12 09:39:53 +01:00
parent 94fde1a6b2
commit a4d37132bf
12 changed files with 284 additions and 28 deletions

View File

@ -52,12 +52,23 @@ The `cluster` check command does not support any vars.
Check command for the built-in `cluster-zone` check.
Cluster Attributes:
Custom Attributes:
Name | Description
-------------|---------------
cluster_zone | **Optional.** The zone name. Defaults to "$host.name$".
### <a id="itl-icinga-ido"></a> ido
Check command for the built-in `ido` check.
Custom Attributes:
Name | Description
-------------|---------------
ido_type | **Required.** The type of the IDO connection object. Can be either "IdoMysqlConnection" or "IdoPgsqlConnection".
ido_name | **Required.** The name of the IDO connection object.
# <a id="plugin-check-commands"></a> Plugin Check Commands
The Plugin Check Commands provides example configuration for plugin check commands

View File

@ -18,13 +18,15 @@
mkclass_target(dbconnection.ti dbconnection.thpp)
mkembedconfig_target(db_ido-type.conf db_ido-type.cpp)
mkembedconfig_target(db_ido-check.conf db_ido-check.cpp)
set(db_ido_SOURCES
commanddbobject.cpp dbconnection.cpp dbconnection.thpp dbconnection.thpp
db_ido-type.cpp dbevents.cpp dbobject.cpp dbquery.cpp dbreference.cpp dbtype.cpp
dbvalue.cpp endpointdbobject.cpp hostdbobject.cpp hostgroupdbobject.cpp
servicedbobject.cpp servicegroupdbobject.cpp timeperioddbobject.cpp
userdbobject.cpp usergroupdbobject.cpp
db_ido-type.cpp db_ido-check.cpp dbevents.cpp dbobject.cpp dbquery.cpp
dbreference.cpp dbtype.cpp dbvalue.cpp endpointdbobject.cpp hostdbobject.cpp
hostgroupdbobject.cpp idochecktask.cpp servicedbobject.cpp
servicegroupdbobject.cpp timeperioddbobject.cpp userdbobject.cpp
usergroupdbobject.cpp
)
if(ICINGA2_UNITY_BUILD)

View File

@ -0,0 +1,26 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
template CheckCommand "ido-check-command" {
execute = IdoCheck
}
object CheckCommand "ido" {
import "ido-check-command"
}

View File

@ -39,6 +39,10 @@ REGISTER_SCRIPTFUNCTION(ValidateFailoverTimeout, &DbConnection::ValidateFailover
Timer::Ptr DbConnection::m_ProgramStatusTimer;
boost::once_flag DbConnection::m_OnceFlag = BOOST_ONCE_INIT;
DbConnection::DbConnection(void)
: m_QueryStats(15 * 60)
{ }
void DbConnection::OnConfigLoaded(void)
{
DynamicObject::OnConfigLoaded();
@ -443,3 +447,17 @@ void DbConnection::ValidateFailoverTimeout(const String& location, const DbConne
location + ": Failover timeout minimum is 60s.", object->GetDebugInfo()));
}
}
void DbConnection::IncreaseQueryCount(void)
{
double now = Utility::GetTime();
boost::mutex::scoped_lock lock(m_StatsMutex);
m_QueryStats.InsertValue(now, 1);
}
int DbConnection::GetQueryCount(RingBuffer::SizeType span) const
{
boost::mutex::scoped_lock lock(m_StatsMutex);
return m_QueryStats.GetValues(span);
}

View File

@ -25,7 +25,9 @@
#include "db_ido/dbobject.hpp"
#include "db_ido/dbquery.hpp"
#include "base/timer.hpp"
#include "base/ringbuffer.hpp"
#include <boost/thread/once.hpp>
#include <boost/thread/mutex.hpp>
#define IDO_CURRENT_SCHEMA_VERSION "1.13.0"
#define IDO_COMPAT_SCHEMA_VERSION "1.12.0"
@ -43,6 +45,8 @@ class I2_DB_IDO_API DbConnection : public ObjectImpl<DbConnection>
public:
DECLARE_OBJECT(DbConnection);
DbConnection(void);
static void InitializeDbTimer(void);
void SetObjectID(const DbObject::Ptr& dbobj, const DbReference& dbref);
@ -67,6 +71,8 @@ public:
void SetStatusUpdate(const DbObject::Ptr& dbobj, bool hasupdate);
bool GetStatusUpdate(const DbObject::Ptr& dbobj) const;
int GetQueryCount(RingBuffer::SizeType span) const;
static void ValidateFailoverTimeout(const String& location, const DbConnection::Ptr& object);
protected:
@ -87,6 +93,8 @@ protected:
void PrepareDatabase(void);
void IncreaseQueryCount(void);
private:
std::map<DbObject::Ptr, DbReference> m_ObjectIDs;
std::map<std::pair<DbType::Ptr, DbReference>, DbReference> m_InsertIDs;
@ -105,6 +113,9 @@ private:
static void InsertRuntimeVariable(const String& key, const Value& value);
static void ProgramStatusHandler(void);
mutable boost::mutex m_StatsMutex;
RingBuffer m_QueryStats;
};
struct database_error : virtual std::exception, virtual boost::exception { };

View File

@ -48,6 +48,12 @@ abstract class DbConnection : DynamicObject
[config] double failover_timeout {
default {{{ return 60; }}}
};
String schema_version;
bool connected;
bool should_connect {
default {{{ return true; }}}
};
};
}

115
lib/db_ido/idochecktask.cpp Normal file
View File

@ -0,0 +1,115 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#include "db_ido/idochecktask.hpp"
#include "icinga/host.hpp"
#include "icinga/checkcommand.hpp"
#include "icinga/macroprocessor.hpp"
#include "icinga/perfdatavalue.hpp"
#include "remote/apilistener.hpp"
#include "remote/endpoint.hpp"
#include "remote/zone.hpp"
#include "base/function.hpp"
#include "base/utility.hpp"
#include "base/dynamictype.hpp"
#include "base/convert.hpp"
#include <boost/foreach.hpp>
using namespace icinga;
REGISTER_SCRIPTFUNCTION(IdoCheck, &IdoCheckTask::ScriptFunc);
void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr,
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros)
{
CheckCommand::Ptr commandObj = checkable->GetCheckCommand();
Value raw_command = commandObj->GetCommandLine();
Host::Ptr host;
Service::Ptr service;
tie(host, service) = GetHostService(checkable);
MacroProcessor::ResolverList resolvers;
if (service)
resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(std::make_pair("command", commandObj));
resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
String idoType = MacroProcessor::ResolveMacros("$ido_type$", resolvers, checkable->GetLastCheckResult(),
NULL, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
if (resolvedMacros && !useResolvedMacros)
return;
String idoName = MacroProcessor::ResolveMacros("$ido_name$", resolvers, checkable->GetLastCheckResult(),
NULL, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
if (resolvedMacros && !useResolvedMacros)
return;
if (idoName.IsEmpty()) {
cr->SetOutput("Macro 'ido_name' must be set.");
cr->SetState(ServiceUnknown);
checkable->ProcessCheckResult(cr);
return;
}
Type::Ptr type = Type::GetByName(idoType);
if (!type || !Type::GetByName("DbConnection")->IsAssignableFrom(type)) {
cr->SetOutput("IDO type '" + idoType + "' is invalid.");
cr->SetState(ServiceUnknown);
checkable->ProcessCheckResult(cr);
return;
}
DynamicType::Ptr dtype = DynamicType::GetByName(idoType);
VERIFY(dtype);
DbConnection::Ptr conn = static_pointer_cast<DbConnection>(dtype->GetObject(idoName));
double qps = conn->GetQueryCount(60) / 60.0;
if (!conn->GetConnected() && conn->GetShouldConnect()) {
cr->SetOutput("Could not connect to the database server.");
cr->SetState(ServiceCritical);
} else {
String schema_version = conn->GetSchemaVersion();
if (Utility::CompareVersion(IDO_CURRENT_SCHEMA_VERSION, schema_version) < 0) {
cr->SetOutput("Outdated schema version: " + schema_version + "; Latest version: " IDO_CURRENT_SCHEMA_VERSION);
cr->SetState(ServiceWarning);
} else {
std::ostringstream msgbuf;
msgbuf << "Connected to the database server; queries per second: " << std::fixed << std::setprecision(3) << qps;
cr->SetOutput(msgbuf.str());
cr->SetState(ServiceOK);
}
}
Array::Ptr perfdata = new Array();
perfdata->Add(new PerfdataValue("queries", qps));
perfdata->Add(new PerfdataValue("queries_1min", conn->GetQueryCount(60)));
perfdata->Add(new PerfdataValue("queries_5mins", conn->GetQueryCount(5 * 60)));
perfdata->Add(new PerfdataValue("queries_15mins", conn->GetQueryCount(15 * 60)));
cr->SetPerformanceData(perfdata);
checkable->ProcessCheckResult(cr);
}

View File

@ -0,0 +1,46 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-2015 Icinga Development Team (http://www.icinga.org) *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software Foundation *
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
******************************************************************************/
#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(void);
};
}
#endif /* IDOCHECKTASK_H */

View File

@ -38,7 +38,7 @@ REGISTER_TYPE(IdoMysqlConnection);
REGISTER_STATSFUNCTION(IdoMysqlConnectionStats, &IdoMysqlConnection::StatsFunc);
IdoMysqlConnection::IdoMysqlConnection(void)
: m_QueryQueue(500000), m_Connected(false)
: m_QueryQueue(500000)
{ }
void IdoMysqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)
@ -49,7 +49,7 @@ void IdoMysqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::P
size_t items = idomysqlconnection->m_QueryQueue.GetLength();
Dictionary::Ptr stats = new Dictionary();
stats->Set("version", IDO_CURRENT_SCHEMA_VERSION);
stats->Set("version", idomysqlconnection->GetSchemaVersion());
stats->Set("instance_name", idomysqlconnection->GetInstanceName());
stats->Set("query_queue_items", items);
@ -65,7 +65,7 @@ void IdoMysqlConnection::Resume(void)
{
DbConnection::Resume();
m_Connected = false;
SetConnected(false);
m_QueryQueue.SetExceptionCallback(boost::bind(&IdoMysqlConnection::ExceptionHandler, this, _1));
@ -102,10 +102,10 @@ void IdoMysqlConnection::ExceptionHandler(boost::exception_ptr exp)
boost::mutex::scoped_lock lock(m_ConnectionMutex);
if (m_Connected) {
if (GetConnected()) {
mysql_close(&m_Connection);
m_Connected = false;
SetConnected(false);
}
}
@ -120,13 +120,13 @@ void IdoMysqlConnection::Disconnect(void)
boost::mutex::scoped_lock lock(m_ConnectionMutex);
if (!m_Connected)
if (!GetConnected())
return;
Query("COMMIT");
mysql_close(&m_Connection);
m_Connected = false;
SetConnected(false);
}
void IdoMysqlConnection::TxTimerHandler(void)
@ -143,7 +143,7 @@ void IdoMysqlConnection::InternalNewTransaction(void)
{
boost::mutex::scoped_lock lock(m_ConnectionMutex);
if (!m_Connected)
if (!GetConnected())
return;
Query("COMMIT");
@ -161,6 +161,8 @@ void IdoMysqlConnection::Reconnect(void)
CONTEXT("Reconnecting to MySQL IDO database '" + GetName() + "'");
SetShouldConnect(true);
std::vector<DbObject::Ptr> active_dbobjs;
{
@ -168,13 +170,13 @@ void IdoMysqlConnection::Reconnect(void)
bool reconnect = false;
if (m_Connected) {
if (GetConnected()) {
/* Check if we're really still connected */
if (mysql_ping(&m_Connection) == 0)
return;
mysql_close(&m_Connection);
m_Connected = false;
SetConnected(false);
reconnect = true;
}
@ -213,7 +215,7 @@ void IdoMysqlConnection::Reconnect(void)
BOOST_THROW_EXCEPTION(std::runtime_error(mysql_error(&m_Connection)));
}
m_Connected = true;
SetConnected(true);
String dbVersionName = "idoutils";
IdoMysqlResult result = Query("SELECT version FROM " + GetTablePrefix() + "dbversion WHERE name='" + Escape(dbVersionName) + "'");
@ -222,7 +224,7 @@ void IdoMysqlConnection::Reconnect(void)
if (!row) {
mysql_close(&m_Connection);
m_Connected = false;
SetConnected(false);
Log(LogCritical, "IdoMysqlConnection", "Schema does not provide any valid version! Verify your schema installation.");
@ -234,9 +236,11 @@ void IdoMysqlConnection::Reconnect(void)
String version = row->Get("version");
SetSchemaVersion(version);
if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) {
mysql_close(&m_Connection);
m_Connected = false;
SetConnected(false);
Log(LogCritical, "IdoMysqlConnection")
<< "Schema version '" << version << "' does not match the required version '"
@ -293,7 +297,8 @@ void IdoMysqlConnection::Reconnect(void)
if (status_update_age < GetFailoverTimeout()) {
mysql_close(&m_Connection);
m_Connected = false;
SetConnected(false);
SetShouldConnect(false);
return;
}
@ -304,7 +309,7 @@ void IdoMysqlConnection::Reconnect(void)
<< "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out.";
mysql_close(&m_Connection);
m_Connected = false;
SetConnected(false);
return;
}
@ -374,6 +379,8 @@ IdoMysqlResult IdoMysqlConnection::Query(const String& query)
Log(LogDebug, "IdoMysqlConnection")
<< "Query: " << query;
IncreaseQueryCount();
if (mysql_query(&m_Connection, query.CStr()) != 0) {
std::ostringstream msgbuf;
String message = mysql_error(&m_Connection);
@ -484,7 +491,7 @@ void IdoMysqlConnection::ActivateObject(const DbObject::Ptr& dbobj)
void IdoMysqlConnection::InternalActivateObject(const DbObject::Ptr& dbobj)
{
if (!m_Connected)
if (!GetConnected())
return;
DbReference dbref = GetObjectID(dbobj);
@ -513,7 +520,7 @@ void IdoMysqlConnection::DeactivateObject(const DbObject::Ptr& dbobj)
{
boost::mutex::scoped_lock lock(m_ConnectionMutex);
if (!m_Connected)
if (!GetConnected())
return;
DbReference dbref = GetObjectID(dbobj);
@ -606,7 +613,7 @@ void IdoMysqlConnection::InternalExecuteQuery(const DbQuery& query, DbQueryType
if ((query.Category & GetCategories()) == 0)
return;
if (!m_Connected)
if (!GetConnected())
return;
if (query.Object && query.Object->GetObject()->GetExtension("agent_check").ToBool())
@ -746,7 +753,7 @@ void IdoMysqlConnection::InternalCleanUpExecuteQuery(const String& table, const
{
boost::mutex::scoped_lock lock(m_ConnectionMutex);
if (!m_Connected)
if (!GetConnected())
return;
Query("DELETE FROM " + GetTablePrefix() + table + " WHERE instance_id = " +

View File

@ -63,7 +63,6 @@ private:
WorkQueue m_QueryQueue;
boost::mutex m_ConnectionMutex;
bool m_Connected;
MYSQL m_Connection;
int m_AffectedRows;

View File

@ -51,7 +51,7 @@ void IdoPgsqlConnection::StatsFunc(const Dictionary::Ptr& status, const Array::P
size_t items = idopgsqlconnection->m_QueryQueue.GetLength();
Dictionary::Ptr stats = new Dictionary();
stats->Set("version", IDO_CURRENT_SCHEMA_VERSION);
stats->Set("version", idopgsqlconnection->GetSchemaVersion());
stats->Set("instance_name", idopgsqlconnection->GetInstanceName());
stats->Set("query_queue_items", items);
@ -67,6 +67,7 @@ void IdoPgsqlConnection::Resume(void)
{
DbConnection::Resume();
SetConnected(false);
m_Connection = NULL;
m_QueryQueue.SetExceptionCallback(boost::bind(&IdoPgsqlConnection::ExceptionHandler, this, _1));
@ -106,6 +107,7 @@ void IdoPgsqlConnection::ExceptionHandler(boost::exception_ptr exp)
if (m_Connection) {
PQfinish(m_Connection);
SetConnected(false);
m_Connection = NULL;
}
}
@ -127,6 +129,7 @@ void IdoPgsqlConnection::Disconnect(void)
Query("COMMIT");
PQfinish(m_Connection);
SetConnected(false);
m_Connection = NULL;
}
@ -175,6 +178,7 @@ void IdoPgsqlConnection::Reconnect(void)
Query("SELECT 1");
return;
} catch (const std::exception&) {
SetConnected(false);
PQfinish(m_Connection);
m_Connection = NULL;
reconnect = true;
@ -203,9 +207,12 @@ void IdoPgsqlConnection::Reconnect(void)
if (!m_Connection)
return;
SetConnected(true);
if (PQstatus(m_Connection) != CONNECTION_OK) {
String message = PQerrorMessage(m_Connection);
PQfinish(m_Connection);
SetConnected(false);
m_Connection = NULL;
Log(LogCritical, "IdoPgsqlConnection")
@ -222,18 +229,21 @@ void IdoPgsqlConnection::Reconnect(void)
if (!row) {
PQfinish(m_Connection);
SetConnected(false);
m_Connection = NULL;
Log(LogCritical, "IdoPgsqlConnection", "Schema does not provide any valid version! Verify your schema installation.");
Application::RequestShutdown(EXIT_FAILURE);
return;
BOOST_THROW_EXCEPTION(std::runtime_error("Schema does not provide any valid version! Verify your schema installation."));
}
String version = row->Get("version");
SetSchemaVersion(version);
if (Utility::CompareVersion(IDO_COMPAT_SCHEMA_VERSION, version) < 0) {
PQfinish(m_Connection);
SetConnected(false);
m_Connection = NULL;
Log(LogCritical, "IdoPgsqlConnection")
@ -288,6 +298,7 @@ void IdoPgsqlConnection::Reconnect(void)
if (status_update_age < GetFailoverTimeout()) {
PQfinish(m_Connection);
SetConnected(false);
m_Connection = NULL;
return;
@ -299,6 +310,7 @@ void IdoPgsqlConnection::Reconnect(void)
<< "Local endpoint '" << my_endpoint->GetName() << "' is not authoritative, bailing out.";
PQfinish(m_Connection);
SetConnected(false);
m_Connection = NULL;
return;
@ -369,6 +381,8 @@ IdoPgsqlResult IdoPgsqlConnection::Query(const String& query)
Log(LogDebug, "IdoPgsqlConnection")
<< "Query: " << query;
IncreaseQueryCount();
PGresult *result = PQexec(m_Connection, query.CStr());
if (!result) {

View File

@ -30,5 +30,6 @@ macro(MKEMBEDCONFIG_TARGET EmbedInput EmbedOutput)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS mkembedconfig ${EmbedInput}
)
set_property(SOURCE ${CMAKE_CURRENT_BINARY_DIR}/${EmbedOutput} PROPERTY EXCLUDE_UNITY_BUILD TRUE)
endmacro()