diff options
Diffstat (limited to 'lib/icinga/icingaapplication.cpp')
-rw-r--r-- | lib/icinga/icingaapplication.cpp | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp new file mode 100644 index 0000000..94ae0ed --- /dev/null +++ b/lib/icinga/icingaapplication.cpp @@ -0,0 +1,321 @@ +/* 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()); +} |