Implement attributes for some well-known macros and allow macro recursion.

Refs #5856
Fixes #5959
This commit is contained in:
Gunnar Beutner 2014-04-08 13:23:24 +02:00
parent 607d4451d6
commit 0fdb9ea21b
22 changed files with 340 additions and 441 deletions

View File

@ -295,10 +295,9 @@ Value HostsTable::NotesExpandedAccessor(const Value& row)
if (!host) if (!host)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(host); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
resolvers.push_back(IcingaApplication::GetInstance());
Value value = CompatUtility::GetCustomAttributeConfig(host, "notes"); Value value = CompatUtility::GetCustomAttributeConfig(host, "notes");
@ -322,10 +321,9 @@ Value HostsTable::NotesUrlExpandedAccessor(const Value& row)
if (!host) if (!host)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(host); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
resolvers.push_back(IcingaApplication::GetInstance());
Value value = CompatUtility::GetCustomAttributeConfig(host, "notes_url"); Value value = CompatUtility::GetCustomAttributeConfig(host, "notes_url");
@ -349,10 +347,9 @@ Value HostsTable::ActionUrlExpandedAccessor(const Value& row)
if (!host) if (!host)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(host); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
resolvers.push_back(IcingaApplication::GetInstance());
Value value = CompatUtility::GetCustomAttributeConfig(host, "action_url"); Value value = CompatUtility::GetCustomAttributeConfig(host, "action_url");
@ -399,10 +396,9 @@ Value HostsTable::IconImageExpandedAccessor(const Value& row)
if (!host) if (!host)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(host); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
resolvers.push_back(IcingaApplication::GetInstance());
Value value = CompatUtility::GetCustomAttributeConfig(host, "icon_image"); Value value = CompatUtility::GetCustomAttributeConfig(host, "icon_image");

View File

@ -310,10 +310,10 @@ Value ServicesTable::NotesExpandedAccessor(const Value& row)
if (!service) if (!service)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(service->GetHost()); resolvers.push_back(std::make_pair("host", service->GetHost()));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
Value value = CompatUtility::GetCustomAttributeConfig(service, "notes"); Value value = CompatUtility::GetCustomAttributeConfig(service, "notes");
@ -337,10 +337,10 @@ Value ServicesTable::NotesUrlExpandedAccessor(const Value& row)
if (!service) if (!service)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(service->GetHost()); resolvers.push_back(std::make_pair("host", service->GetHost()));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
Value value = CompatUtility::GetCustomAttributeConfig(service, "notes_url"); Value value = CompatUtility::GetCustomAttributeConfig(service, "notes_url");
@ -364,10 +364,10 @@ Value ServicesTable::ActionUrlExpandedAccessor(const Value& row)
if (!service) if (!service)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(service->GetHost()); resolvers.push_back(std::make_pair("host", service->GetHost()));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
Value value = CompatUtility::GetCustomAttributeConfig(service, "action_url"); Value value = CompatUtility::GetCustomAttributeConfig(service, "action_url");
@ -391,10 +391,10 @@ Value ServicesTable::IconImageExpandedAccessor(const Value& row)
if (!service) if (!service)
return Empty; return Empty;
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(service->GetHost()); resolvers.push_back(std::make_pair("host", service->GetHost()));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
Value value = CompatUtility::GetCustomAttributeConfig(service, "icon_image"); Value value = CompatUtility::GetCustomAttributeConfig(service, "icon_image");

View File

@ -79,11 +79,11 @@ void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C
else else
host = static_pointer_cast<Host>(checkable); host = static_pointer_cast<Host>(checkable);
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
if (service) if (service)
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(host); resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
if (service) { if (service) {
String line = MacroProcessor::ResolveMacros(GetServiceFormatTemplate(), resolvers, cr); String line = MacroProcessor::ResolveMacros(GetServiceFormatTemplate(), resolvers, cr);

View File

@ -24,9 +24,9 @@ class PerfdataWriter : DynamicObject
"TIMET::$icinga.timet$\t" "TIMET::$icinga.timet$\t"
"HOSTNAME::$host.name$\t" "HOSTNAME::$host.name$\t"
"HOSTPERFDATA::$host.perfdata$\t" "HOSTPERFDATA::$host.perfdata$\t"
"HOSTCHECKCOMMAND::$host.checkcommand$\t" "HOSTCHECKCOMMAND::$host.check_command$\t"
"HOSTSTATE::$host.state$\t" "HOSTSTATE::$host.state$\t"
"HOSTSTATETYPE::$host.statetype$"; "HOSTSTATETYPE::$host.state_type$";
}}} }}}
}; };
[config] String service_format_template { [config] String service_format_template {
@ -34,13 +34,13 @@ class PerfdataWriter : DynamicObject
return "DATATYPE::SERVICEPERFDATA\t" return "DATATYPE::SERVICEPERFDATA\t"
"TIMET::$icinga.timet$\t" "TIMET::$icinga.timet$\t"
"HOSTNAME::$host.name$\t" "HOSTNAME::$host.name$\t"
"SERVICEDESC::$service.description$\t" "SERVICEDESC::$service.name$\t"
"SERVICEPERFDATA::$service.perfdata$\t" "SERVICEPERFDATA::$service.perfdata$\t"
"SERVICECHECKCOMMAND::$service.checkcommand$\t" "SERVICECHECKCOMMAND::$service.check_command$\t"
"HOSTSTATE::$host.state$\t" "HOSTSTATE::$host.state$\t"
"HOSTSTATETYPE::$host.statetype$\t" "HOSTSTATETYPE::$host.state_type$\t"
"SERVICESTATE::$service.state$\t" "SERVICESTATE::$service.state$\t"
"SERVICESTATETYPE::$service.statetype$"; "SERVICESTATETYPE::$service.state_type$";
}}} }}}
}; };

View File

@ -119,56 +119,64 @@ external commands).
The following host custom attributes are available in all commands that are executed for The following host custom attributes are available in all commands that are executed for
hosts or services: hosts or services:
Name | Description Name | Description
---------------------------|-------------- -----------------------------|--------------
host.name | The name of the host object. host.name | The name of the host object.
host.displayname | The value of the `display_name` attribute. host.display_name | The value of the `display_name` attribute.
host.state | The host's current state. Can be one of `UNREACHABLE`, `UP` and `DOWN`. host.state | The host's current state. Can be one of `UNREACHABLE`, `UP` and `DOWN`.
host.stateid | The host's current state. Can be one of `0` (up), `1` (down) and `2` (unreachable). host.stateid | The host's current state. Can be one of `0` (up), `1` (down) and `2` (unreachable).
host.statetype | The host's current state type. Can be one of `SOFT` and `HARD`. host.statetype | The host's current state type. Can be one of `SOFT` and `HARD`.
host.attempt | The current check attempt number. host.check_attempt | The current check attempt number.
host.maxattempt | The maximum number of checks which are executed before changing to a hard state. host.max_check_attempts | The maximum number of checks which are executed before changing to a hard state.
host.laststate | The host's previous state. Can be one of `UNREACHABLE`, `UP` and `DOWN`. host.last_state | The host's previous state. Can be one of `UNREACHABLE`, `UP` and `DOWN`.
host.laststateid | The host's previous state. Can be one of `0` (up), `1` (down) and `2` (unreachable). host.last_state_id | The host's previous state. Can be one of `0` (up), `1` (down) and `2` (unreachable).
host.laststatetype | The host's previous state type. Can be one of `SOFT` and `HARD`. host.last_state_type | The host's previous state type. Can be one of `SOFT` and `HARD`.
host.laststatechange | The last state change's timestamp. host.last_state_change | The last state change's timestamp.
host.durationsec | The time since the last state change. host.duration_sec | The time since the last state change.
host.latency | The host's check latency. host.latency | The host's check latency.
host.executiontime | The host's check execution time. host.execution_time | The host's check execution time.
host.output | The last check's output. host.output | The last check's output.
host.perfdata | The last check's performance data. host.perfdata | The last check's performance data.
host.lastcheck | The timestamp when the last check was executed. host.last_check | The timestamp when the last check was executed.
host.totalservices | Number of services associated with the host. host.total_services | Number of services associated with the host.
host.totalservicesok | Number of services associated with the host which are in an `OK` state. host.total_services_ok | Number of services associated with the host which are in an `OK` state.
host.totalserviceswarning | Number of services associated with the host which are in a `WARNING` state. host.total_services_warning | Number of services associated with the host which are in a `WARNING` state.
host.totalservicesunknown | Number of services associated with the host which are in an `UNKNOWN` state. host.total_services_unknown | Number of services associated with the host which are in an `UNKNOWN` state.
host.totalservicescritical | Number of services associated with the host which are in a `CRITICAL` state. host.total_services_critical | Number of services associated with the host which are in a `CRITICAL` state.
### <a id="service-runtime-macros"></a> Service Runtime Macros ### <a id="service-runtime-macros"></a> Service Runtime Macros
The following service macros are available in all commands that are executed for The following service macros are available in all commands that are executed for
services: services:
Name | Description Name | Description
------------------------|-------------- ---------------------------|--------------
service.description | The short name of the service object. service.name | The short name of the service object.
service.displayname | The value of the `display_name` attribute. service.display_name | The value of the `display_name` attribute.
service.checkcommand | This is an alias for the `SERVICEDISPLAYNAME` macro. service.check_command | This is an alias for the `SERVICEDISPLAYNAME` macro.
service.state | The service's current state. Can be one of `OK`, `WARNING`, `CRITICAL` and `UNKNOWN`. service.state | The service's current state. Can be one of `OK`, `WARNING`, `CRITICAL` and `UNKNOWN`.
service.stateid | The service's current state. Can be one of `0` (ok), `1` (warning), `2` (critical) and `3` (unknown). service.state_id | The service's current state. Can be one of `0` (ok), `1` (warning), `2` (critical) and `3` (unknown).
service.statetype | The service's current state type. Can be one of `SOFT` and `HARD`. service.state_type | The service's current state type. Can be one of `SOFT` and `HARD`.
service.attempt | The current check attempt number. service.check_attempt | The current check attempt number.
service.maxattempt | The maximum number of checks which are executed before changing to a hard state. service.max_check_attempts | The maximum number of checks which are executed before changing to a hard state.
service.laststate | The service's previous state. Can be one of `OK`, `WARNING`, `CRITICAL` and `UNKNOWN`. service.last_state | The service's previous state. Can be one of `OK`, `WARNING`, `CRITICAL` and `UNKNOWN`.
service.laststateid | The service's previous state. Can be one of `0` (ok), `1` (warning), `2` (critical) and `3` (unknown). service.last_state_id | The service's previous state. Can be one of `0` (ok), `1` (warning), `2` (critical) and `3` (unknown).
service.laststatetype | The service's previous state type. Can be one of `SOFT` and `HARD`. service.last_state_type | The service's previous state type. Can be one of `SOFT` and `HARD`.
service.laststatechange | The last state change's timestamp. service.last_state_change | The last state change's timestamp.
service.durationsec | The time since the last state change. service.duration_sec | The time since the last state change.
service.latency | The service's check latency. service.latency | The service's check latency.
service.executiontime | The service's check execution time. service.execution_time | The service's check execution time.
service.output | The last check's output. service.output | The last check's output.
service.perfdata | The last check's performance data. service.perfdata | The last check's performance data.
service.lastcheck | The timestamp when the last check was executed. service.last_check | The timestamp when the last check was executed.
### <a id="command-runtime-macros"></a> Command Runtime Macros
The following custom attributes are available in all commands:
Name | Description
-----------------------|--------------
command.name | The name of the command object.
### <a id="user-runtime-macros"></a> User Runtime Macros ### <a id="user-runtime-macros"></a> User Runtime Macros
@ -178,8 +186,7 @@ users:
Name | Description Name | Description
-----------------------|-------------- -----------------------|--------------
user.name | The name of the user object. user.name | The name of the user object.
user.displayname | The value of the display_name attribute. user.display_name | The value of the display_name attribute.
### <a id="notification-runtime-macros"></a> Notification Runtime Macros ### <a id="notification-runtime-macros"></a> Notification Runtime Macros
@ -196,7 +203,7 @@ The following macros are available in all executed commands:
Name | Description Name | Description
-----------------------|-------------- -----------------------|--------------
icinga.timet | Current UNIX timestamp. icinga.timet | Current UNIX timestamp.
icinga.longdatetime | Current date and time including timezone information. Example: `2014-01-03 11:23:08 +0000` icinga.long_date_time | Current date and time including timezone information. Example: `2014-01-03 11:23:08 +0000`
icinga.shortdatetime | Current date and time. Example: `2014-01-03 11:23:08` icinga.short_date_time | Current date and time. Example: `2014-01-03 11:23:08`
icinga.date | Current date. Example: `2014-01-03` icinga.date | Current date. Example: `2014-01-03`
icinga.time | Current time including timezone information. Example: `11:23:08 +0000` icinga.time | Current time including timezone information. Example: `11:23:08 +0000`

View File

@ -6,8 +6,8 @@
object Host "localhost" { object Host "localhost" {
import "linux-server" import "linux-server"
vars.address = "127.0.0.1" address = "127.0.0.1"
vars.address6 = "::1" address6 = "::1"
} }
object Service "icinga" { object Service "icinga" {

View File

@ -56,6 +56,12 @@ abstract class Checkable : DynamicObject
default {{{ return true; }}} default {{{ return true; }}}
}; };
[config] String notes;
[config] String notes_url;
[config] String action_url;
[config] String icon_image;
[config] String icon_image_alt;
[state] double next_check (NextCheckRaw); [state] double next_check (NextCheckRaw);
[state] int check_attempt { [state] int check_attempt {
default {{{ return 1; }}} default {{{ return 1; }}}

View File

@ -221,154 +221,72 @@ String Host::StateTypeToString(StateType type)
bool Host::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const bool Host::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const
{ {
String key; if (macro == "state") {
Dictionary::Ptr vars; *result = StateToString(GetState());
return true;
} else if (macro == "state_id") {
*result = Convert::ToString(GetState());
return true;
} else if (macro == "state_type") {
*result = StateTypeToString(GetStateType());
return true;
} else if (macro == "last_state") {
*result = StateToString(GetLastState());
return true;
} else if (macro == "last_state_id") {
*result = Convert::ToString(GetLastState());
return true;
} else if (macro == "last_state_type") {
*result = StateTypeToString(GetLastStateType());
return true;
} else if (macro == "last_state_change") {
*result = Convert::ToString((long)GetLastStateChange());
return true;
} else if (macro == "duration_sec") {
*result = Convert::ToString((long)(Utility::GetTime() - GetLastStateChange()));
return true;
} else if (macro == "total_services" || macro == "total_services_ok" || macro == "total_services_warning"
|| macro == "total_services_unknown" || macro == "total_services_critical") {
int filter = -1;
int count = 0;
/* special treatment for address macros providing name fallback */ if (macro == "total_services_ok")
if (macro == "address" || macro == "address6") { filter = ServiceOK;
vars = GetVars(); else if (macro == "total_services_warning")
filter = ServiceWarning;
else if (macro == "total_services_unknown")
filter = ServiceUnknown;
else if (macro == "total_services_critical")
filter = ServiceCritical;
String value; BOOST_FOREACH(const Service::Ptr& service, GetServices()) {
if (vars && vars->Contains(macro)) if (filter != -1 && service->GetState() != filter)
value = vars->Get(macro); continue;
if (value.IsEmpty()) { count++;
*result = GetName(); }
*result = Convert::ToString(count);
return true; return true;
} else {
*result = value;
return true;
}
}
else if (macro == "host.vars.address" || macro == "host.vars.address6") {
key = macro.SubStr(10);
vars = GetVars();
String value;
if (vars && vars->Contains(key))
value = vars->Get(key);
if (value.IsEmpty()) {
*result = GetName();
return true;
} else {
*result = value;
return true;
}
} }
/* require prefix for object macros */ CheckResult::Ptr cr = GetLastCheckResult();
if (macro.SubStr(0, 5) == "host.") {
key = macro.SubStr(5);
if (key.SubStr(0, 5) == "vars.") { if (cr) {
vars = GetVars(); if (macro == "latency") {
String vars_key = key.SubStr(5); *result = Convert::ToString(Service::CalculateLatency(cr));
if (vars && vars->Contains(vars_key)) {
*result = vars->Get(vars_key);
return true;
}
}
else if (key == "name") {
*result = GetName();
return true; return true;
} } else if (macro == "execution_time") {
else if (key == "displaymane") { *result = Convert::ToString(Service::CalculateExecutionTime(cr));
*result = GetDisplayName();
return true; return true;
} } else if (macro == "output") {
*result = cr->GetOutput();
CheckResult::Ptr cr = GetLastCheckResult();
if (key == "state") {
*result = StateToString(GetState());
return true; return true;
} else if (key == "stateid") { } else if (macro == "perfdata") {
*result = Convert::ToString(GetState()); *result = PluginUtility::FormatPerfdata(cr->GetPerformanceData());
return true; return true;
} else if (key == "statetype") { } else if (macro == "last_check") {
*result = StateTypeToString(GetStateType()); *result = Convert::ToString((long)cr->GetScheduleStart());
return true;
} else if (key == "attempt") {
*result = Convert::ToString(GetCheckAttempt());
return true;
} else if (key == "maxattempt") {
*result = Convert::ToString(GetMaxCheckAttempts());
return true;
} else if (key == "laststate") {
*result = StateToString(GetLastState());
return true;
} else if (key == "laststateid") {
*result = Convert::ToString(GetLastState());
return true;
} else if (key == "laststatetype") {
*result = StateTypeToString(GetLastStateType());
return true;
} else if (key == "laststatechange") {
*result = Convert::ToString((long)GetLastStateChange());
return true;
} else if (key == "durationsec") {
*result = Convert::ToString((long)(Utility::GetTime() - GetLastStateChange()));
return true;
} else if (key == "checkcommand") {
CheckCommand::Ptr commandObj = GetCheckCommand();
if (commandObj)
*result = commandObj->GetName();
else
*result = "";
return true;
} else if (key == "totalservices" || key == "totalservicesok" || key == "totalserviceswarning"
|| key == "totalservicesunknown" || key == "totalservicescritical") {
int filter = -1;
int count = 0;
if (key == "totalservicesok")
filter = ServiceOK;
else if (key == "totalserviceswarning")
filter = ServiceWarning;
else if (key == "totalservicesunknown")
filter = ServiceUnknown;
else if (key == "totalservicescritical")
filter = ServiceCritical;
BOOST_FOREACH(const Service::Ptr& service, GetServices()) {
if (filter != -1 && service->GetState() != filter)
continue;
count++;
}
*result = Convert::ToString(count);
return true;
}
if (cr) {
if (key == "latency") {
*result = Convert::ToString(Service::CalculateLatency(cr));
return true;
} else if (key == "executiontime") {
*result = Convert::ToString(Service::CalculateExecutionTime(cr));
return true;
} else if (key == "output") {
*result = cr->GetOutput();
return true;
} else if (key == "perfdata") {
*result = PluginUtility::FormatPerfdata(cr->GetPerformanceData());
return true;
} else if (key == "lastcheck") {
*result = Convert::ToString((long)cr->GetScheduleStart());
return true;
}
}
} else {
vars = GetVars();
if (vars && vars->Contains(macro)) {
*result = vars->Get(macro);
return true; return true;
} }
} }

View File

@ -15,6 +15,9 @@ class Host : Checkable
}}} }}}
}; };
[config] Array::Ptr groups; [config] Array::Ptr groups;
[config] String address;
[config] String address6;
}; };
} }

View File

@ -52,6 +52,12 @@
%attribute %array "authorities" { %attribute %array "authorities" {
%attribute %name(Endpoint) "*" %attribute %name(Endpoint) "*"
}, },
%attribute %string "notes",
%attribute %string "notes_url",
%attribute %string "action_url",
%attribute %string "icon_image",
%attribute %string "icon_image_alt",
} }
%type Host %inherits Checkable { %type Host %inherits Checkable {
@ -59,6 +65,9 @@
%attribute %array "groups" { %attribute %array "groups" {
%attribute %name(HostGroup) "*" %attribute %name(HostGroup) "*"
}, },
%attribute %string "address",
%attribute %string "address6",
} }
%type HostGroup { %type HostGroup {
@ -133,7 +142,10 @@
%attribute %array "notification_state_filter" { %attribute %array "notification_state_filter" {
%attribute %number "*" %attribute %number "*"
}, },
%attribute %name(TimePeriod) "notification_period" %attribute %name(TimePeriod) "notification_period",
%attribute %string "email",
%attribute %string "pager",
} }
%type UserGroup { %type UserGroup {

View File

@ -133,29 +133,23 @@ String IcingaApplication::GetNodeName(void) const
bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const
{ {
/* require icinga prefix for application macros */ double now = Utility::GetTime();
if (macro.SubStr(0, 7) == "icinga.") {
String key = macro.SubStr(7);
double now = Utility::GetTime();
if (key == "timet") {
*result = Convert::ToString((long)now);
return true;
} else if (key == "longdatetime") {
*result = Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", now);
return true;
} else if (key == "shortdatetime") {
*result = Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", now);
return true;
} else if (key == "date") {
*result = Utility::FormatDateTime("%Y-%m-%d", now);
return true;
} else if (key == "time") {
*result = Utility::FormatDateTime("%H:%M:%S %z", now);
return true;
}
if (macro == "timet") {
*result = Convert::ToString((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;
} }
Dictionary::Ptr vars = GetVars(); Dictionary::Ptr vars = GetVars();

View File

@ -24,11 +24,15 @@
#include "base/objectlock.h" #include "base/objectlock.h"
#include "base/logger_fwd.h" #include "base/logger_fwd.h"
#include "base/context.h" #include "base/context.h"
#include "base/dynamicobject.h"
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/classification.hpp>
using namespace icinga; using namespace icinga;
Value MacroProcessor::ResolveMacros(const Value& str, const std::vector<MacroResolver::Ptr>& resolvers, Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers,
const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn) const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn)
{ {
Value result; Value result;
@ -57,25 +61,93 @@ Value MacroProcessor::ResolveMacros(const Value& str, const std::vector<MacroRes
return result; return result;
} }
bool MacroProcessor::ResolveMacro(const String& macro, const std::vector<MacroResolver::Ptr>& resolvers, bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resolvers,
const CheckResult::Ptr& cr, String *result) const CheckResult::Ptr& cr, String *result)
{ {
CONTEXT("Resolving macro '" + macro + "'"); CONTEXT("Resolving macro '" + macro + "'");
BOOST_FOREACH(const MacroResolver::Ptr& resolver, resolvers) { std::vector<String> tokens;
if (resolver->ResolveMacro(macro, cr, result)) boost::algorithm::split(tokens, macro, boost::is_any_of("."));
String objName;
if (tokens.size() > 1) {
objName = tokens[0];
tokens.erase(tokens.begin());
}
BOOST_FOREACH(const ResolverSpec& resolver, resolvers) {
if (!objName.IsEmpty() && objName != resolver.first)
continue;
if (objName.IsEmpty()) {
DynamicObject::Ptr dobj = dynamic_pointer_cast<DynamicObject>(resolver.second);
if (dobj) {
Dictionary::Ptr vars = dobj->GetVars();
if (vars && vars->Contains(macro)) {
*result = vars->Get(macro);
return true;
}
}
}
MacroResolver::Ptr mresolver = dynamic_pointer_cast<MacroResolver>(resolver.second);
if (mresolver && mresolver->ResolveMacro(boost::algorithm::join(tokens, "."), cr, result))
return true; return true;
Value ref = resolver.second;
bool valid = true;
BOOST_FOREACH(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;
const Type *type = object->GetReflectionType();
if (!type) {
valid = false;
break;
}
int field = type->GetFieldId(token);
if (field == -1) {
valid = false;
break;
}
ref = object->GetField(field);
}
}
if (valid) {
*result = ref;
return true;
}
} }
return false; return false;
} }
String MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers,
String MacroProcessor::InternalResolveMacros(const String& str, const std::vector<MacroResolver::Ptr>& resolvers, const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn, int recursionLevel)
const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn)
{ {
CONTEXT("Resolving macros for string '" + str + "'"); CONTEXT("Resolving macros for string '" + str + "'");
if (recursionLevel > 15)
BOOST_THROW_EXCEPTION(std::runtime_error("Infinite recursion detected while resolving macros"));
size_t offset, pos_first, pos_second; size_t offset, pos_first, pos_second;
offset = 0; offset = 0;
@ -100,6 +172,9 @@ String MacroProcessor::InternalResolveMacros(const String& str, const std::vecto
if (!found) if (!found)
Log(LogWarning, "icinga", "Macro '" + name + "' is not defined."); Log(LogWarning, "icinga", "Macro '" + name + "' is not defined.");
/* recursively resolve macros in the macro */
resolved_macro = InternalResolveMacros(resolved_macro, resolvers, cr, EscapeCallback(), recursionLevel + 1);
if (escapeFn) if (escapeFn)
resolved_macro = escapeFn(resolved_macro); resolved_macro = escapeFn(resolved_macro);

View File

@ -25,7 +25,7 @@
#include "base/dictionary.h" #include "base/dictionary.h"
#include "base/array.h" #include "base/array.h"
#include <boost/function.hpp> #include <boost/function.hpp>
#include <vector> #include <map>
namespace icinga namespace icinga
{ {
@ -39,18 +39,20 @@ class I2_ICINGA_API MacroProcessor
{ {
public: public:
typedef boost::function<String (const String&)> EscapeCallback; typedef boost::function<String (const String&)> EscapeCallback;
typedef std::pair<String, Object::Ptr> ResolverSpec;
typedef std::vector<ResolverSpec> ResolverList;
static Value ResolveMacros(const Value& str, const std::vector<MacroResolver::Ptr>& resolvers, static Value ResolveMacros(const Value& str, const ResolverList& resolvers,
const CheckResult::Ptr& cr, const EscapeCallback& escapeFn = EscapeCallback()); const CheckResult::Ptr& cr, const EscapeCallback& escapeFn = EscapeCallback());
static bool ResolveMacro(const String& macro, const std::vector<MacroResolver::Ptr>& resolvers, static bool ResolveMacro(const String& macro, const ResolverList& resolvers,
const CheckResult::Ptr& cr, String *result); const CheckResult::Ptr& cr, String *result);
private: private:
MacroProcessor(void); MacroProcessor(void);
static String InternalResolveMacros(const String& str, static String InternalResolveMacros(const String& str,
const std::vector<MacroResolver::Ptr>& resolvers, const CheckResult::Ptr& cr, const ResolverList& resolvers, const CheckResult::Ptr& cr,
const EscapeCallback& escapeFn); const EscapeCallback& escapeFn, int recursionLevel = 0);
}; };
} }

View File

@ -20,22 +20,3 @@
#include "icinga/macroresolver.h" #include "icinga/macroresolver.h"
using namespace icinga; using namespace icinga;
StaticMacroResolver::StaticMacroResolver(void)
: m_Macros(make_shared<Dictionary>())
{ }
void StaticMacroResolver::Add(const String& macro, const String& value)
{
m_Macros->Set(macro, value);
}
bool StaticMacroResolver::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const
{
if (m_Macros->Contains(macro)) {
*result = m_Macros->Get(macro);
return true;
}
return false;
}

View File

@ -41,21 +41,6 @@ public:
virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const = 0; virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const = 0;
}; };
class I2_ICINGA_API StaticMacroResolver : public Object, public MacroResolver
{
public:
DECLARE_PTR_TYPEDEFS(StaticMacroResolver);
StaticMacroResolver(void);
void Add(const String& macro, const String& value);
virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const;
private:
Dictionary::Ptr m_Macros;
};
} }
#endif /* MACRORESOLVER_H */ #endif /* MACRORESOLVER_H */

View File

@ -135,93 +135,47 @@ String Service::StateTypeToString(StateType type)
bool Service::ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const bool Service::ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const
{ {
String key; if (macro == "state") {
Dictionary::Ptr vars; *result = StateToString(GetState());
return true;
} else if (macro == "state_id") {
*result = Convert::ToString(GetState());
return true;
} else if (macro == "state_type") {
*result = StateTypeToString(GetStateType());
return true;
} else if (macro == "last_state") {
*result = StateToString(GetLastState());
return true;
} else if (macro == "last_state_id") {
*result = Convert::ToString(GetLastState());
return true;
} else if (macro == "last_state_type") {
*result = StateTypeToString(GetLastStateType());
return true;
} else if (macro == "last_state_change") {
*result = Convert::ToString((long)GetLastStateChange());
return true;
} else if (macro == "duration_sec") {
*result = Convert::ToString((long)(Utility::GetTime() - GetLastStateChange()));
return true;
}
/* require prefix for object macros */ if (cr) {
if (macro.SubStr(0, 8) == "service.") { if (macro == "latency") {
key = macro.SubStr(8); *result = Convert::ToString(Service::CalculateLatency(cr));
if (key.SubStr(0, 5) == "vars.") {
vars = GetVars();
String vars_key = key.SubStr(5);
if (vars && vars->Contains(vars_key)) {
*result = vars->Get(vars_key);
return true;
}
} else if (key == "description") {
*result = GetShortName();
return true; return true;
} else if (key == "displayname") { } else if (macro == "execution_time") {
*result = GetDisplayName(); *result = Convert::ToString(Service::CalculateExecutionTime(cr));
return true; return true;
} else if (key == "checkcommand") { } else if (macro == "output") {
CheckCommand::Ptr commandObj = GetCheckCommand(); *result = cr->GetOutput();
if (commandObj)
*result = commandObj->GetName();
else
*result = "";
return true; return true;
} } else if (macro == "perfdata") {
*result = PluginUtility::FormatPerfdata(cr->GetPerformanceData());
if (key == "state") {
*result = StateToString(GetState());
return true; return true;
} else if (key == "stateid") { } else if (macro == "last_check") {
*result = Convert::ToString(GetState()); *result = Convert::ToString((long)cr->GetExecutionEnd());
return true;
} else if (key == "statetype") {
*result = StateTypeToString(GetStateType());
return true;
} else if (key == "attempt") {
*result = Convert::ToString(GetCheckAttempt());
return true;
} else if (key == "maxattempt") {
*result = Convert::ToString(GetMaxCheckAttempts());
return true;
} else if (key == "laststate") {
*result = StateToString(GetLastState());
return true;
} else if (key == "laststateid") {
*result = Convert::ToString(GetLastState());
return true;
} else if (key == "laststatetype") {
*result = StateTypeToString(GetLastStateType());
return true;
} else if (key == "laststatechange") {
*result = Convert::ToString((long)GetLastStateChange());
return true;
} else if (key == "durationsec") {
*result = Convert::ToString((long)(Utility::GetTime() - GetLastStateChange()));
return true;
}
if (cr) {
if (key == "latency") {
*result = Convert::ToString(Service::CalculateLatency(cr));
return true;
} else if (key == "executiontime") {
*result = Convert::ToString(Service::CalculateExecutionTime(cr));
return true;
} else if (key == "output") {
*result = cr->GetOutput();
return true;
} else if (key == "perfdata") {
*result = PluginUtility::FormatPerfdata(cr->GetPerformanceData());
return true;
} else if (key == "lastcheck") {
*result = Convert::ToString((long)cr->GetExecutionEnd());
return true;
}
}
} else {
vars = GetVars();
if (vars && vars->Contains(macro)) {
*result = vars->Get(macro);
return true; return true;
} }
} }

View File

@ -91,39 +91,3 @@ void User::ValidateFilters(const String& location, const Dictionary::Ptr& attrs)
location + ": Type filter is invalid."); location + ": Type filter is invalid.");
} }
} }
bool User::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const
{
String key;
Dictionary::Ptr vars;
/* require prefix for object macros */
if (macro.SubStr(0, 5) == "user.") {
key = macro.SubStr(5);
if (key.SubStr(0, 5) == "vars.") {
vars = GetVars();
String vars_key = key.SubStr(5);
if (vars && vars->Contains(vars_key)) {
*result = vars->Get(vars_key);
return true;
}
} else if (key == "name") {
*result = GetName();
return true;
} else if (key == "displayname") {
*result = GetDisplayName();
return true;
}
} else {
vars = GetVars();
if (vars && vars->Contains(macro)) {
*result = vars->Get(macro);
return true;
}
}
return false;
}

View File

@ -34,7 +34,7 @@ namespace icinga
* *
* @ingroup icinga * @ingroup icinga
*/ */
class I2_ICINGA_API User : public ObjectImpl<User>, public MacroResolver class I2_ICINGA_API User : public ObjectImpl<User>
{ {
public: public:
DECLARE_PTR_TYPEDEFS(User); DECLARE_PTR_TYPEDEFS(User);
@ -43,8 +43,6 @@ public:
/* Notifications */ /* Notifications */
TimePeriod::Ptr GetNotificationPeriod(void) const; TimePeriod::Ptr GetNotificationPeriod(void) const;
virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const;
static void ValidateFilters(const String& location, const Dictionary::Ptr& attrs); static void ValidateFilters(const String& location, const Dictionary::Ptr& attrs);
protected: protected:

View File

@ -19,6 +19,10 @@ class User : DynamicObject
[config] Array::Ptr notification_type_filter (NotificationTypeFilterRaw); [config] Array::Ptr notification_type_filter (NotificationTypeFilterRaw);
int notification_type_filter_real (NotificationTypeFilter); int notification_type_filter_real (NotificationTypeFilter);
[config] Array::Ptr notification_state_filter (NotificationStateFilterRaw); [config] Array::Ptr notification_state_filter (NotificationStateFilterRaw);
[config] String email;
[config] String pager;
int notification_state_filter_real (NotificationStateFilter); int notification_state_filter_real (NotificationStateFilter);
[state] bool enable_notifications; [state] bool enable_notifications;
[state] double last_notification; [state] double last_notification;

View File

@ -44,12 +44,12 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes
Service::Ptr service; Service::Ptr service;
tie(host, service) = GetHostService(checkable); tie(host, service) = GetHostService(checkable);
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
if (service) if (service)
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(host); resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(commandObj); resolvers.push_back(std::make_pair("command", commandObj));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, checkable->GetLastCheckResult(), Utility::EscapeShellCmd); Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, checkable->GetLastCheckResult(), Utility::EscapeShellCmd);

View File

@ -41,12 +41,12 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable)
Service::Ptr service; Service::Ptr service;
tie(host, service) = GetHostService(checkable); tie(host, service) = GetHostService(checkable);
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
if (service) if (service)
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(host); resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(commandObj); resolvers.push_back(std::make_pair("command", commandObj));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, checkable->GetLastCheckResult(), Utility::EscapeShellCmd); Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, checkable->GetLastCheckResult(), Utility::EscapeShellCmd);

View File

@ -44,24 +44,24 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, c
Value raw_command = commandObj->GetCommandLine(); Value raw_command = commandObj->GetCommandLine();
StaticMacroResolver::Ptr notificationMacroResolver = make_shared<StaticMacroResolver>(); Dictionary::Ptr notificationExtra = make_shared<Dictionary>();
notificationMacroResolver->Add("notification.type", Notification::NotificationTypeToString(type)); notificationExtra->Set("type", Notification::NotificationTypeToString(type));
notificationMacroResolver->Add("notification.author", author); notificationExtra->Set("author", author);
notificationMacroResolver->Add("notification.comment", comment); notificationExtra->Set("comment", comment);
Host::Ptr host; Host::Ptr host;
Service::Ptr service; Service::Ptr service;
tie(host, service) = GetHostService(checkable); tie(host, service) = GetHostService(checkable);
std::vector<MacroResolver::Ptr> resolvers; MacroProcessor::ResolverList resolvers;
resolvers.push_back(user); resolvers.push_back(std::make_pair("user", user));
resolvers.push_back(notificationMacroResolver); resolvers.push_back(std::make_pair("notification", notificationExtra));
resolvers.push_back(notification); resolvers.push_back(std::make_pair("notification", notification));
if (service) if (service)
resolvers.push_back(service); resolvers.push_back(std::make_pair("service", service));
resolvers.push_back(host);; resolvers.push_back(std::make_pair("host", host));
resolvers.push_back(commandObj); resolvers.push_back(std::make_pair("command", commandObj));
resolvers.push_back(IcingaApplication::GetInstance()); resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance()));
Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, cr, Utility::EscapeShellCmd); Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, cr, Utility::EscapeShellCmd);