icinga2/lib/icinga/icingaapplication.cpp
Alexander A. Klimov a9c80ffb2e MacroProcessor::ResolveMacro(): treat quasi-CV-object IcingaApplication as real CV-object
As MacroProcessor checked just for CustomVarObject base class, but
IcingaApplication provided the vars attribute by itself, it had to also
resolve CV macros by itself. That logic diverged from MacroProcessor so that
macros inside IcingaApplication CVs weren't resolved. Until now.
2023-05-31 16:35:09 +02:00

322 lines
9.3 KiB
C++

/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "icinga/icingaapplication.hpp"
#include "icinga/icingaapplication-ti.cpp"
#include "icinga/cib.hpp"
#include "icinga/macroprocessor.hpp"
#include "config/configcompiler.hpp"
#include "base/atomic-file.hpp"
#include "base/configwriter.hpp"
#include "base/configtype.hpp"
#include "base/exception.hpp"
#include "base/logger.hpp"
#include "base/objectlock.hpp"
#include "base/convert.hpp"
#include "base/debug.hpp"
#include "base/utility.hpp"
#include "base/timer.hpp"
#include "base/scriptglobal.hpp"
#include "base/initialize.hpp"
#include "base/statsfunction.hpp"
#include "base/loader.hpp"
#include <fstream>
using namespace icinga;
static Timer::Ptr l_RetentionTimer;
REGISTER_TYPE(IcingaApplication);
/* Ensure that the priority is lower than the basic System namespace initialization in scriptframe.cpp. */
INITIALIZE_ONCE_WITH_PRIORITY(&IcingaApplication::StaticInitialize, InitializePriority::InitIcingaApplication);
static Namespace::Ptr l_IcingaNS;
void IcingaApplication::StaticInitialize()
{
/* Pre-fill global constants, can be overridden with user input later in icinga-app/icinga.cpp. */
String node_name = Utility::GetFQDN();
if (node_name.IsEmpty()) {
Log(LogNotice, "IcingaApplication", "No FQDN available. Trying Hostname.");
node_name = Utility::GetHostName();
if (node_name.IsEmpty()) {
Log(LogWarning, "IcingaApplication", "No FQDN nor Hostname available. Setting Nodename to 'localhost'.");
node_name = "localhost";
}
}
ScriptGlobal::Set("NodeName", node_name);
ScriptGlobal::Set("ReloadTimeout", 300);
ScriptGlobal::Set("MaxConcurrentChecks", 512);
Namespace::Ptr systemNS = ScriptGlobal::Get("System");
/* Ensure that the System namespace is already initialized. Otherwise this is a programming error. */
VERIFY(systemNS);
systemNS->Set("ApplicationType", "IcingaApplication", true);
systemNS->Set("ApplicationVersion", Application::GetAppVersion(), true);
Namespace::Ptr globalNS = ScriptGlobal::GetGlobals();
VERIFY(globalNS);
l_IcingaNS = new Namespace(true);
globalNS->Set("Icinga", l_IcingaNS, true);
}
INITIALIZE_ONCE_WITH_PRIORITY([]() {
l_IcingaNS->Freeze();
}, InitializePriority::FreezeNamespaces);
REGISTER_STATSFUNCTION(IcingaApplication, &IcingaApplication::StatsFunc);
void IcingaApplication::StatsFunc(const Dictionary::Ptr& status, const Array::Ptr& perfdata)
{
DictionaryData nodes;
for (const IcingaApplication::Ptr& icingaapplication : ConfigType::GetObjectsByType<IcingaApplication>()) {
nodes.emplace_back(icingaapplication->GetName(), new Dictionary({
{ "node_name", icingaapplication->GetNodeName() },
{ "enable_notifications", icingaapplication->GetEnableNotifications() },
{ "enable_event_handlers", icingaapplication->GetEnableEventHandlers() },
{ "enable_flapping", icingaapplication->GetEnableFlapping() },
{ "enable_host_checks", icingaapplication->GetEnableHostChecks() },
{ "enable_service_checks", icingaapplication->GetEnableServiceChecks() },
{ "enable_perfdata", icingaapplication->GetEnablePerfdata() },
{ "environment", icingaapplication->GetEnvironment() },
{ "pid", Utility::GetPid() },
{ "program_start", Application::GetStartTime() },
{ "version", Application::GetAppVersion() }
}));
}
status->Set("icingaapplication", new Dictionary(std::move(nodes)));
}
/**
* The entry point for the Icinga application.
*
* @returns An exit status.
*/
int IcingaApplication::Main()
{
Log(LogDebug, "IcingaApplication", "In IcingaApplication::Main()");
/* periodically dump the program state */
l_RetentionTimer = Timer::Create();
l_RetentionTimer->SetInterval(300);
l_RetentionTimer->OnTimerExpired.connect([this](const Timer * const&) { DumpProgramState(); });
l_RetentionTimer->Start();
RunEventLoop();
Log(LogInformation, "IcingaApplication", "Icinga has shut down.");
return EXIT_SUCCESS;
}
void IcingaApplication::OnShutdown()
{
{
ObjectLock olock(this);
l_RetentionTimer->Stop();
}
DumpProgramState();
}
static void PersistModAttrHelper(AtomicFile& fp, ConfigObject::Ptr& previousObject, const ConfigObject::Ptr& object, const String& attr, const Value& value)
{
if (object != previousObject) {
if (previousObject) {
ConfigWriter::EmitRaw(fp, "\tobj.version = ");
ConfigWriter::EmitValue(fp, 0, previousObject->GetVersion());
ConfigWriter::EmitRaw(fp, "\n}\n\n");
}
ConfigWriter::EmitRaw(fp, "var obj = ");
Array::Ptr args1 = new Array({
object->GetReflectionType()->GetName(),
object->GetName()
});
ConfigWriter::EmitFunctionCall(fp, "get_object", args1);
ConfigWriter::EmitRaw(fp, "\nif (obj) {\n");
}
ConfigWriter::EmitRaw(fp, "\tobj.");
Array::Ptr args2 = new Array({
attr,
value
});
ConfigWriter::EmitFunctionCall(fp, "modify_attribute", args2);
ConfigWriter::EmitRaw(fp, "\n");
previousObject = object;
}
void IcingaApplication::DumpProgramState()
{
ConfigObject::DumpObjects(Configuration::StatePath);
DumpModifiedAttributes();
}
void IcingaApplication::DumpModifiedAttributes()
{
String path = Configuration::ModAttrPath;
try {
Utility::Glob(path + ".tmp.*", &Utility::Remove, GlobFile);
} catch (const std::exception& ex) {
Log(LogWarning, "IcingaApplication") << DiagnosticInformation(ex);
}
AtomicFile fp (path, 0644);
ConfigObject::Ptr previousObject;
ConfigObject::DumpModifiedAttributes([&fp, &previousObject](const ConfigObject::Ptr& object, const String& attr, const Value& value) {
PersistModAttrHelper(fp, previousObject, object, attr, value);
});
if (previousObject) {
ConfigWriter::EmitRaw(fp, "\tobj.version = ");
ConfigWriter::EmitValue(fp, 0, previousObject->GetVersion());
ConfigWriter::EmitRaw(fp, "\n}\n");
}
fp.Commit();
}
IcingaApplication::Ptr IcingaApplication::GetInstance()
{
return static_pointer_cast<IcingaApplication>(Application::GetInstance());
}
bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr&, Value *result) const
{
double now = Utility::GetTime();
if (macro == "timet") {
*result = static_cast<long>(now);
return true;
} else if (macro == "long_date_time") {
*result = Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", now);
return true;
} else if (macro == "short_date_time") {
*result = Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", now);
return true;
} else if (macro == "date") {
*result = Utility::FormatDateTime("%Y-%m-%d", now);
return true;
} else if (macro == "time") {
*result = Utility::FormatDateTime("%H:%M:%S %z", now);
return true;
} else if (macro == "uptime") {
*result = Utility::FormatDuration(Application::GetUptime());
return true;
}
if (macro.Contains("num_services")) {
ServiceStatistics ss = CIB::CalculateServiceStats();
if (macro == "num_services_ok") {
*result = ss.services_ok;
return true;
} else if (macro == "num_services_warning") {
*result = ss.services_warning;
return true;
} else if (macro == "num_services_critical") {
*result = ss.services_critical;
return true;
} else if (macro == "num_services_unknown") {
*result = ss.services_unknown;
return true;
} else if (macro == "num_services_pending") {
*result = ss.services_pending;
return true;
} else if (macro == "num_services_unreachable") {
*result = ss.services_unreachable;
return true;
} else if (macro == "num_services_flapping") {
*result = ss.services_flapping;
return true;
} else if (macro == "num_services_in_downtime") {
*result = ss.services_in_downtime;
return true;
} else if (macro == "num_services_acknowledged") {
*result = ss.services_acknowledged;
return true;
} else if (macro == "num_services_handled") {
*result = ss.services_handled;
return true;
} else if (macro == "num_services_problem") {
*result = ss.services_problem;
return true;
}
}
else if (macro.Contains("num_hosts")) {
HostStatistics hs = CIB::CalculateHostStats();
if (macro == "num_hosts_up") {
*result = hs.hosts_up;
return true;
} else if (macro == "num_hosts_down") {
*result = hs.hosts_down;
return true;
} else if (macro == "num_hosts_pending") {
*result = hs.hosts_pending;
return true;
} else if (macro == "num_hosts_unreachable") {
*result = hs.hosts_unreachable;
return true;
} else if (macro == "num_hosts_flapping") {
*result = hs.hosts_flapping;
return true;
} else if (macro == "num_hosts_in_downtime") {
*result = hs.hosts_in_downtime;
return true;
} else if (macro == "num_hosts_acknowledged") {
*result = hs.hosts_acknowledged;
return true;
} else if (macro == "num_hosts_handled") {
*result = hs.hosts_handled;
return true;
} else if (macro == "num_hosts_problem") {
*result = hs.hosts_problem;
return true;
}
}
return false;
}
String IcingaApplication::GetNodeName() const
{
return ScriptGlobal::Get("NodeName");
}
/* Intentionally kept here, since an agent may not have the CheckerComponent loaded. */
int IcingaApplication::GetMaxConcurrentChecks() const
{
return ScriptGlobal::Get("MaxConcurrentChecks");
}
String IcingaApplication::GetEnvironment() const
{
return Application::GetAppEnvironment();
}
void IcingaApplication::SetEnvironment(const String& value, bool suppress_events, const Value& cookie)
{
Application::SetAppEnvironment(value);
}
void IcingaApplication::ValidateVars(const Lazy<Dictionary::Ptr>& lvalue, const ValidationUtils& utils)
{
MacroProcessor::ValidateCustomVars(this, lvalue());
}