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"
|
2018-10-30 16:00:41 +01:00
|
|
|
#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>
|
2019-06-28 11:56:15 +02:00
|
|
|
#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
|
2017-10-11 14:19:42 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
REGISTER_TYPE(IcingaDB);
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
IcingaDB::IcingaDB()
|
2019-11-02 18:01:31 +01:00
|
|
|
: m_Rcon(nullptr)
|
2017-10-12 11:47:49 +02:00
|
|
|
{
|
2018-10-18 14:39:59 +02:00
|
|
|
m_Rcon = nullptr;
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
m_WorkQueue.SetName("IcingaDB");
|
2018-07-10 11:09:37 +02:00
|
|
|
|
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.
|
|
|
|
*/
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::Start(bool runtimeCreated)
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
2019-10-29 17:32:29 +01:00
|
|
|
ObjectImpl<IcingaDB>::Start(runtimeCreated);
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-10-29 17:32:29 +01: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;
|
2018-10-18 14:39:59 +02:00
|
|
|
m_ConfigDumpDone = false;
|
|
|
|
|
|
|
|
m_Rcon = new RedisConnection(GetHost(), GetPort(), GetPath(), GetPassword(), GetDbIndex());
|
|
|
|
m_Rcon->Start();
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-06-28 11:56:15 +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);
|
2019-06-28 11:56:15 +02:00
|
|
|
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_SubscriptionTimer = new Timer();
|
|
|
|
m_SubscriptionTimer->SetInterval(15);
|
2019-06-28 11:56:15 +02:00
|
|
|
m_SubscriptionTimer->OnTimerExpired.connect([this](const Timer * const&) { UpdateSubscriptionsTimerHandler(); });
|
2017-09-25 14:41:43 +02:00
|
|
|
m_SubscriptionTimer->Start();
|
|
|
|
|
|
|
|
m_StatsTimer = new Timer();
|
2019-05-13 11:37:54 +02:00
|
|
|
m_StatsTimer->SetInterval(1);
|
2019-06-28 11:56:15 +02:00
|
|
|
m_StatsTimer->OnTimerExpired.connect([this](const Timer * const&) { PublishStatsTimerHandler(); });
|
2017-09-25 14:41:43 +02:00
|
|
|
m_StatsTimer->Start();
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
m_WorkQueue.SetName("IcingaDB");
|
2017-10-02 09:59:11 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
boost::thread thread(&IcingaDB::HandleEvents, this);
|
2017-09-25 14:41:43 +02:00
|
|
|
thread.detach();
|
2018-10-18 14:39:59 +02:00
|
|
|
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::ExceptionHandler(boost::exception_ptr exp)
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
2019-10-29 17:32:29 +01:00
|
|
|
Log(LogCritical, "IcingaDB", "Exception during redis query. Verify that Redis is operational.");
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-10-29 17:32:29 +01: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
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::ReconnectTimerHandler()
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
2019-06-28 11:56:15 +02:00
|
|
|
m_WorkQueue.Enqueue([this]() { TryToReconnect(); });
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::TryToReconnect()
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
|
|
|
AssertOnWorkQueue();
|
|
|
|
|
2018-10-25 17:48:32 +02:00
|
|
|
if (m_ConfigDumpDone)
|
2017-09-25 14:41:43 +02:00
|
|
|
return;
|
2018-10-25 17:48:32 +02:00
|
|
|
else
|
2018-10-18 14:39:59 +02:00
|
|
|
m_Rcon->Start();
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2018-11-26 13:34:05 +01:00
|
|
|
if (!m_Rcon || !m_Rcon->IsConnected())
|
2018-10-25 17:48:32 +02:00
|
|
|
return;
|
2018-10-26 14:07:07 +02:00
|
|
|
|
2018-10-18 14:39:59 +02:00
|
|
|
UpdateSubscriptions();
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2018-10-18 14:39:59 +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();
|
|
|
|
|
2018-10-18 14:39:59 +02:00
|
|
|
m_ConfigDumpDone = true;
|
|
|
|
|
2017-09-25 14:41:43 +02:00
|
|
|
m_ConfigDumpInProgress = false;
|
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::UpdateSubscriptionsTimerHandler()
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
2019-06-28 11:56:15 +02:00
|
|
|
m_WorkQueue.Enqueue([this]() { UpdateSubscriptions(); });
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::UpdateSubscriptions()
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
|
|
|
AssertOnWorkQueue();
|
|
|
|
|
2019-11-02 15:47:51 +01:00
|
|
|
Log(LogNotice, "IcingaDB", "Updating Redis subscriptions");
|
2017-10-12 11:47:49 +02:00
|
|
|
|
2018-10-26 15:01:05 +02:00
|
|
|
/* TODO:
|
|
|
|
* Silently return in this case. Usually the RedisConnection checks for connectivity and logs in failure case.
|
|
|
|
* But since we expect and answer here and break Icinga in case of receiving no answer/an unexpected one we opt for
|
|
|
|
* better safe than sorry here. Future implementation needs to include an improved error handling and answer verification.
|
|
|
|
*/
|
2018-11-26 13:34:05 +01:00
|
|
|
if (!m_Rcon || !m_Rcon->IsConnected())
|
2018-10-25 17:48:32 +02:00
|
|
|
return;
|
2018-10-26 15:01:05 +02:00
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
String cursor = "0";
|
2017-10-12 11:47:49 +02:00
|
|
|
String keyPrefix = "icinga:subscription:";
|
|
|
|
|
2017-09-25 14:41:43 +02:00
|
|
|
do {
|
2019-08-05 13:30:09 +02:00
|
|
|
Array::Ptr reply = m_Rcon->GetResultOfQuery({ "SCAN", cursor, "MATCH", keyPrefix + "*", "COUNT", "1000" });
|
|
|
|
VERIFY(reply->GetLength() % 2u == 0u);
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
cursor = reply->Get(0);
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
Array::Ptr keys = reply->Get(1);
|
|
|
|
ObjectLock oLock (keys);
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
for (String key : keys) {
|
|
|
|
if (boost::algorithm::ends_with(key, ":limit"))
|
2017-10-12 17:46:06 +02:00
|
|
|
continue;
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2017-10-12 17:46:06 +02:00
|
|
|
RedisSubscriptionInfo rsi;
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
if (!IcingaDB::GetSubscriptionTypes(key, rsi)) {
|
|
|
|
Log(LogInformation, "IcingaDB")
|
2018-06-28 17:38:32 +02:00
|
|
|
<< "Subscription \"" << key << "\" has no types listed.";
|
|
|
|
} else {
|
2017-10-12 11:47:49 +02:00
|
|
|
m_Subscriptions[key.SubStr(keyPrefix.GetLength())] = rsi;
|
2018-06-28 17:38:32 +02:00
|
|
|
}
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
2019-08-05 13:30:09 +02:00
|
|
|
} while (cursor != "0");
|
2017-10-12 11:47:49 +02:00
|
|
|
|
2019-11-02 15:47:51 +01:00
|
|
|
Log(LogNotice, "IcingaDB")
|
2018-05-15 14:43:01 +02:00
|
|
|
<< "Current Redis event subscriptions: " << m_Subscriptions.size();
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
bool IcingaDB::GetSubscriptionTypes(String key, RedisSubscriptionInfo& rsi)
|
2017-10-12 17:46:06 +02:00
|
|
|
{
|
|
|
|
try {
|
2019-08-05 13:30:09 +02:00
|
|
|
Array::Ptr redisReply = m_Rcon->GetResultOfQuery({ "SMEMBERS", key });
|
2017-10-12 17:46:06 +02:00
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
if (redisReply->GetLength() == 0)
|
2018-06-28 17:38:32 +02:00
|
|
|
return false;
|
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
{
|
|
|
|
ObjectLock oLock (redisReply);
|
|
|
|
|
|
|
|
for (String member : redisReply) {
|
|
|
|
rsi.EventTypes.insert(member);
|
|
|
|
}
|
2017-10-12 17:46:06 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
Log(LogInformation, "IcingaDB")
|
2017-10-12 17:46:06 +02:00
|
|
|
<< "Subscriber Info - Key: " << key << " Value: " << Value(Array::FromSet(rsi.EventTypes));
|
|
|
|
|
|
|
|
} catch (const std::exception& ex) {
|
2019-10-29 17:32:29 +01:00
|
|
|
Log(LogWarning, "IcingaDB")
|
2017-10-12 17:46:06 +02:00
|
|
|
<< "Invalid Redis subscriber info for subscriber '" << key << "': " << DiagnosticInformation(ex);
|
2018-06-28 17:38:32 +02:00
|
|
|
|
|
|
|
return false;
|
2017-10-12 17:46:06 +02:00
|
|
|
}
|
2018-06-28 17:38:32 +02:00
|
|
|
|
|
|
|
return true;
|
2017-10-12 17:46:06 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::PublishStatsTimerHandler(void)
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
2019-06-28 11:56:15 +02:00
|
|
|
m_WorkQueue.Enqueue([this]() { PublishStats(); });
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::PublishStats()
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
|
|
|
AssertOnWorkQueue();
|
|
|
|
|
2018-11-26 13:34:05 +01:00
|
|
|
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);
|
2017-09-25 14:41:43 +02:00
|
|
|
String jsonStats = JsonEncode(status);
|
|
|
|
|
2019-10-31 14:52:46 +01:00
|
|
|
m_Rcon->FireAndForgetQuery({ "PUBLISH", "icinga:stats", jsonStats }, true);
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01: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("StateChange");
|
|
|
|
types.insert("Notification");
|
|
|
|
types.insert("AcknowledgementSet");
|
|
|
|
types.insert("AcknowledgementCleared");
|
|
|
|
types.insert("CommentAdded");
|
|
|
|
types.insert("CommentRemoved");
|
|
|
|
types.insert("DowntimeAdded");
|
|
|
|
types.insert("DowntimeRemoved");
|
|
|
|
types.insert("DowntimeStarted");
|
|
|
|
types.insert("DowntimeTriggered");
|
|
|
|
|
|
|
|
queue->SetTypes(types);
|
|
|
|
|
|
|
|
queue->AddClient(this);
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
Dictionary::Ptr event = queue->WaitForEvent(this);
|
|
|
|
|
|
|
|
if (!event)
|
|
|
|
continue;
|
|
|
|
|
2019-06-28 11:56:15 +02:00
|
|
|
m_WorkQueue.Enqueue([this, event]() { SendEvent(event); });
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
queue->RemoveClient(this);
|
|
|
|
EventQueue::UnregisterIfUnused(queueName, queue);
|
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::HandleEvent(const Dictionary::Ptr& event)
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
|
|
|
AssertOnWorkQueue();
|
|
|
|
|
|
|
|
for (const std::pair<String, RedisSubscriptionInfo>& kv : m_Subscriptions) {
|
|
|
|
const auto& name = kv.first;
|
|
|
|
const auto& rsi = kv.second;
|
|
|
|
|
|
|
|
if (rsi.EventTypes.find(event->Get("type")) == rsi.EventTypes.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
String body = JsonEncode(event);
|
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
double maxExists = m_Rcon->GetResultOfQuery({ "EXISTS", "icinga:subscription:" + name + ":limit" });
|
2018-10-18 14:39:59 +02:00
|
|
|
|
2017-10-12 17:46:06 +02:00
|
|
|
long maxEvents = MAX_EVENTS_DEFAULT;
|
2019-08-05 13:30:09 +02:00
|
|
|
if (maxExists != 0) {
|
|
|
|
String redisReply = m_Rcon->GetResultOfQuery({ "GET", "icinga:subscription:" + name + ":limit"});
|
2018-05-15 14:43:01 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
Log(LogInformation, "IcingaDB")
|
2019-08-05 13:30:09 +02:00
|
|
|
<< "Got limit " << redisReply << " for " << name;
|
2018-05-15 14:43:01 +02:00
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
maxEvents = Convert::ToLong(redisReply);
|
2017-10-12 17:46:06 +02:00
|
|
|
}
|
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
m_Rcon->FireAndForgetQueries({
|
2018-10-18 14:39:59 +02:00
|
|
|
{ "MULTI" },
|
|
|
|
{ "LPUSH", "icinga:event:" + name, body },
|
|
|
|
{ "LTRIM", "icinga:event:" + name, "0", String(maxEvents - 1)},
|
|
|
|
{ "EXEC" }});
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
}
|
2017-10-16 12:03:49 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::SendEvent(const Dictionary::Ptr& event)
|
2017-10-16 12:03:49 +02:00
|
|
|
{
|
|
|
|
AssertOnWorkQueue();
|
|
|
|
|
2018-11-26 13:34:05 +01:00
|
|
|
if (!m_Rcon || !m_Rcon->IsConnected())
|
2018-10-26 14:07:07 +02:00
|
|
|
return;
|
|
|
|
|
2018-10-30 16:00:41 +01:00
|
|
|
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
|
2019-06-28 11:56:15 +02:00
|
|
|
m_WorkQueue.Enqueue([this, checkable]() { UpdateState(checkable); });
|
2018-11-15 17:04:03 +01:00
|
|
|
}
|
|
|
|
|
2018-10-30 16:00:41 +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
|
|
|
|
2018-10-30 16:00:41 +01:00
|
|
|
for (const Comment::Ptr& c : checkable->GetComments()) {
|
|
|
|
if (c->GetEntryType() == CommentAcknowledgement) {
|
|
|
|
if (c->GetEntryTime() > entry) {
|
|
|
|
entry = c->GetEntryTime();
|
|
|
|
AckComment = c;
|
2018-11-26 15:03:11 +01:00
|
|
|
StateChangeHandler(checkable);
|
2018-10-30 16:00:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-11-02 18:01:31 +01:00
|
|
|
|
2018-10-30 16:00:41 +01:00
|
|
|
event->Set("comment_id", GetObjectIdentifier(AckComment));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-16 12:03:49 +02:00
|
|
|
String body = JsonEncode(event);
|
|
|
|
|
2019-08-05 13:30:09 +02:00
|
|
|
m_Rcon->FireAndForgetQueries({
|
2018-10-18 14:39:59 +02:00
|
|
|
{ "PUBLISH", "icinga:event:all", body },
|
|
|
|
{ "PUBLISH", "icinga:event:" + event->Get("type"), body }});
|
2017-10-16 12:03:49 +02:00
|
|
|
}
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::Stop(bool runtimeRemoved)
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
2019-10-29 17:32:29 +01:00
|
|
|
Log(LogInformation, "IcingaDB")
|
2018-05-15 14:43:01 +02:00
|
|
|
<< "'" << GetName() << "' stopped.";
|
2017-09-25 14:41:43 +02:00
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
ObjectImpl<IcingaDB>::Stop(runtimeRemoved);
|
2017-09-25 14:41:43 +02:00
|
|
|
}
|
|
|
|
|
2019-10-29 17:32:29 +01:00
|
|
|
void IcingaDB::AssertOnWorkQueue()
|
2017-09-25 14:41:43 +02:00
|
|
|
{
|
|
|
|
ASSERT(m_WorkQueue.IsWorkerThread());
|
|
|
|
}
|