Merge pull request #8302 from Icinga/bugfix/windows-systemroot-aliases-6259

Macros: support $env.ENV_VAR_NAME$
This commit is contained in:
Julian Brost 2023-02-20 13:09:15 +01:00 committed by GitHub
commit a84a0a3cee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 162 additions and 98 deletions

View File

@ -664,6 +664,13 @@ The following macros provide global statistics:
icinga.num\_hosts\_in\_downtime | Current number of hosts in downtime.
icinga.num\_hosts\_acknowledged | Current number of acknowledged host problems.
### Environment Variable Runtime Macros <a id="env-runtime-macros"></a>
All environment variables of the Icinga process are available as runtime macros
named `env.<env var name>`. E.g. `$env.ProgramFiles$` for ProgramFiles which is
especially useful on Windows. In contrast to the other runtime macros env vars
require the `env.` prefix.
## Apply Rules <a id="using-apply"></a>

View File

@ -60,7 +60,6 @@ void IdoCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResult
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", commandObj);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
String idoType = MacroProcessor::ResolveMacros("$ido_type$", resolvers, checkable->GetLastCheckResult(),
nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);

View File

@ -41,6 +41,7 @@ set(icinga_SOURCES
customvarobject.cpp customvarobject.hpp customvarobject-ti.hpp
dependency.cpp dependency.hpp dependency-ti.hpp dependency-apply.cpp
downtime.cpp downtime.hpp downtime-ti.hpp
envresolver.cpp envresolver.hpp
eventcommand.cpp eventcommand.hpp eventcommand-ti.hpp
externalcommandprocessor.cpp externalcommandprocessor.hpp
host.cpp host.hpp host-ti.hpp

View File

@ -667,7 +667,6 @@ Dictionary::Ptr ApiActions::ExecuteCommand(const ConfigObject::Ptr& object, cons
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
String resolved_endpoint = MacroProcessor::ResolveMacros(
endpoint, resolvers, checkable->GetLastCheckResult(),

View File

@ -0,0 +1,20 @@
/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
#include "base/string.hpp"
#include "base/value.hpp"
#include "icinga/envresolver.hpp"
#include "icinga/checkresult.hpp"
#include <cstdlib>
using namespace icinga;
bool EnvResolver::ResolveMacro(const String& macro, const CheckResult::Ptr&, Value *result) const
{
auto value (getenv(macro.CStr()));
if (value) {
*result = value;
}
return value;
}

View File

@ -0,0 +1,30 @@
/* Icinga 2 | (c) 2020 Icinga GmbH | GPLv2+ */
#ifndef ENVRESOLVER_H
#define ENVRESOLVER_H
#include "base/object.hpp"
#include "base/string.hpp"
#include "base/value.hpp"
#include "icinga/macroresolver.hpp"
#include "icinga/checkresult.hpp"
namespace icinga
{
/**
* Resolves env var names.
*
* @ingroup icinga
*/
class EnvResolver final : public Object, public MacroResolver
{
public:
DECLARE_PTR_TYPEDEFS(EnvResolver);
bool ResolveMacro(const String& macro, const CheckResult::Ptr&, Value *result) const override;
};
}
#endif /* ENVRESOLVER_H */

View File

@ -3,6 +3,8 @@
#include "icinga/macroprocessor.hpp"
#include "icinga/macroresolver.hpp"
#include "icinga/customvarobject.hpp"
#include "icinga/envresolver.hpp"
#include "icinga/icingaapplication.hpp"
#include "base/array.hpp"
#include "base/objectlock.hpp"
#include "base/logger.hpp"
@ -73,6 +75,16 @@ Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolv
return result;
}
static const EnvResolver::Ptr l_EnvResolver = new EnvResolver();
static MacroProcessor::ResolverList GetDefaultResolvers()
{
return {
{ "icinga", IcingaApplication::GetInstance() },
{ "env", l_EnvResolver, false }
};
}
bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resolvers,
const CheckResult::Ptr& cr, Value *result, bool *recursive_macro)
{
@ -88,77 +100,84 @@ bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resol
tokens.erase(tokens.begin());
}
for (const ResolverSpec& resolver : resolvers) {
if (!objName.IsEmpty() && objName != resolver.first)
continue;
const auto defaultResolvers (GetDefaultResolvers());
if (objName.IsEmpty()) {
CustomVarObject::Ptr dobj = dynamic_pointer_cast<CustomVarObject>(resolver.second);
for (auto resolverList : {&resolvers, &defaultResolvers}) {
for (auto& resolver : *resolverList) {
if (!objName.IsEmpty() && objName != resolver.Name)
continue;
if (dobj) {
Dictionary::Ptr vars = dobj->GetVars();
if (vars && vars->Contains(macro)) {
*result = vars->Get(macro);
*recursive_macro = true;
return true;
}
}
}
auto *mresolver = dynamic_cast<MacroResolver *>(resolver.second.get());
if (mresolver && mresolver->ResolveMacro(boost::algorithm::join(tokens, "."), cr, result))
return true;
Value ref = resolver.second;
bool valid = true;
for (const String& token : tokens) {
if (ref.IsObjectType<Dictionary>()) {
Dictionary::Ptr dict = ref;
if (dict->Contains(token)) {
ref = dict->Get(token);
if (objName.IsEmpty()) {
if (!resolver.ResolveShortMacros)
continue;
} else {
valid = false;
break;
CustomVarObject::Ptr dobj = dynamic_pointer_cast<CustomVarObject>(resolver.Obj);
if (dobj) {
Dictionary::Ptr vars = dobj->GetVars();
if (vars && vars->Contains(macro)) {
*result = vars->Get(macro);
*recursive_macro = true;
return true;
}
}
} else if (ref.IsObject()) {
Object::Ptr object = ref;
Type::Ptr type = object->GetReflectionType();
if (!type) {
valid = false;
break;
}
int field = type->GetFieldId(token);
if (field == -1) {
valid = false;
break;
}
ref = object->GetField(field);
Field fieldInfo = type->GetFieldInfo(field);
if (strcmp(fieldInfo.TypeName, "Timestamp") == 0)
ref = static_cast<long>(ref);
}
}
if (valid) {
if (tokens[0] == "vars" ||
tokens[0] == "action_url" ||
tokens[0] == "notes_url" ||
tokens[0] == "notes")
*recursive_macro = true;
auto *mresolver = dynamic_cast<MacroResolver *>(resolver.Obj.get());
*result = ref;
return true;
if (mresolver && mresolver->ResolveMacro(boost::algorithm::join(tokens, "."), cr, result))
return true;
Value ref = resolver.Obj;
bool valid = true;
for (const String& token : tokens) {
if (ref.IsObjectType<Dictionary>()) {
Dictionary::Ptr dict = ref;
if (dict->Contains(token)) {
ref = dict->Get(token);
continue;
} else {
valid = false;
break;
}
} else if (ref.IsObject()) {
Object::Ptr object = ref;
Type::Ptr type = object->GetReflectionType();
if (!type) {
valid = false;
break;
}
int field = type->GetFieldId(token);
if (field == -1) {
valid = false;
break;
}
ref = object->GetField(field);
Field fieldInfo = type->GetFieldInfo(field);
if (strcmp(fieldInfo.TypeName, "Timestamp") == 0)
ref = static_cast<long>(ref);
}
}
if (valid) {
if (tokens[0] == "vars" ||
tokens[0] == "action_url" ||
tokens[0] == "notes_url" ||
tokens[0] == "notes")
*recursive_macro = true;
*result = ref;
return true;
}
}
}
@ -170,9 +189,12 @@ Value MacroProcessor::EvaluateFunction(const Function::Ptr& func, const Resolver
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros, int recursionLevel)
{
Dictionary::Ptr resolvers_this = new Dictionary();
const auto defaultResolvers (GetDefaultResolvers());
for (const ResolverSpec& resolver : resolvers) {
resolvers_this->Set(resolver.first, resolver.second);
for (auto resolverList : {&resolvers, &defaultResolvers}) {
for (auto& resolver: *resolverList) {
resolvers_this->Set(resolver.Name, resolver.Obj);
}
}
auto internalResolveMacrosShim = [resolvers, cr, resolvedMacros, useResolvedMacros, recursionLevel](const std::vector<Value>& args) {

View File

@ -7,6 +7,7 @@
#include "icinga/checkable.hpp"
#include "base/value.hpp"
#include <vector>
#include <utility>
namespace icinga
{
@ -19,8 +20,21 @@ namespace icinga
class MacroProcessor
{
public:
struct ResolverSpec
{
String Name;
Object::Ptr Obj;
// Whether to resolve not only e.g. $host.address$, but also just $address$
bool ResolveShortMacros;
inline ResolverSpec(String name, Object::Ptr obj, bool resolveShortMacros = true)
: Name(std::move(name)), Obj(std::move(obj)), ResolveShortMacros(resolveShortMacros)
{
}
};
typedef std::function<Value (const Value&)> EscapeCallback;
typedef std::pair<String, Object::Ptr> ResolverSpec;
typedef std::vector<ResolverSpec> ResolverList;
static Value ResolveMacros(const Value& str, const ResolverList& resolvers,

View File

@ -61,7 +61,6 @@ void IcingadbCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckR
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", commandObj);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
auto resolve ([&](const String& macro) {
return MacroProcessor::ResolveMacros(macro, resolvers, checkable->GetLastCheckResult(),

View File

@ -10,7 +10,6 @@
#include "icinga/eventcommand.hpp"
#include "icinga/timeperiod.hpp"
#include "icinga/macroprocessor.hpp"
#include "icinga/icingaapplication.hpp"
#include "icinga/compatutility.hpp"
#include "icinga/pluginutility.hpp"
#include "base/configtype.hpp"
@ -315,7 +314,6 @@ Value HostsTable::NotesExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "host", host },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(host->GetNotes(), resolvers);
@ -340,7 +338,6 @@ Value HostsTable::NotesUrlExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "host", host },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(host->GetNotesUrl(), resolvers);
@ -365,7 +362,6 @@ Value HostsTable::ActionUrlExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "host", host },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(host->GetActionUrl(), resolvers);
@ -422,7 +418,6 @@ Value HostsTable::IconImageExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "host", host },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(host->GetIconImage(), resolvers);

View File

@ -12,7 +12,6 @@
#include "icinga/eventcommand.hpp"
#include "icinga/timeperiod.hpp"
#include "icinga/macroprocessor.hpp"
#include "icinga/icingaapplication.hpp"
#include "icinga/compatutility.hpp"
#include "icinga/pluginutility.hpp"
#include "base/configtype.hpp"
@ -372,7 +371,6 @@ Value ServicesTable::NotesExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "service", service },
{ "host", service->GetHost() },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(service->GetNotes(), resolvers);
@ -398,7 +396,6 @@ Value ServicesTable::NotesUrlExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "service", service },
{ "host", service->GetHost() },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(service->GetNotesUrl(), resolvers);
@ -424,7 +421,6 @@ Value ServicesTable::ActionUrlExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "service", service },
{ "host", service->GetHost() },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(service->GetActionUrl(), resolvers);
@ -450,7 +446,6 @@ Value ServicesTable::IconImageExpandedAccessor(const Value& row)
MacroProcessor::ResolverList resolvers {
{ "service", service },
{ "host", service->GetHost() },
{ "icinga", IcingaApplication::GetInstance() }
};
return MacroProcessor::ResolveMacros(service->GetIconImage(), resolvers);

View File

@ -63,7 +63,6 @@ void ClusterZoneCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const Che
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", command);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
String zoneName = MacroProcessor::ResolveMacros("$cluster_zone$", resolvers, checkable->GetLastCheckResult(),
nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);

View File

@ -4,7 +4,6 @@
# include <stdlib.h>
#endif /* _WIN32 */
#include "methods/dummychecktask.hpp"
#include "icinga/icingaapplication.hpp"
#include "icinga/pluginutility.hpp"
#include "base/utility.hpp"
#include "base/perfdatavalue.hpp"
@ -37,7 +36,6 @@ void DummyCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", command);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
int dummyState = MacroProcessor::ResolveMacros("$dummy_state$", resolvers, checkable->GetLastCheckResult(),
nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);

View File

@ -5,7 +5,6 @@
#include "icinga/service.hpp"
#include "icinga/checkcommand.hpp"
#include "icinga/macroprocessor.hpp"
#include "icinga/icingaapplication.hpp"
#include "icinga/clusterevents.hpp"
#include "icinga/checkable.hpp"
#include "remote/apilistener.hpp"
@ -41,7 +40,6 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", command);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
String missingIcingaMinVersion;

View File

@ -4,7 +4,6 @@
#include "icinga/pluginutility.hpp"
#include "icinga/checkcommand.hpp"
#include "icinga/macroprocessor.hpp"
#include "icinga/icingaapplication.hpp"
#include "base/configtype.hpp"
#include "base/logger.hpp"
#include "base/function.hpp"
@ -37,7 +36,6 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", commandObj);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
int timeout = commandObj->GetTimeout();

View File

@ -4,7 +4,6 @@
#include "icinga/eventcommand.hpp"
#include "icinga/macroprocessor.hpp"
#include "icinga/pluginutility.hpp"
#include "icinga/icingaapplication.hpp"
#include "base/configtype.hpp"
#include "base/logger.hpp"
#include "base/function.hpp"
@ -36,7 +35,6 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable,
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", commandObj);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
int timeout = commandObj->GetTimeout();
std::function<void(const Value& commandLine, const ProcessResult&)> callback;

View File

@ -6,7 +6,6 @@
#include "icinga/pluginutility.hpp"
#include "icinga/service.hpp"
#include "icinga/macroprocessor.hpp"
#include "icinga/icingaapplication.hpp"
#include "base/function.hpp"
#include "base/logger.hpp"
#include "base/utility.hpp"
@ -53,7 +52,6 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification,
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", commandObj);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
int timeout = commandObj->GetTimeout();
std::function<void(const Value& commandLine, const ProcessResult&)> callback;

View File

@ -1,7 +1,6 @@
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
#include "methods/sleepchecktask.hpp"
#include "icinga/icingaapplication.hpp"
#include "icinga/pluginutility.hpp"
#include "base/utility.hpp"
#include "base/convert.hpp"
@ -33,7 +32,6 @@ void SleepCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckResu
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("command", commandObj);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
double sleepTime = MacroProcessor::ResolveMacros("$sleep_time$", resolvers, checkable->GetLastCheckResult(),
nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);

View File

@ -293,7 +293,6 @@ void GraphiteWriter::CheckResultHandlerInternal(const Checkable::Ptr& checkable,
if (service)
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
String prefix;

View File

@ -224,7 +224,6 @@ void InfluxdbCommonWriter::CheckResultHandlerWQ(const Checkable::Ptr& checkable,
if (service)
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
String prefix;

View File

@ -195,8 +195,7 @@ void OpenTsdbWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C
if (service)
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
// Resolve macros for the service and host template config line
if (config_tmpl_tags) {
ObjectLock olock(config_tmpl_tags);

View File

@ -116,7 +116,6 @@ void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C
if (service)
resolvers.emplace_back("service", service);
resolvers.emplace_back("host", host);
resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
if (service) {
String line = MacroProcessor::ResolveMacros(GetServiceFormatTemplate(), resolvers, cr, nullptr, &PerfdataWriter::EscapeMacroMetric);