From 0fdb9ea21b9feaea93da8c5c7bc7df575d912891 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 8 Apr 2014 13:23:24 +0200 Subject: [PATCH] Implement attributes for some well-known macros and allow macro recursion. Refs #5856 Fixes #5959 --- components/livestatus/hoststable.cpp | 28 ++- components/livestatus/servicestable.cpp | 32 ++-- components/perfdata/perfdatawriter.cpp | 8 +- components/perfdata/perfdatawriter.ti | 12 +- doc/3.03-custom-attributes-runtime-macros.md | 103 +++++----- etc/icinga2/conf.d/localhost.conf | 4 +- lib/icinga/checkable.ti | 6 + lib/icinga/host.cpp | 192 ++++++------------- lib/icinga/host.ti | 3 + lib/icinga/icinga-type.conf | 14 +- lib/icinga/icingaapplication.cpp | 38 ++-- lib/icinga/macroprocessor.cpp | 89 ++++++++- lib/icinga/macroprocessor.h | 12 +- lib/icinga/macroresolver.cpp | 19 -- lib/icinga/macroresolver.h | 15 -- lib/icinga/service.cpp | 118 ++++-------- lib/icinga/user.cpp | 36 ---- lib/icinga/user.h | 4 +- lib/icinga/user.ti | 4 + lib/methods/pluginchecktask.cpp | 10 +- lib/methods/plugineventtask.cpp | 10 +- lib/methods/pluginnotificationtask.cpp | 24 +-- 22 files changed, 340 insertions(+), 441 deletions(-) diff --git a/components/livestatus/hoststable.cpp b/components/livestatus/hoststable.cpp index bc1ffe549..0fc7ff2da 100644 --- a/components/livestatus/hoststable.cpp +++ b/components/livestatus/hoststable.cpp @@ -295,10 +295,9 @@ Value HostsTable::NotesExpandedAccessor(const Value& row) if (!host) return Empty; - std::vector resolvers; - - resolvers.push_back(host); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(host, "notes"); @@ -322,10 +321,9 @@ Value HostsTable::NotesUrlExpandedAccessor(const Value& row) if (!host) return Empty; - std::vector resolvers; - - resolvers.push_back(host); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(host, "notes_url"); @@ -349,10 +347,9 @@ Value HostsTable::ActionUrlExpandedAccessor(const Value& row) if (!host) return Empty; - std::vector resolvers; - - resolvers.push_back(host); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(host, "action_url"); @@ -399,10 +396,9 @@ Value HostsTable::IconImageExpandedAccessor(const Value& row) if (!host) return Empty; - std::vector resolvers; - - resolvers.push_back(host); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(host, "icon_image"); diff --git a/components/livestatus/servicestable.cpp b/components/livestatus/servicestable.cpp index 429b640d1..b521287d2 100644 --- a/components/livestatus/servicestable.cpp +++ b/components/livestatus/servicestable.cpp @@ -310,10 +310,10 @@ Value ServicesTable::NotesExpandedAccessor(const Value& row) if (!service) return Empty; - std::vector resolvers; - resolvers.push_back(service); - resolvers.push_back(service->GetHost()); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", service->GetHost())); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(service, "notes"); @@ -337,10 +337,10 @@ Value ServicesTable::NotesUrlExpandedAccessor(const Value& row) if (!service) return Empty; - std::vector resolvers; - resolvers.push_back(service); - resolvers.push_back(service->GetHost()); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", service->GetHost())); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(service, "notes_url"); @@ -364,10 +364,10 @@ Value ServicesTable::ActionUrlExpandedAccessor(const Value& row) if (!service) return Empty; - std::vector resolvers; - resolvers.push_back(service); - resolvers.push_back(service->GetHost()); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", service->GetHost())); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(service, "action_url"); @@ -391,10 +391,10 @@ Value ServicesTable::IconImageExpandedAccessor(const Value& row) if (!service) return Empty; - std::vector resolvers; - resolvers.push_back(service); - resolvers.push_back(service->GetHost()); - resolvers.push_back(IcingaApplication::GetInstance()); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", service->GetHost())); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value value = CompatUtility::GetCustomAttributeConfig(service, "icon_image"); diff --git a/components/perfdata/perfdatawriter.cpp b/components/perfdata/perfdatawriter.cpp index 8cd227a19..8cdb2c36b 100644 --- a/components/perfdata/perfdatawriter.cpp +++ b/components/perfdata/perfdatawriter.cpp @@ -79,11 +79,11 @@ void PerfdataWriter::CheckResultHandler(const Checkable::Ptr& checkable, const C else host = static_pointer_cast(checkable); - std::vector resolvers; + MacroProcessor::ResolverList resolvers; if (service) - resolvers.push_back(service); - resolvers.push_back(host); - resolvers.push_back(IcingaApplication::GetInstance()); + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); if (service) { String line = MacroProcessor::ResolveMacros(GetServiceFormatTemplate(), resolvers, cr); diff --git a/components/perfdata/perfdatawriter.ti b/components/perfdata/perfdatawriter.ti index dca86e492..95f9cfc39 100644 --- a/components/perfdata/perfdatawriter.ti +++ b/components/perfdata/perfdatawriter.ti @@ -24,9 +24,9 @@ class PerfdataWriter : DynamicObject "TIMET::$icinga.timet$\t" "HOSTNAME::$host.name$\t" "HOSTPERFDATA::$host.perfdata$\t" - "HOSTCHECKCOMMAND::$host.checkcommand$\t" + "HOSTCHECKCOMMAND::$host.check_command$\t" "HOSTSTATE::$host.state$\t" - "HOSTSTATETYPE::$host.statetype$"; + "HOSTSTATETYPE::$host.state_type$"; }}} }; [config] String service_format_template { @@ -34,13 +34,13 @@ class PerfdataWriter : DynamicObject return "DATATYPE::SERVICEPERFDATA\t" "TIMET::$icinga.timet$\t" "HOSTNAME::$host.name$\t" - "SERVICEDESC::$service.description$\t" + "SERVICEDESC::$service.name$\t" "SERVICEPERFDATA::$service.perfdata$\t" - "SERVICECHECKCOMMAND::$service.checkcommand$\t" + "SERVICECHECKCOMMAND::$service.check_command$\t" "HOSTSTATE::$host.state$\t" - "HOSTSTATETYPE::$host.statetype$\t" + "HOSTSTATETYPE::$host.state_type$\t" "SERVICESTATE::$service.state$\t" - "SERVICESTATETYPE::$service.statetype$"; + "SERVICESTATETYPE::$service.state_type$"; }}} }; diff --git a/doc/3.03-custom-attributes-runtime-macros.md b/doc/3.03-custom-attributes-runtime-macros.md index 02bfc1338..c60b6c5bd 100644 --- a/doc/3.03-custom-attributes-runtime-macros.md +++ b/doc/3.03-custom-attributes-runtime-macros.md @@ -119,56 +119,64 @@ external commands). The following host custom attributes are available in all commands that are executed for hosts or services: - Name | Description - ---------------------------|-------------- - host.name | The name of the host object. - host.displayname | The value of the `display_name` attribute. - 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.statetype | The host's current state type. Can be one of `SOFT` and `HARD`. - host.attempt | The current check attempt number. - host.maxattempt | 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.laststateid | 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.laststatechange | The last state change's timestamp. - host.durationsec | The time since the last state change. - host.latency | The host's check latency. - host.executiontime | The host's check execution time. - host.output | The last check's output. - host.perfdata | The last check's performance data. - host.lastcheck | The timestamp when the last check was executed. - host.totalservices | Number of services associated with the host. - host.totalservicesok | 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.totalservicesunknown | 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. + Name | Description + -----------------------------|-------------- + host.name | The name of the host object. + 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.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.check_attempt | The current check attempt number. + host.max_check_attempts | The maximum number of checks which are executed before changing to a hard state. + host.last_state | The host's previous state. Can be one of `UNREACHABLE`, `UP` and `DOWN`. + host.last_state_id | The host's previous state. Can be one of `0` (up), `1` (down) and `2` (unreachable). + host.last_state_type | The host's previous state type. Can be one of `SOFT` and `HARD`. + host.last_state_change | The last state change's timestamp. + host.duration_sec | The time since the last state change. + host.latency | The host's check latency. + host.execution_time | The host's check execution time. + host.output | The last check's output. + host.perfdata | The last check's performance data. + host.last_check | The timestamp when the last check was executed. + host.total_services | Number of services associated with the host. + host.total_services_ok | Number of services associated with the host which are in an `OK` state. + host.total_services_warning | Number of services associated with the host which are in a `WARNING` state. + host.total_services_unknown | Number of services associated with the host which are in an `UNKNOWN` state. + host.total_services_critical | Number of services associated with the host which are in a `CRITICAL` state. ### Service Runtime Macros The following service macros are available in all commands that are executed for services: - Name | Description - ------------------------|-------------- - service.description | The short name of the service object. - service.displayname | The value of the `display_name` attribute. - service.checkcommand | 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.stateid | 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.attempt | The current check attempt number. - service.maxattempt | 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.laststateid | 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.laststatechange | The last state change's timestamp. - service.durationsec | The time since the last state change. - service.latency | The service's check latency. - service.executiontime | The service's check execution time. - service.output | The last check's output. - service.perfdata | The last check's performance data. - service.lastcheck | The timestamp when the last check was executed. + Name | Description + ---------------------------|-------------- + service.name | The short name of the service object. + service.display_name | The value of the `display_name` attribute. + 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_id | The service's current state. Can be one of `0` (ok), `1` (warning), `2` (critical) and `3` (unknown). + service.state_type | The service's current state type. Can be one of `SOFT` and `HARD`. + service.check_attempt | The current check attempt number. + service.max_check_attempts | The maximum number of checks which are executed before changing to a hard state. + service.last_state | The service's previous state. Can be one of `OK`, `WARNING`, `CRITICAL` and `UNKNOWN`. + service.last_state_id | The service's previous state. Can be one of `0` (ok), `1` (warning), `2` (critical) and `3` (unknown). + service.last_state_type | The service's previous state type. Can be one of `SOFT` and `HARD`. + service.last_state_change | The last state change's timestamp. + service.duration_sec | The time since the last state change. + service.latency | The service's check latency. + service.execution_time | The service's check execution time. + service.output | The last check's output. + service.perfdata | The last check's performance data. + service.last_check | The timestamp when the last check was executed. + +### Command Runtime Macros + +The following custom attributes are available in all commands: + + Name | Description + -----------------------|-------------- + command.name | The name of the command object. ### User Runtime Macros @@ -178,8 +186,7 @@ users: Name | Description -----------------------|-------------- 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. ### Notification Runtime Macros @@ -196,7 +203,7 @@ The following macros are available in all executed commands: Name | Description -----------------------|-------------- icinga.timet | Current UNIX timestamp. - icinga.longdatetime | 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.long_date_time | Current date and time including timezone information. Example: `2014-01-03 11:23:08 +0000` + icinga.short_date_time | Current date and time. Example: `2014-01-03 11:23:08` icinga.date | Current date. Example: `2014-01-03` icinga.time | Current time including timezone information. Example: `11:23:08 +0000` diff --git a/etc/icinga2/conf.d/localhost.conf b/etc/icinga2/conf.d/localhost.conf index 7c80e6929..f4b208347 100644 --- a/etc/icinga2/conf.d/localhost.conf +++ b/etc/icinga2/conf.d/localhost.conf @@ -6,8 +6,8 @@ object Host "localhost" { import "linux-server" - vars.address = "127.0.0.1" - vars.address6 = "::1" + address = "127.0.0.1" + address6 = "::1" } object Service "icinga" { diff --git a/lib/icinga/checkable.ti b/lib/icinga/checkable.ti index 9f5493cf7..dd2fade39 100644 --- a/lib/icinga/checkable.ti +++ b/lib/icinga/checkable.ti @@ -56,6 +56,12 @@ abstract class Checkable : DynamicObject 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] int check_attempt { default {{{ return 1; }}} diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 32c6b775e..aaf427d95 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -221,154 +221,72 @@ String Host::StateTypeToString(StateType type) bool Host::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const { - String key; - Dictionary::Ptr vars; + if (macro == "state") { + *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 == "address" || macro == "address6") { - vars = GetVars(); + if (macro == "total_services_ok") + filter = ServiceOK; + 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; - if (vars && vars->Contains(macro)) - value = vars->Get(macro); + BOOST_FOREACH(const Service::Ptr& service, GetServices()) { + if (filter != -1 && service->GetState() != filter) + continue; - if (value.IsEmpty()) { - *result = GetName(); + count++; + } + + *result = Convert::ToString(count); 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 */ - if (macro.SubStr(0, 5) == "host.") { - key = macro.SubStr(5); + CheckResult::Ptr cr = GetLastCheckResult(); - 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(); + if (cr) { + if (macro == "latency") { + *result = Convert::ToString(Service::CalculateLatency(cr)); return true; - } - else if (key == "displaymane") { - *result = GetDisplayName(); + } else if (macro == "execution_time") { + *result = Convert::ToString(Service::CalculateExecutionTime(cr)); return true; - } - - CheckResult::Ptr cr = GetLastCheckResult(); - - if (key == "state") { - *result = StateToString(GetState()); + } else if (macro == "output") { + *result = cr->GetOutput(); return true; - } else if (key == "stateid") { - *result = Convert::ToString(GetState()); + } else if (macro == "perfdata") { + *result = PluginUtility::FormatPerfdata(cr->GetPerformanceData()); 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; - } 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); + } else if (macro == "last_check") { + *result = Convert::ToString((long)cr->GetScheduleStart()); return true; } } diff --git a/lib/icinga/host.ti b/lib/icinga/host.ti index c0ec1891c..a45493258 100644 --- a/lib/icinga/host.ti +++ b/lib/icinga/host.ti @@ -15,6 +15,9 @@ class Host : Checkable }}} }; [config] Array::Ptr groups; + + [config] String address; + [config] String address6; }; } diff --git a/lib/icinga/icinga-type.conf b/lib/icinga/icinga-type.conf index 5acc3b87b..7bf57db97 100644 --- a/lib/icinga/icinga-type.conf +++ b/lib/icinga/icinga-type.conf @@ -52,6 +52,12 @@ %attribute %array "authorities" { %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 { @@ -59,6 +65,9 @@ %attribute %array "groups" { %attribute %name(HostGroup) "*" }, + + %attribute %string "address", + %attribute %string "address6", } %type HostGroup { @@ -133,7 +142,10 @@ %attribute %array "notification_state_filter" { %attribute %number "*" }, - %attribute %name(TimePeriod) "notification_period" + %attribute %name(TimePeriod) "notification_period", + + %attribute %string "email", + %attribute %string "pager", } %type UserGroup { diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp index b65cf2e80..bc161e0b5 100644 --- a/lib/icinga/icingaapplication.cpp +++ b/lib/icinga/icingaapplication.cpp @@ -133,29 +133,23 @@ String IcingaApplication::GetNodeName(void) const bool IcingaApplication::ResolveMacro(const String& macro, const CheckResult::Ptr&, String *result) const { - /* require icinga prefix for application macros */ - 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; - } + double now = Utility::GetTime(); + 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(); diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index 0cf1d9e01..103c4cc18 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -24,11 +24,15 @@ #include "base/objectlock.h" #include "base/logger_fwd.h" #include "base/context.h" +#include "base/dynamicobject.h" #include +#include +#include +#include using namespace icinga; -Value MacroProcessor::ResolveMacros(const Value& str, const std::vector& resolvers, +Value MacroProcessor::ResolveMacros(const Value& str, const ResolverList& resolvers, const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn) { Value result; @@ -57,25 +61,93 @@ Value MacroProcessor::ResolveMacros(const Value& str, const std::vector& resolvers, +bool MacroProcessor::ResolveMacro(const String& macro, const ResolverList& resolvers, const CheckResult::Ptr& cr, String *result) { CONTEXT("Resolving macro '" + macro + "'"); - BOOST_FOREACH(const MacroResolver::Ptr& resolver, resolvers) { - if (resolver->ResolveMacro(macro, cr, result)) + std::vector tokens; + 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(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(resolver.second); + + if (mresolver && mresolver->ResolveMacro(boost::algorithm::join(tokens, "."), cr, result)) return true; + + Value ref = resolver.second; + bool valid = true; + + BOOST_FOREACH(const String& token, tokens) { + if (ref.IsObjectType()) { + 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; } - -String MacroProcessor::InternalResolveMacros(const String& str, const std::vector& resolvers, - const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn) +String MacroProcessor::InternalResolveMacros(const String& str, const ResolverList& resolvers, + const CheckResult::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn, int recursionLevel) { 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; offset = 0; @@ -100,6 +172,9 @@ String MacroProcessor::InternalResolveMacros(const String& str, const std::vecto if (!found) 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) resolved_macro = escapeFn(resolved_macro); diff --git a/lib/icinga/macroprocessor.h b/lib/icinga/macroprocessor.h index cd7fead01..61cea23ba 100644 --- a/lib/icinga/macroprocessor.h +++ b/lib/icinga/macroprocessor.h @@ -25,7 +25,7 @@ #include "base/dictionary.h" #include "base/array.h" #include -#include +#include namespace icinga { @@ -39,18 +39,20 @@ class I2_ICINGA_API MacroProcessor { public: typedef boost::function EscapeCallback; + typedef std::pair ResolverSpec; + typedef std::vector ResolverList; - static Value ResolveMacros(const Value& str, const std::vector& resolvers, + static Value ResolveMacros(const Value& str, const ResolverList& resolvers, const CheckResult::Ptr& cr, const EscapeCallback& escapeFn = EscapeCallback()); - static bool ResolveMacro(const String& macro, const std::vector& resolvers, + static bool ResolveMacro(const String& macro, const ResolverList& resolvers, const CheckResult::Ptr& cr, String *result); private: MacroProcessor(void); static String InternalResolveMacros(const String& str, - const std::vector& resolvers, const CheckResult::Ptr& cr, - const EscapeCallback& escapeFn); + const ResolverList& resolvers, const CheckResult::Ptr& cr, + const EscapeCallback& escapeFn, int recursionLevel = 0); }; } diff --git a/lib/icinga/macroresolver.cpp b/lib/icinga/macroresolver.cpp index 99d837cfb..830fad6b5 100644 --- a/lib/icinga/macroresolver.cpp +++ b/lib/icinga/macroresolver.cpp @@ -20,22 +20,3 @@ #include "icinga/macroresolver.h" using namespace icinga; - -StaticMacroResolver::StaticMacroResolver(void) - : m_Macros(make_shared()) -{ } - -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; -} diff --git a/lib/icinga/macroresolver.h b/lib/icinga/macroresolver.h index 18b269416..8637d4bf9 100644 --- a/lib/icinga/macroresolver.h +++ b/lib/icinga/macroresolver.h @@ -41,21 +41,6 @@ public: 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 */ diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 0fc2a17df..0fcaecac3 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -135,93 +135,47 @@ String Service::StateTypeToString(StateType type) bool Service::ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const { - String key; - Dictionary::Ptr vars; + if (macro == "state") { + *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 (macro.SubStr(0, 8) == "service.") { - key = macro.SubStr(8); - - 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(); + if (cr) { + if (macro == "latency") { + *result = Convert::ToString(Service::CalculateLatency(cr)); return true; - } else if (key == "displayname") { - *result = GetDisplayName(); + } else if (macro == "execution_time") { + *result = Convert::ToString(Service::CalculateExecutionTime(cr)); return true; - } else if (key == "checkcommand") { - CheckCommand::Ptr commandObj = GetCheckCommand(); - - if (commandObj) - *result = commandObj->GetName(); - else - *result = ""; - + } else if (macro == "output") { + *result = cr->GetOutput(); return true; - } - - if (key == "state") { - *result = StateToString(GetState()); + } else if (macro == "perfdata") { + *result = PluginUtility::FormatPerfdata(cr->GetPerformanceData()); return true; - } else if (key == "stateid") { - *result = Convert::ToString(GetState()); - 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); + } else if (macro == "last_check") { + *result = Convert::ToString((long)cr->GetExecutionEnd()); return true; } } diff --git a/lib/icinga/user.cpp b/lib/icinga/user.cpp index 16fffb268..4231a02e0 100644 --- a/lib/icinga/user.cpp +++ b/lib/icinga/user.cpp @@ -91,39 +91,3 @@ void User::ValidateFilters(const String& location, const Dictionary::Ptr& attrs) 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; -} diff --git a/lib/icinga/user.h b/lib/icinga/user.h index 22509f075..4cff8709e 100644 --- a/lib/icinga/user.h +++ b/lib/icinga/user.h @@ -34,7 +34,7 @@ namespace icinga * * @ingroup icinga */ -class I2_ICINGA_API User : public ObjectImpl, public MacroResolver +class I2_ICINGA_API User : public ObjectImpl { public: DECLARE_PTR_TYPEDEFS(User); @@ -43,8 +43,6 @@ public: /* Notifications */ 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); protected: diff --git a/lib/icinga/user.ti b/lib/icinga/user.ti index 896abf2cf..74e9d74a3 100644 --- a/lib/icinga/user.ti +++ b/lib/icinga/user.ti @@ -19,6 +19,10 @@ class User : DynamicObject [config] Array::Ptr notification_type_filter (NotificationTypeFilterRaw); int notification_type_filter_real (NotificationTypeFilter); [config] Array::Ptr notification_state_filter (NotificationStateFilterRaw); + + [config] String email; + [config] String pager; + int notification_state_filter_real (NotificationStateFilter); [state] bool enable_notifications; [state] double last_notification; diff --git a/lib/methods/pluginchecktask.cpp b/lib/methods/pluginchecktask.cpp index 5e2431fe9..a5e4db767 100644 --- a/lib/methods/pluginchecktask.cpp +++ b/lib/methods/pluginchecktask.cpp @@ -44,12 +44,12 @@ void PluginCheckTask::ScriptFunc(const Checkable::Ptr& checkable, const CheckRes Service::Ptr service; tie(host, service) = GetHostService(checkable); - std::vector resolvers; + MacroProcessor::ResolverList resolvers; if (service) - resolvers.push_back(service); - resolvers.push_back(host); - resolvers.push_back(commandObj); - resolvers.push_back(IcingaApplication::GetInstance()); + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("command", commandObj)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, checkable->GetLastCheckResult(), Utility::EscapeShellCmd); diff --git a/lib/methods/plugineventtask.cpp b/lib/methods/plugineventtask.cpp index 37976d8d0..355db7efd 100644 --- a/lib/methods/plugineventtask.cpp +++ b/lib/methods/plugineventtask.cpp @@ -41,12 +41,12 @@ void PluginEventTask::ScriptFunc(const Checkable::Ptr& checkable) Service::Ptr service; tie(host, service) = GetHostService(checkable); - std::vector resolvers; + MacroProcessor::ResolverList resolvers; if (service) - resolvers.push_back(service); - resolvers.push_back(host); - resolvers.push_back(commandObj); - resolvers.push_back(IcingaApplication::GetInstance()); + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("command", commandObj)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, checkable->GetLastCheckResult(), Utility::EscapeShellCmd); diff --git a/lib/methods/pluginnotificationtask.cpp b/lib/methods/pluginnotificationtask.cpp index 88d7b148e..cccfb0059 100644 --- a/lib/methods/pluginnotificationtask.cpp +++ b/lib/methods/pluginnotificationtask.cpp @@ -44,24 +44,24 @@ void PluginNotificationTask::ScriptFunc(const Notification::Ptr& notification, c Value raw_command = commandObj->GetCommandLine(); - StaticMacroResolver::Ptr notificationMacroResolver = make_shared(); - notificationMacroResolver->Add("notification.type", Notification::NotificationTypeToString(type)); - notificationMacroResolver->Add("notification.author", author); - notificationMacroResolver->Add("notification.comment", comment); + Dictionary::Ptr notificationExtra = make_shared(); + notificationExtra->Set("type", Notification::NotificationTypeToString(type)); + notificationExtra->Set("author", author); + notificationExtra->Set("comment", comment); Host::Ptr host; Service::Ptr service; tie(host, service) = GetHostService(checkable); - std::vector resolvers; - resolvers.push_back(user); - resolvers.push_back(notificationMacroResolver); - resolvers.push_back(notification); + MacroProcessor::ResolverList resolvers; + resolvers.push_back(std::make_pair("user", user)); + resolvers.push_back(std::make_pair("notification", notificationExtra)); + resolvers.push_back(std::make_pair("notification", notification)); if (service) - resolvers.push_back(service); - resolvers.push_back(host);; - resolvers.push_back(commandObj); - resolvers.push_back(IcingaApplication::GetInstance()); + resolvers.push_back(std::make_pair("service", service)); + resolvers.push_back(std::make_pair("host", host)); + resolvers.push_back(std::make_pair("command", commandObj)); + resolvers.push_back(std::make_pair("icinga", IcingaApplication::GetInstance())); Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, cr, Utility::EscapeShellCmd);