diff --git a/doc/10-icinga-template-library.md b/doc/10-icinga-template-library.md
index b9db4e2f711..f7cb867dd27 100644
--- a/doc/10-icinga-template-library.md
+++ b/doc/10-icinga-template-library.md
@@ -83,13 +83,18 @@ Custom variables passed as [command parameters](03-monitoring-basics.md#command-
Name | Description
-----------------------|---------------
icinga\_min\_version | **Optional.** Required minimum Icinga 2 version, e.g. `2.8.0`. If not satisfied, the state changes to `Critical`. Release packages only.
+icinga\_perfdata | **Optional.** Only yield the given performance data. E.g. `[ "*_latency", "*_execution_time" ]`
### cluster
Check command for the built-in `cluster` check. This check returns performance
data for the current Icinga instance and connected endpoints.
-The `cluster` check command does not support any vars.
+Custom variables passed as [command parameters](03-monitoring-basics.md#command-passing-parameters):
+
+Name | Description
+-----------------------|---------------
+cluster\_perfdata | **Optional.** Only yield the given performance data. E.g. `[ "*_checks*", "num_*" ]`
### cluster-zone
diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp
index d8e6f20b0c1..a88d7abea58 100644
--- a/lib/base/utility.cpp
+++ b/lib/base/utility.cpp
@@ -6,6 +6,7 @@
#include "base/logger.hpp"
#include "base/exception.hpp"
#include "base/socket.hpp"
+#include "base/perfdatavalue.hpp"
#include "base/utility.hpp"
#include "base/json.hpp"
#include "base/objectlock.hpp"
@@ -1905,3 +1906,48 @@ bool Utility::ComparePasswords(const String& enteredPassword, const String& actu
return result;
}
+
+/**
+ * Removes all PerfdataValues from perfdata whose labels don't match any pattern of the filter Array.
+ */
+void Utility::FilterPerfdata(const Array::Ptr& perfdata, const Value& filter)
+{
+ if (!filter.IsObject()) {
+ return;
+ }
+
+ auto filterArray (dynamic_pointer_cast((Object::Ptr)filter));
+
+ if (!filterArray) {
+ return;
+ }
+
+ auto filterStrings (filterArray->ToSet());
+
+ for (decltype(perfdata->GetLength()) i = 0; i < perfdata->GetLength(); ++i) {
+ auto item (perfdata->Get(i));
+
+ if (!item.IsObject()) {
+ continue;
+ }
+
+ auto itemPerfdataValue (dynamic_pointer_cast((Object::Ptr)item));
+
+ if (!itemPerfdataValue) {
+ continue;
+ }
+
+ auto label (itemPerfdataValue->GetLabel());
+
+ for (auto& pattern : filterStrings) {
+ if (Utility::Match(pattern, label)) {
+ // continue 2;
+ goto NextItem;
+ }
+ }
+
+ perfdata->Remove(i--);
+
+NextItem:;
+ }
+}
diff --git a/lib/base/utility.hpp b/lib/base/utility.hpp
index 6760160899e..9b9dc42127b 100644
--- a/lib/base/utility.hpp
+++ b/lib/base/utility.hpp
@@ -137,6 +137,8 @@ class Utility
static bool ComparePasswords(const String& enteredPassword, const String& actualPassword);
+ static void FilterPerfdata(const Array::Ptr& perfdata, const Value& filter);
+
#ifdef I2_DEBUG
static void SetTime(double);
static void IncrementTime(double);
diff --git a/lib/methods/clusterchecktask.cpp b/lib/methods/clusterchecktask.cpp
index 6ce28cac440..f01bf03a5ba 100644
--- a/lib/methods/clusterchecktask.cpp
+++ b/lib/methods/clusterchecktask.cpp
@@ -6,6 +6,7 @@
#include "icinga/cib.hpp"
#include "icinga/service.hpp"
#include "icinga/icingaapplication.hpp"
+#include "icinga/macroprocessor.hpp"
#include "icinga/checkcommand.hpp"
#include "base/application.hpp"
#include "base/objectlock.hpp"
@@ -25,10 +26,28 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe
REQUIRE_NOT_NULL(checkable);
REQUIRE_NOT_NULL(cr);
+ CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
+
+ Host::Ptr host;
+ Service::Ptr service;
+ tie(host, service) = GetHostService(checkable);
+
+ MacroProcessor::ResolverList resolvers;
+ if (MacroResolver::OverrideMacros)
+ resolvers.emplace_back("override", MacroResolver::OverrideMacros);
+
+ if (service)
+ resolvers.emplace_back("service", service);
+ resolvers.emplace_back("host", host);
+ resolvers.emplace_back("command", command);
+ resolvers.emplace_back("icinga", IcingaApplication::GetInstance());
+
+ auto perfdataFilter (MacroProcessor::ResolveMacros("$cluster_perfdata$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros));
+
if (resolvedMacros && !useResolvedMacros)
return;
- CheckCommand::Ptr command = CheckCommand::ExecuteOverride ? CheckCommand::ExecuteOverride : checkable->GetCheckCommand();
String commandName = command->GetName();
ApiListener::Ptr listener = ApiListener::GetInstance();
@@ -86,7 +105,9 @@ void ClusterCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRe
} else {
/* use feature stats perfdata */
std::pair feature_stats = CIB::GetFeatureStats();
- cr->SetPerformanceData(feature_stats.second);
+ auto& perfdata (feature_stats.second);
+ Utility::FilterPerfdata(perfdata, perfdataFilter);
+ cr->SetPerformanceData(perfdata);
cr->SetCommand(commandName);
cr->SetState(state);
diff --git a/lib/methods/icingachecktask.cpp b/lib/methods/icingachecktask.cpp
index 40795495d6b..4c37dd3d010 100644
--- a/lib/methods/icingachecktask.cpp
+++ b/lib/methods/icingachecktask.cpp
@@ -48,6 +48,9 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes
String icingaMinVersion = MacroProcessor::ResolveMacros("$icinga_min_version$", resolvers, checkable->GetLastCheckResult(),
&missingIcingaMinVersion, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros);
+ auto perfdataFilter (MacroProcessor::ResolveMacros("$icinga_perfdata$", resolvers, checkable->GetLastCheckResult(),
+ nullptr, MacroProcessor::EscapeCallback(), resolvedMacros, useResolvedMacros));
+
if (resolvedMacros && !useResolvedMacros)
return;
@@ -151,6 +154,7 @@ void IcingaCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes
perfdata->Add(new PerfdataValue("sum_bytes_sent_per_second", bytesSentPerSecond));
perfdata->Add(new PerfdataValue("sum_bytes_received_per_second", bytesReceivedPerSecond));
+ Utility::FilterPerfdata(perfdata, perfdataFilter);
cr->SetPerformanceData(perfdata);
ServiceState state = ServiceOK;