icinga2/lib/icingadb/icingadb.cpp

249 lines
5.5 KiB
C++
Raw Normal View History

2019-11-02 14:00:06 +01:00
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2017-09-25 14:41:43 +02:00
2019-10-29 18:36:16 +01:00
#include "icingadb/icingadb.hpp"
#include "icingadb/icingadb-ti.cpp"
#include "icingadb/redisconnection.hpp"
2017-09-25 14:41:43 +02:00
#include "remote/eventqueue.hpp"
#include "base/json.hpp"
#include "icinga/checkable.hpp"
#include "icinga/host.hpp"
2017-10-12 17:46:06 +02:00
#include <boost/algorithm/string.hpp>
2019-11-02 18:01:31 +01:00
#include <memory>
#include <utility>
2017-09-25 14:41:43 +02:00
using namespace icinga;
2017-10-12 17:46:06 +02:00
#define MAX_EVENTS_DEFAULT 5000
using Prio = RedisConnection::QueryPriority;
static const char * const l_LuaPublishStats = R"EOF(
local xa = {'XADD', KEYS[1], '*'}
for i = 1, #ARGV do
table.insert(xa, ARGV[i])
end
local id = redis.call(unpack(xa))
local xr = redis.call('XRANGE', KEYS[1], '-', '+')
for i = 1, #xr - 1 do
redis.call('XDEL', KEYS[1], xr[i][1])
end
return id
)EOF";
REGISTER_TYPE(IcingaDB);
2017-09-25 14:41:43 +02:00
IcingaDB::IcingaDB()
2019-11-02 18:01:31 +01:00
: m_Rcon(nullptr)
2017-10-12 11:47:49 +02:00
{
m_Rcon = nullptr;
m_WorkQueue.SetName("IcingaDB");
2019-06-14 11:52:34 +02:00
m_PrefixConfigObject = "icinga:config:";
m_PrefixConfigCheckSum = "icinga:checksum:";
2019-09-17 11:20:52 +02:00
m_PrefixStateObject = "icinga:config:state:";
2017-10-12 11:47:49 +02:00
}
2017-09-25 14:41:43 +02:00
/**
* Starts the component.
*/
void IcingaDB::Start(bool runtimeCreated)
2017-09-25 14:41:43 +02:00
{
ObjectImpl<IcingaDB>::Start(runtimeCreated);
2017-09-25 14:41:43 +02:00
Log(LogInformation, "IcingaDB")
2018-05-15 14:43:01 +02:00
<< "'" << GetName() << "' started.";
2017-09-25 14:41:43 +02:00
m_ConfigDumpInProgress = false;
m_ConfigDumpDone = false;
m_Rcon = new RedisConnection(GetHost(), GetPort(), GetPath(), GetPassword(), GetDbIndex());
m_Rcon->Start();
2017-09-25 14:41:43 +02:00
m_WorkQueue.SetExceptionCallback([this](boost::exception_ptr exp) { ExceptionHandler(std::move(exp)); });
2017-09-25 14:41:43 +02:00
m_ReconnectTimer = new Timer();
m_ReconnectTimer->SetInterval(15);
m_ReconnectTimer->OnTimerExpired.connect([this](const Timer * const&) { ReconnectTimerHandler(); });
2017-09-25 14:41:43 +02:00
m_ReconnectTimer->Start();
m_ReconnectTimer->Reschedule(0);
m_StatsTimer = new Timer();
m_StatsTimer->SetInterval(1);
m_StatsTimer->OnTimerExpired.connect([this](const Timer * const&) { PublishStatsTimerHandler(); });
2017-09-25 14:41:43 +02:00
m_StatsTimer->Start();
m_WorkQueue.SetName("IcingaDB");
2017-10-02 09:59:11 +02:00
boost::thread thread(&IcingaDB::HandleEvents, this);
2017-09-25 14:41:43 +02:00
thread.detach();
2017-09-25 14:41:43 +02:00
}
void IcingaDB::ExceptionHandler(boost::exception_ptr exp)
2017-09-25 14:41:43 +02:00
{
Log(LogCritical, "IcingaDB", "Exception during redis query. Verify that Redis is operational.");
2017-09-25 14:41:43 +02:00
Log(LogDebug, "IcingaDB")
2018-05-15 14:43:01 +02:00
<< "Exception during redis operation: " << DiagnosticInformation(exp);
2017-09-25 14:41:43 +02:00
}
void IcingaDB::ReconnectTimerHandler()
2017-09-25 14:41:43 +02:00
{
m_WorkQueue.Enqueue([this]() { TryToReconnect(); });
2017-09-25 14:41:43 +02:00
}
void IcingaDB::TryToReconnect()
2017-09-25 14:41:43 +02:00
{
AssertOnWorkQueue();
if (m_ConfigDumpDone)
2017-09-25 14:41:43 +02:00
return;
else
m_Rcon->Start();
2017-09-25 14:41:43 +02:00
if (!m_Rcon || !m_Rcon->IsConnected())
return;
2018-10-26 14:07:07 +02:00
if (m_ConfigDumpInProgress || m_ConfigDumpDone)
2017-09-25 14:41:43 +02:00
return;
2017-10-12 11:47:49 +02:00
2017-09-25 14:41:43 +02:00
/* Config dump */
m_ConfigDumpInProgress = true;
2019-01-28 15:19:10 +01:00
PublishStats();
2017-09-25 14:41:43 +02:00
UpdateAllConfigObjects();
m_ConfigDumpDone = true;
2017-09-25 14:41:43 +02:00
m_ConfigDumpInProgress = false;
}
void IcingaDB::PublishStatsTimerHandler(void)
2017-09-25 14:41:43 +02:00
{
m_WorkQueue.Enqueue([this]() { PublishStats(); });
2017-09-25 14:41:43 +02:00
}
void IcingaDB::PublishStats()
2017-09-25 14:41:43 +02:00
{
AssertOnWorkQueue();
if (!m_Rcon || !m_Rcon->IsConnected())
2018-10-26 14:07:07 +02:00
return;
2018-06-08 11:38:36 +02:00
Dictionary::Ptr status = GetStats();
2019-01-28 15:19:10 +01:00
status->Set("config_dump_in_progress", m_ConfigDumpInProgress);
status->Set("timestamp", TimestampToMilliseconds(Utility::GetTime()));
2017-09-25 14:41:43 +02:00
std::vector<String> eval ({"EVAL", l_LuaPublishStats, "1", "icinga:stats"});
{
ObjectLock statusLock (status);
for (auto& kv : status) {
eval.emplace_back(kv.first);
eval.emplace_back(JsonEncode(kv.second));
}
}
m_Rcon->FireAndForgetQuery(std::move(eval), Prio::Heartbeat);
2017-09-25 14:41:43 +02:00
}
void IcingaDB::HandleEvents()
2017-09-25 14:41:43 +02:00
{
String queueName = Utility::NewUniqueID();
EventQueue::Ptr queue = new EventQueue(queueName);
EventQueue::Register(queueName, queue);
std::set<String> types;
types.insert("CheckResult");
types.insert("AcknowledgementSet");
types.insert("AcknowledgementCleared");
queue->SetTypes(types);
queue->AddClient(this);
for (;;) {
Dictionary::Ptr event = queue->WaitForEvent(this);
if (!event)
continue;
m_WorkQueue.Enqueue([this, event]() { SendEvent(event); });
2017-09-25 14:41:43 +02:00
}
queue->RemoveClient(this);
EventQueue::UnregisterIfUnused(queueName, queue);
}
void IcingaDB::SendEvent(const Dictionary::Ptr& event)
2017-10-16 12:03:49 +02:00
{
AssertOnWorkQueue();
if (!m_Rcon || !m_Rcon->IsConnected())
2018-10-26 14:07:07 +02:00
return;
String type = event->Get("type");
2018-11-15 17:04:03 +01:00
if (type == "CheckResult") {
Checkable::Ptr checkable;
2019-11-02 18:01:31 +01:00
2018-11-15 17:04:03 +01:00
if (event->Contains("service")) {
checkable = Service::GetByNamePair(event->Get("host"), event->Get("service"));
} else {
checkable = Host::GetByName(event->Get("host"));
}
2019-11-02 18:01:31 +01:00
2018-11-15 17:04:03 +01:00
// Update State for icingaweb
m_WorkQueue.Enqueue([this, checkable]() { UpdateState(checkable); });
2018-11-15 17:04:03 +01:00
}
if (type.Contains("Acknowledgement")) {
Checkable::Ptr checkable;
if (event->Contains("service")) {
checkable = Service::GetByNamePair(event->Get("host"), event->Get("service"));
event->Set("service_id", GetObjectIdentifier(checkable));
} else {
checkable = Host::GetByName(event->Get("host"));
event->Set("host_id", GetObjectIdentifier(checkable));
}
if (type == "AcknowledgementSet") {
Timestamp entry = 0;
Comment::Ptr AckComment;
2019-11-02 18:01:31 +01:00
for (const Comment::Ptr& c : checkable->GetComments()) {
if (c->GetEntryType() == CommentAcknowledgement) {
if (c->GetEntryTime() > entry) {
entry = c->GetEntryTime();
AckComment = c;
StateChangeHandler(checkable);
}
}
}
2019-11-02 18:01:31 +01:00
event->Set("comment_id", GetObjectIdentifier(AckComment));
}
}
2017-10-16 12:03:49 +02:00
}
2017-09-25 14:41:43 +02:00
void IcingaDB::Stop(bool runtimeRemoved)
2017-09-25 14:41:43 +02:00
{
Log(LogInformation, "IcingaDB")
2018-05-15 14:43:01 +02:00
<< "'" << GetName() << "' stopped.";
2017-09-25 14:41:43 +02:00
ObjectImpl<IcingaDB>::Stop(runtimeRemoved);
2017-09-25 14:41:43 +02:00
}
void IcingaDB::AssertOnWorkQueue()
2017-09-25 14:41:43 +02:00
{
ASSERT(m_WorkQueue.IsWorkerThread());
}