description = "Make socket timeouts return an UNKNOWN state instead of CRITICAL"
}
"-t" = "$nrpe_timeout$"
+ "-a" = {
+ value = "$nrpe_arguments$"
+ repeat_key = false
+ order = 1
+ }
}
vars.nrpe_address = "$address$"
return "HARD";
}
-bool Host::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const
+bool Host::ResolveMacro(const String& macro, const CheckResult::Ptr&, Value *result) const
{
if (macro == "state") {
*result = StateToString(GetState());
static StateType StateTypeFromString(const String& state);
static String StateTypeToString(StateType state);
- virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const;
+ virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const;
protected:
virtual void Stop(void);
%attribute %dictionary "arguments" {
%attribute %string "*",
%attribute %dictionary "*" {
+ %attribute %string "key"
%attribute %string "value"
%attribute %string "description"
%attribute %number "required"
%attribute %number "skip_key"
+ %attribute %number "repeat_key"
%attribute %string "set_if"
%attribute %number "order"
}
return ScriptVariable::Get("NodeName");
}
-bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const
+bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr&, Value *result) const
{
double now = Utility::GetTime();
Dictionary::Ptr GetVars(void) const;
String GetNodeName(void) const;
- virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const;
+ virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const;
bool GetEnableNotifications(void) const;
void SetEnableNotifications(bool enabled);
}
bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resolvers,
- const CheckResult::Ptr& cr, String *result, bool *recursive_macro)
+ const CheckResult::Ptr& cr, Value *result, bool *recursive_macro)
{
CONTEXT("Resolving macro '" + macro + "'");
Dictionary::Ptr vars = dobj->GetVars();
if (vars && vars->Contains(macro)) {
- Value value = vars->Get(macro);
-
- if (value.IsObjectType<Array>())
- value = Utility::Join(value, ';');
-
- *result = value;
+ *result = vars->Get(macro);
*recursive_macro = true;
return true;
}
tokens[0] == "notes")
*recursive_macro = true;
- if (ref.IsObjectType<Array>())
- ref = Utility::Join(ref, ';');
-
*result = ref;
return true;
}
return false;
}
-String MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers,
+Value MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers,
const CheckResult::Ptr& cr, String *missingMacro,
const MacroProcessor::EscapeCallback& escapeFn, const Dictionary::Ptr& resolvedMacros,
bool useResolvedMacros, int recursionLevel)
String name = result.SubStr(pos_first + 1, pos_second - pos_first - 1);
- String resolved_macro;
+ Value resolved_macro;
bool recursive_macro;
bool found;
}
/* recursively resolve macros in the macro if it was a user macro */
- if (recursive_macro)
- resolved_macro = InternalResolveMacros(resolved_macro,
- resolvers, cr, missingMacro, EscapeCallback(), Dictionary::Ptr(),
- false, recursionLevel + 1);
+ if (recursive_macro) {
+ if (resolved_macro.IsObjectType<Array>()) {
+ Array::Ptr arr = resolved_macro;
+ Array::Ptr result = new Array();
+
+ ObjectLock olock(arr);
+ BOOST_FOREACH(Value& value, arr) {
+ result->Add(InternalResolveMacros(value,
+ resolvers, cr, missingMacro, EscapeCallback(), Dictionary::Ptr(),
+ false, recursionLevel + 1));
+ }
+
+ resolved_macro = result;
+ } else
+ resolved_macro = InternalResolveMacros(resolved_macro,
+ resolvers, cr, missingMacro, EscapeCallback(), Dictionary::Ptr(),
+ false, recursionLevel + 1);
+ }
if (!useResolvedMacros && found && resolvedMacros)
resolvedMacros->Set(name, resolved_macro);
if (escapeFn)
resolved_macro = escapeFn(resolved_macro);
+ /* we're done if the value is an array */
+ if (resolved_macro.IsObjectType<Array>()) {
+ /* don't allow mixing strings and arrays in macro strings */
+ if (pos_first != 0 || pos_second != str.GetLength() - 1)
+ BOOST_THROW_EXCEPTION(std::invalid_argument("Mixing both strings and non-strings in macros is not allowed."));
+
+ return resolved_macro;
+ }
+
+ String resolved_macro_str = resolved_macro;
+
result.Replace(pos_first, pos_second - pos_first + 1, resolved_macro);
- offset = pos_first + resolved_macro.GetLength() + 1;
+ offset = pos_first + resolved_macro_str.GetLength() + 1;
}
return result;
class I2_ICINGA_API MacroProcessor
{
public:
- typedef boost::function<String (const String&)> EscapeCallback;
+ typedef boost::function<Value (const Value&)> EscapeCallback;
typedef std::pair<String, Object::Ptr> ResolverSpec;
typedef std::vector<ResolverSpec> ResolverList;
MacroProcessor(void);
static bool ResolveMacro(const String& macro, const ResolverList& resolvers,
- const CheckResult::Ptr& cr, String *result, bool *recursive_macro);
- static String InternalResolveMacros(const String& str,
+ const CheckResult::Ptr& cr, Value *result, bool *recursive_macro);
+ static Value InternalResolveMacros(const String& str,
const ResolverList& resolvers, const CheckResult::Ptr& cr,
String *missingMacro, const EscapeCallback& escapeFn,
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros,
public:
DECLARE_PTR_TYPEDEFS(MacroResolver);
- virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const = 0;
+ virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const = 0;
};
}
{
int Order;
bool SkipKey;
+ bool RepeatKey;
bool SkipValue;
String Key;
- String Value;
+ Value AValue;
CommandArgument(void)
- : Order(0), SkipKey(false), SkipValue(false)
+ : Order(0), SkipKey(false), RepeatKey(true), SkipValue(false)
{ }
bool operator<(const CommandArgument& rhs) const
}
};
+void PluginUtility::AddArgumentHelper(const Array::Ptr& args, const String& key, const String& value, bool add_key, bool add_value)
+{
+ if (add_key)
+ args->Add(key);
+
+ if (add_value)
+ args->Add(value);
+}
+
+Value PluginUtility::EscapeMacroShellArg(const Value& value)
+{
+ String result;
+
+ if (value.IsObjectType<Array>()) {
+ Array::Ptr arr = value;
+
+ ObjectLock olock(arr);
+ BOOST_FOREACH(const Value& arg, arr) {
+ if (result.GetLength() > 0)
+ result += " ";
+
+ result += Utility::EscapeShellArg(arg);
+ }
+ } else
+ result = Utility::EscapeShellArg(value);
+
+ return result;
+}
+
void PluginUtility::ExecuteCommand(const Command::Ptr& commandObj, const Checkable::Ptr& checkable,
const CheckResult::Ptr& cr, const MacroProcessor::ResolverList& macroResolvers,
const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros,
Value command;
if (!raw_arguments || raw_command.IsObjectType<Array>())
command = MacroProcessor::ResolveMacros(raw_command, macroResolvers, cr, NULL,
- Utility::EscapeShellArg, resolvedMacros, useResolvedMacros);
+ PluginUtility::EscapeMacroShellArg, resolvedMacros, useResolvedMacros);
else {
Array::Ptr arr = new Array();
arr->Add(raw_command);
if (arginfo.IsObjectType<Dictionary>()) {
Dictionary::Ptr argdict = arginfo;
+ if (argdict->Contains("key"))
+ arg.Key = argdict->Get("key");
argval = argdict->Get("value");
if (argdict->Contains("required"))
required = argdict->Get("required");
arg.SkipKey = argdict->Get("skip_key");
+ if (argdict->Contains("repeat_key"))
+ arg.RepeatKey = argdict->Get("repeat_key");
arg.Order = argdict->Get("order");
String set_if = argdict->Get("set_if");
arg.SkipValue = true;
String missingMacro;
- arg.Value = MacroProcessor::ResolveMacros(argval, macroResolvers,
+ arg.AValue = MacroProcessor::ResolveMacros(argval, macroResolvers,
cr, &missingMacro, MacroProcessor::EscapeCallback(), resolvedMacros,
useResolvedMacros);
Array::Ptr command_arr = command;
BOOST_FOREACH(const CommandArgument& arg, args) {
- if (!arg.SkipKey)
- command_arr->Add(arg.Key);
+ Array::Ptr arr;
+
+ if (arg.AValue.IsString())
+ AddArgumentHelper(command_arr, arg.Key, arg.AValue, !arg.SkipKey, !arg.SkipValue);
+ else if (arg.AValue.IsObjectType<Array>())
+ arr = static_cast<Array::Ptr>(arg.AValue);
+ else
+ continue;
+
+ if (arr) {
+ bool first = true;
- if (!arg.SkipValue)
- command_arr->Add(arg.Value);
+ ObjectLock olock(arr);
+ BOOST_FOREACH(const Value& value, arr) {
+ bool add_key;
+
+ if (first) {
+ first = false;
+ add_key = !arg.SkipKey;
+ } else
+ add_key = !arg.SkipKey && arg.RepeatKey;
+
+
+ AddArgumentHelper(command_arr, arg.Key, value, add_key, !arg.SkipValue);
+ }
+ }
}
}
NULL, MacroProcessor::EscapeCallback(), resolvedMacros,
useResolvedMacros);
+ if (value.IsObjectType<Array>())
+ value = Utility::Join(value, ';');
+
envMacros->Set(kv.first, value);
}
}
public:
static void ExecuteCommand(const Command::Ptr& commandObj, const Checkable::Ptr& checkable,
const CheckResult::Ptr& cr, const MacroProcessor::ResolverList& macroResolvers,
- const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros,
+ const Dictionary::Ptr& resolvedMacros, bool useResolvedMacros,
const boost::function<void(const Value& commandLine, const ProcessResult&)>& callback = boost::function<void(const Value& commandLine, const ProcessResult&)>());
static ServiceState ExitStatusToState(int exitStatus);
private:
PluginUtility(void);
+
+ static void AddArgumentHelper(const Array::Ptr& args, const String& key, const String& value, bool add_key, bool add_value);
+ static Value EscapeMacroShellArg(const Value& value);
};
}
return "HARD";
}
-bool Service::ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const
+bool Service::ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const
{
if (macro == "state") {
*result = StateToString(GetState());
Host::Ptr GetHost(void) const;
- virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const;
+ virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, Value *result) const;
static ServiceState StateFromString(const String& state);
static String StateToString(ServiceState state);
String prefix;
if (service) {
- prefix = MacroProcessor::ResolveMacros(GetServiceNameTemplate(), resolvers, cr, NULL, &GraphiteWriter::EscapeMetric);
+ prefix = MacroProcessor::ResolveMacros(GetServiceNameTemplate(), resolvers, cr, NULL, &GraphiteWriter::EscapeMacroMetric);
SendMetric(prefix, "state", service->GetState());
} else {
- prefix = MacroProcessor::ResolveMacros(GetHostNameTemplate(), resolvers, cr, NULL, &GraphiteWriter::EscapeMetric);
+ prefix = MacroProcessor::ResolveMacros(GetHostNameTemplate(), resolvers, cr, NULL, &GraphiteWriter::EscapeMacroMetric);
SendMetric(prefix, "state", host->GetState());
}
return result;
}
+
+Value GraphiteWriter::EscapeMacroMetric(const Value& value)
+{
+ if (value.IsObjectType<Array>()) {
+ Array::Ptr arr = value;
+ Array::Ptr result = new Array();
+
+ ObjectLock olock(arr);
+ BOOST_FOREACH(const Value& arg, arr) {
+ result->Add(EscapeMetric(arg));
+ }
+
+ return Utility::Join(result, '.');
+ } else
+ return EscapeMetric(value);
+}
void SendMetric(const String& prefix, const String& name, double value);
void SendPerfdata(const String& prefix, const CheckResult::Ptr& cr);
static String EscapeMetric(const String& str);
+ static Value EscapeMacroMetric(const Value& value);
void ReconnectTimerHandler(void);
};
RotateFile(m_HostOutputFile, GetHostTempPath(), GetHostPerfdataPath());
}
+Value PerfdataWriter::EscapeMacroMetric(const Value& value)
+{
+ if (value.IsObjectType<Array>())
+ return Utility::Join(value, ';');
+ else
+ return value;
+}
+
void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr)
{
CONTEXT("Writing performance data for object '" + checkable->GetName() + "'");
resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
if (service) {
- String line = MacroProcessor::ResolveMacros(GetServiceFormatTemplate(), resolvers, cr);
+ String line = MacroProcessor::ResolveMacros(GetServiceFormatTemplate(), resolvers, cr, NULL, &PerfdataWriter::EscapeMacroMetric);
{
ObjectLock olock(this);
m_ServiceOutputFile << line << "\n";
}
} else {
- String line = MacroProcessor::ResolveMacros(GetHostFormatTemplate(), resolvers, cr);
+ String line = MacroProcessor::ResolveMacros(GetHostFormatTemplate(), resolvers, cr, NULL, &PerfdataWriter::EscapeMacroMetric);
{
ObjectLock olock(this);
private:
void CheckResultHandler(const Checkable::Ptr& checkable, const CheckResult::Ptr& cr);
+ static Value EscapeMacroMetric(const Value& value);
Timer::Ptr m_RotationTimer;
void RotationTimerHandler(void);
base-json.cpp base-match.cpp base-netstring.cpp base-object.cpp
base-serialize.cpp base-shellescape.cpp base-stacktrace.cpp
base-stream.cpp base-string.cpp base-timer.cpp base-type.cpp
- base-value.cpp config-ops.cpp icinga-perfdata.cpp test.cpp
+ base-value.cpp config-ops.cpp icinga-macros.cpp icinga-perfdata.cpp
+ test.cpp
)
set_property(SOURCE test.cpp PROPERTY EXCLUDE_UNITY_BUILD TRUE)
base_value/format
config_ops/simple
config_ops/advanced
+ icinga_macros/simple
icinga_perfdata/empty
icinga_perfdata/simple
icinga_perfdata/quotes
--- /dev/null
+/******************************************************************************
+ * Icinga 2 *
+ * Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software Foundation *
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ******************************************************************************/
+
+#include "icinga/macroprocessor.hpp"
+#include <boost/test/unit_test.hpp>
+
+using namespace icinga;
+
+BOOST_AUTO_TEST_SUITE(icinga_macros)
+
+BOOST_AUTO_TEST_CASE(simple)
+{
+ Dictionary::Ptr macrosA = new Dictionary();
+ macrosA->Set("testA", 7);
+ macrosA->Set("testB", "hello");
+
+ Dictionary::Ptr macrosB = new Dictionary();
+ macrosB->Set("testA", 3);
+ macrosB->Set("testC", "world");
+
+ Array::Ptr testD = new Array();
+ testD->Add(3);
+ testD->Add("test");
+
+ macrosB->Set("testD", testD);
+
+ MacroProcessor::ResolverList resolvers;
+ resolvers.push_back(std::make_pair("macrosA", macrosA));
+ resolvers.push_back(std::make_pair("macrosB", macrosB));
+
+ BOOST_CHECK(MacroProcessor::ResolveMacros("$macrosA.testB$ $macrosB.testC$", resolvers) == "hello world");
+ BOOST_CHECK(MacroProcessor::ResolveMacros("$testA$", resolvers) == "7");
+
+ Array::Ptr result = MacroProcessor::ResolveMacros("$testD$", resolvers);
+ BOOST_CHECK(result->GetLength() == 2);
+
+}
+
+BOOST_AUTO_TEST_SUITE_END()