From 6d69d6c63931b21816196df6d8b4c79173561303 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Fri, 22 Mar 2013 14:40:55 +0100 Subject: [PATCH] Refactor the macro resolver Fixes #3884 --- lib/icinga/Makefile.am | 2 + lib/icinga/host.cpp | 98 +++++++++++++-------- lib/icinga/host.h | 7 +- lib/icinga/icinga-type.conf | 16 +++- lib/icinga/icinga.vcxproj | 4 +- lib/icinga/icingaapplication.cpp | 35 ++++++-- lib/icinga/icingaapplication.h | 5 +- lib/icinga/macroprocessor.cpp | 67 +++++++------- lib/icinga/macroprocessor.h | 11 ++- lib/icinga/macroresolver.cpp | 42 +++++++++ lib/icinga/macroresolver.h | 62 +++++++++++++ lib/icinga/notification.cpp | 36 ++++---- lib/icinga/notification.h | 9 +- lib/icinga/perfdatawriter.cpp | 9 +- lib/icinga/pluginchecktask.cpp | 33 +++++-- lib/icinga/pluginnotificationtask.cpp | 47 +++++++--- lib/icinga/service-check.cpp | 7 -- lib/icinga/service-notification.cpp | 1 + lib/icinga/service.cpp | 122 ++++++++++++++------------ lib/icinga/service.h | 10 ++- lib/icinga/user.cpp | 26 ++++-- lib/icinga/user.h | 6 +- 22 files changed, 450 insertions(+), 205 deletions(-) create mode 100644 lib/icinga/macroresolver.cpp create mode 100644 lib/icinga/macroresolver.h diff --git a/lib/icinga/Makefile.am b/lib/icinga/Makefile.am index ee2db2677..3da4dcdef 100644 --- a/lib/icinga/Makefile.am +++ b/lib/icinga/Makefile.am @@ -30,6 +30,8 @@ libicinga_la_SOURCES = \ icingaapplication.h \ macroprocessor.cpp \ macroprocessor.h \ + macroresolver.cpp \ + macroresolver.h \ notification.cpp \ notification.h \ notificationrequestmessage.cpp \ diff --git a/lib/icinga/host.cpp b/lib/icinga/host.cpp index 408ba4721..209d85321 100644 --- a/lib/icinga/host.cpp +++ b/lib/icinga/host.cpp @@ -24,6 +24,7 @@ #include "base/objectlock.h" #include "base/logger_fwd.h" #include "base/timer.h" +#include "base/convert.h" #include "config/configitembuilder.h" #include "config/configcompilercontext.h" #include @@ -224,6 +225,7 @@ void Host::UpdateSlaveServices(void) keys.insert("check_period"); keys.insert("servicedependencies"); keys.insert("hostdependencies"); + keys.insert("export_macros"); ExpressionList::Ptr host_exprl = boost::make_shared(); item->GetLinkedExpressionList()->ExtractFiltered(keys, host_exprl); @@ -543,55 +545,81 @@ String Host::StateToString(HostState state) } } -Dictionary::Ptr Host::CalculateDynamicMacros(void) const +bool Host::ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const { - ASSERT(!OwnsLock()); - - Dictionary::Ptr macros = boost::make_shared(); - - { - ObjectLock olock(this); - - macros->Set("HOSTNAME", GetName()); - macros->Set("HOSTDISPLAYNAME", GetDisplayName()); - macros->Set("HOSTALIAS", GetName()); + if (macro == "HOSTNAME" || macro == "HOSTALIAS") { + *result = GetName(); + return true; + } + else if (macro == "HOSTDISPLAYNAME") { + *result = GetDisplayName(); + return true; } - - Dictionary::Ptr cr; Service::Ptr hc = GetHostCheckService(); + Dictionary::Ptr hccr; if (hc) { - ObjectLock olock(hc); - ServiceState state = hc->GetState(); bool reachable = IsReachable(); - macros->Set("HOSTSTATE", CalculateState(state, reachable)); - macros->Set("HOSTSTATEID", state); - macros->Set("HOSTSTATETYPE", Service::StateTypeToString(hc->GetStateType())); - macros->Set("HOSTATTEMPT", hc->GetCurrentCheckAttempt()); - macros->Set("MAXHOSTATTEMPT", hc->GetMaxCheckAttempts()); - - macros->Set("LASTHOSTSTATE", StateToString(GetLastState())); - macros->Set("LASTHOSTSTATEID", GetLastState()); - macros->Set("LASTHOSTSTATETYPE", Service::StateTypeToString(GetLastStateType())); - macros->Set("LASTHOSTSTATECHANGE", (long)hc->GetLastStateChange()); + if (macro == "HOSTSTATE") { + *result = Convert::ToString(CalculateState(state, reachable)); + return true; + } else if (macro == "HOSTSTATEID") { + *result = Convert::ToString(state); + return true; + } else if (macro == "HOSTSTATETYPE") { + *result = Service::StateTypeToString(hc->GetStateType()); + return true; + } else if (macro == "HOSTATTEMPT") { + *result = Convert::ToString(hc->GetCurrentCheckAttempt()); + return true; + } else if (macro == "MAXHOSTATTEMPT") { + *result = Convert::ToString(hc->GetMaxCheckAttempts()); + return true; + } else if (macro == "LASTHOSTSTATE") { + *result = StateToString(GetLastState()); + return true; + } else if (macro == "LASTHOSTSTATEID") { + *result = Convert::ToString(GetLastState()); + return true; + } else if (macro == "LASTHOSTSTATETYPE") { + *result = Service::StateTypeToString(GetLastStateType()); + return true; + } else if (macro == "LASTHOSTSTATECHANGE") { + *result = Convert::ToString((long)hc->GetLastStateChange()); + return true; + } - cr = hc->GetLastCheckResult(); + hccr = hc->GetLastCheckResult(); } - if (cr) { - macros->Set("HOSTLATENCY", Service::CalculateLatency(cr)); - macros->Set("HOSTEXECUTIONTIME", Service::CalculateExecutionTime(cr)); + if (hccr) { + if (macro == "HOSTLATENCY") { + *result = Convert::ToString(Service::CalculateLatency(hccr)); + return true; + } else if (macro == "HOSTEXECUTIONTIME") { + *result = Convert::ToString(Service::CalculateExecutionTime(hccr)); + return true; + } else if (macro == "HOSTOUTPUT") { + *result = hccr->Get("output"); + return true; + } else if (macro == "HOSTPERFDATA") { + *result = hccr->Get("performance_data_raw"); + return true; + } else if (macro == "LASTHOSTCHECK") { + *result = Convert::ToString((long)hccr->Get("schedule_start")); + return true; + } + } - macros->Set("HOSTOUTPUT", cr->Get("output")); - macros->Set("HOSTPERFDATA", cr->Get("performance_data_raw")); + Dictionary::Ptr macros = GetMacros(); - macros->Set("LASTHOSTCHECK", (long)cr->Get("schedule_start")); + if (macros && macros->Contains(macro)) { + *result = macros->Get(macro); + return true; } - macros->Seal(); - - return macros; + return false; } diff --git a/lib/icinga/host.h b/lib/icinga/host.h index 98c527169..2f40b8dac 100644 --- a/lib/icinga/host.h +++ b/lib/icinga/host.h @@ -21,6 +21,7 @@ #define HOST_H #include "icinga/i2-icinga.h" +#include "icinga/macroresolver.h" #include "base/array.h" #include "base/dynamicobject.h" #include "base/dictionary.h" @@ -72,7 +73,7 @@ enum StateType * * @ingroup icinga */ -class I2_ICINGA_API Host : public DynamicObject +class I2_ICINGA_API Host : public DynamicObject, public MacroResolver { public: typedef shared_ptr Ptr; @@ -91,8 +92,6 @@ public: Array::Ptr GetServiceDependencies(void) const; String GetHostCheck(void) const; - Dictionary::Ptr CalculateDynamicMacros(void) const; - shared_ptr GetHostCheckService(void) const; std::set GetParentHosts(void) const; std::set > GetParentServices(void) const; @@ -114,6 +113,8 @@ public: static String StateToString(HostState state); + virtual bool ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const; + protected: virtual void OnRegistrationCompleted(void); virtual void OnAttributeChanged(const String& name); diff --git a/lib/icinga/icinga-type.conf b/lib/icinga/icinga-type.conf index 1c89e8516..72937e2e9 100644 --- a/lib/icinga/icinga-type.conf +++ b/lib/icinga/icinga-type.conf @@ -50,6 +50,8 @@ type Host { %attribute string "*" }, + %attribute array "export_macros", + %attribute string "check_period", %attribute number "check_interval", %attribute number "retry_interval", @@ -74,7 +76,7 @@ type Host { %require "service", %attribute string "service" } - } + }, } }, @@ -88,6 +90,8 @@ type Host { %attribute string "*" }, + %attribute array "export_macros", + %attribute array "users" { %attribute string "*" }, @@ -109,6 +113,9 @@ type Host { %attribute dictionary "macros" { %attribute string "*" }, + + %attribute array "export_macros", + %attribute array "servicegroups" { %attribute string "*" }, @@ -143,6 +150,9 @@ type Service { %attribute dictionary "macros" { %attribute string "*" }, + + %attribute array "export_macros", + %attribute array "check_command" { %attribute string "*" }, @@ -190,6 +200,8 @@ type Service { %attribute string "*" }, + %attribute array "export_macros" + %attribute array "users" { %attribute string "*" }, @@ -221,6 +233,8 @@ type Notification { %attribute string "*" }, + %attribute array "export_macros" + %attribute array "users" { %attribute string "*" }, diff --git a/lib/icinga/icinga.vcxproj b/lib/icinga/icinga.vcxproj index f7d2f88bc..08137ebb2 100644 --- a/lib/icinga/icinga.vcxproj +++ b/lib/icinga/icinga.vcxproj @@ -33,6 +33,7 @@ + @@ -58,6 +59,7 @@ + @@ -240,4 +242,4 @@ - \ No newline at end of file + diff --git a/lib/icinga/icingaapplication.cpp b/lib/icinga/icingaapplication.cpp index 14381b511..9e5f09660 100644 --- a/lib/icinga/icingaapplication.cpp +++ b/lib/icinga/icingaapplication.cpp @@ -22,6 +22,7 @@ #include "base/dynamictype.h" #include "base/logger_fwd.h" #include "base/objectlock.h" +#include "base/convert.h" #include "base/timer.h" #include @@ -184,17 +185,33 @@ shared_ptr IcingaApplication::GetSSLContext(void) const return m_SSLContext; } -Dictionary::Ptr IcingaApplication::CalculateDynamicMacros(void) +bool IcingaApplication::ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const { - Dictionary::Ptr macros = boost::make_shared(); - double now = Utility::GetTime(); - macros->Set("TIMET", (long)now); - macros->Set("LONGDATETIME", Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", now)); - macros->Set("SHORTDATETIME", Utility::FormatDateTime("%Y-%m-%d %H:%M:%S", now)); - macros->Set("DATE", Utility::FormatDateTime("%Y-%m-%d", now)); - macros->Set("TIME", Utility::FormatDateTime("%H:%M:%S %z", now)); + if (macro == "TIMET") { + *result = Convert::ToString((long)now); + return true; + } else if (macro == "LONGDATETIME") { + *result = Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", now); + return true; + } else if (macro == "SHORTDATETIME") { + *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 macros = GetMacros(); + + if (macros && macros->Contains(macro)) { + *result = macros->Get(macro); + return true; + } - return macros; + return false; } diff --git a/lib/icinga/icingaapplication.h b/lib/icinga/icingaapplication.h index 9e6a5610a..1f8d547af 100644 --- a/lib/icinga/icingaapplication.h +++ b/lib/icinga/icingaapplication.h @@ -21,6 +21,7 @@ #define ICINGAAPPLICATION_H #include "icinga/i2-icinga.h" +#include "icinga/macroresolver.h" #include "base/application.h" #include "base/tlsutility.h" @@ -32,7 +33,7 @@ namespace icinga * * @ingroup icinga */ -class I2_ICINGA_API IcingaApplication : public Application +class I2_ICINGA_API IcingaApplication : public Application, public MacroResolver { public: typedef shared_ptr Ptr; @@ -55,7 +56,7 @@ public: double GetStartTime(void) const; - static Dictionary::Ptr CalculateDynamicMacros(void); + virtual bool ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const; private: Attribute m_CertPath; diff --git a/lib/icinga/macroprocessor.cpp b/lib/icinga/macroprocessor.cpp index 104860376..761e1a93c 100644 --- a/lib/icinga/macroprocessor.cpp +++ b/lib/icinga/macroprocessor.cpp @@ -18,24 +18,24 @@ ******************************************************************************/ #include "icinga/macroprocessor.h" +#include "icinga/macroresolver.h" #include "base/utility.h" #include "base/array.h" #include "base/objectlock.h" +#include "base/logger_fwd.h" #include #include #include using namespace icinga; -Value MacroProcessor::ResolveMacros(const Value& cmd, const Dictionary::Ptr& macros, - const MacroProcessor::EscapeCallback& escapeFn) +Value MacroProcessor::ResolveMacros(const Value& cmd, const std::vector& resolvers, + const Dictionary::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn) { Value result; - ASSERT(macros->IsSealed()); - if (cmd.IsScalar()) { - result = InternalResolveMacros(cmd, macros, escapeFn); + result = InternalResolveMacros(cmd, resolvers, cr, escapeFn); } else if (cmd.IsObjectType()) { Array::Ptr resultArr = boost::make_shared(); Array::Ptr arr = cmd; @@ -44,7 +44,7 @@ Value MacroProcessor::ResolveMacros(const Value& cmd, const Dictionary::Ptr& mac BOOST_FOREACH(const Value& arg, arr) { /* Note: don't escape macros here. */ - resultArr->Add(InternalResolveMacros(arg, macros, EscapeCallback())); + resultArr->Add(InternalResolveMacros(arg, resolvers, cr, EscapeCallback())); } result = resultArr; @@ -55,8 +55,20 @@ Value MacroProcessor::ResolveMacros(const Value& cmd, const Dictionary::Ptr& mac return result; } -String MacroProcessor::InternalResolveMacros(const String& str, const Dictionary::Ptr& macros, - const MacroProcessor::EscapeCallback& escapeFn) +bool MacroProcessor::ResolveMacro(const String& macro, const std::vector& resolvers, + const Dictionary::Ptr& cr, String *result) +{ + BOOST_FOREACH(const MacroResolver::Ptr& resolver, resolvers) { + if (resolver->ResolveMacro(macro, cr, result)) + return true; + } + + return false; +} + + +String MacroProcessor::InternalResolveMacros(const String& str, const std::vector& resolvers, + const Dictionary::Ptr& cr, const MacroProcessor::EscapeCallback& escapeFn) { size_t offset, pos_first, pos_second; offset = 0; @@ -70,38 +82,21 @@ String MacroProcessor::InternalResolveMacros(const String& str, const Dictionary String name = result.SubStr(pos_first + 1, pos_second - pos_first - 1); - if (!macros || !macros->Contains(name)) - BOOST_THROW_EXCEPTION(std::runtime_error("Macro '" + name + "' is not defined.")); - - String value = macros->Get(name); - result.Replace(pos_first, pos_second - pos_first + 1, value); - offset = pos_first + value.GetLength(); - } - - return result; -} - -Dictionary::Ptr MacroProcessor::MergeMacroDicts(const std::vector& dicts) -{ - Dictionary::Ptr result = boost::make_shared(); + String resolved_macro; + bool found = ResolveMacro(name, resolvers, cr, &resolved_macro); - BOOST_REVERSE_FOREACH(const Dictionary::Ptr& dict, dicts) { - if (!dict) - continue; - - ObjectLock olock(dict); + /* $$ is an escape sequence for $. */ + if (name.IsEmpty()) { + resolved_macro = "$"; + found = true; + } - String key; - Value value; - BOOST_FOREACH(boost::tie(key, value), dict) { - if (!value.IsScalar()) - continue; + if (!found) + Log(LogWarning, "icinga", "Macro '" + name + "' is not defined."); - result->Set(key, value); - } + result.Replace(pos_first, pos_second - pos_first + 1, resolved_macro); + offset = pos_first + resolved_macro.GetLength(); } - result->Seal(); - return result; } diff --git a/lib/icinga/macroprocessor.h b/lib/icinga/macroprocessor.h index 7860c8f93..f6cea05fd 100644 --- a/lib/icinga/macroprocessor.h +++ b/lib/icinga/macroprocessor.h @@ -21,6 +21,7 @@ #define MACROPROCESSOR_H #include "icinga/i2-icinga.h" +#include "icinga/macroresolver.h" #include "base/dictionary.h" #include #include @@ -38,15 +39,17 @@ class I2_ICINGA_API MacroProcessor public: typedef boost::function EscapeCallback; - static Value ResolveMacros(const Value& str, const Dictionary::Ptr& macros, - const EscapeCallback& escapeFn = EscapeCallback()); - static Dictionary::Ptr MergeMacroDicts(const std::vector& macroDicts); + static Value ResolveMacros(const Value& str, const std::vector& resolvers, + const Dictionary::Ptr& cr, const EscapeCallback& escapeFn = EscapeCallback()); + static bool ResolveMacro(const String& macro, const std::vector& resolvers, + const Dictionary::Ptr& cr, String *result); private: MacroProcessor(void); static String InternalResolveMacros(const String& str, - const Dictionary::Ptr& macros, const EscapeCallback& escapeFn); + const std::vector& resolvers, const Dictionary::Ptr& cr, + const EscapeCallback& escapeFn); }; } diff --git a/lib/icinga/macroresolver.cpp b/lib/icinga/macroresolver.cpp new file mode 100644 index 000000000..720d2c94f --- /dev/null +++ b/lib/icinga/macroresolver.cpp @@ -0,0 +1,42 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 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/macroresolver.h" +#include + +using namespace icinga; + +StaticMacroResolver::StaticMacroResolver(void) + : m_Macros(boost::make_shared()) +{ } + +void StaticMacroResolver::Add(const String& macro, const String& value) +{ + m_Macros->Set(macro, value); +} + +bool StaticMacroResolver::ResolveMacro(const String& macro, const Dictionary::Ptr& cr, 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 new file mode 100644 index 000000000..5944c1422 --- /dev/null +++ b/lib/icinga/macroresolver.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * Icinga 2 * + * Copyright (C) 2012 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. * + ******************************************************************************/ + +#ifndef MACRORESOLVER_H +#define MACRORESOLVER_H + +#include "icinga/i2-icinga.h" +#include "base/dictionary.h" +#include "base/qstring.h" + +namespace icinga +{ + +/** + * Resolves macros. + * + * @ingroup icinga + */ +class I2_ICINGA_API MacroResolver +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + virtual bool ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const = 0; +}; + +class I2_ICINGA_API StaticMacroResolver : public Object, public MacroResolver +{ +public: + typedef shared_ptr Ptr; + typedef weak_ptr WeakPtr; + + StaticMacroResolver(void); + + void Add(const String& macro, const String& value); + + virtual bool ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const; + +private: + Dictionary::Ptr m_Macros; +}; + +} + +#endif /* MACRORESOLVER_H */ diff --git a/lib/icinga/notification.cpp b/lib/icinga/notification.cpp index c3543383f..a90ddb569 100644 --- a/lib/icinga/notification.cpp +++ b/lib/icinga/notification.cpp @@ -81,6 +81,11 @@ Dictionary::Ptr Notification::GetMacros(void) const return m_Macros; } +Array::Ptr Notification::GetExportMacros(void) const +{ + return m_ExportMacros; +} + std::set Notification::GetUsers(void) const { std::set result; @@ -214,8 +219,6 @@ void Notification::BeginExecuteNotification(NotificationType type, const Diction SetLastNotification(Utility::GetTime()); } - Dictionary::Ptr macros = cr->Get("macros"); - std::set allUsers; std::set users = GetUsers(); @@ -228,12 +231,11 @@ void Notification::BeginExecuteNotification(NotificationType type, const Diction BOOST_FOREACH(const User::Ptr& user, allUsers) { Log(LogDebug, "icinga", "Sending notification for user " + user->GetName()); - BeginExecuteNotificationHelper(macros, type, user, ignore_timeperiod); + BeginExecuteNotificationHelper(type, user, cr, ignore_timeperiod); } } -void Notification::BeginExecuteNotificationHelper(const Dictionary::Ptr& notificationMacros, - NotificationType type, const User::Ptr& user, bool ignore_timeperiod) +void Notification::BeginExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const Dictionary::Ptr& cr, bool ignore_timeperiod) { ASSERT(!OwnsLock()); @@ -247,20 +249,12 @@ void Notification::BeginExecuteNotificationHelper(const Dictionary::Ptr& notific } } - std::vector macroDicts; - - macroDicts.push_back(user->GetMacros()); - macroDicts.push_back(user->CalculateDynamicMacros()); - - macroDicts.push_back(notificationMacros); - - Dictionary::Ptr macros = MacroProcessor::MergeMacroDicts(macroDicts); - Notification::Ptr self = GetSelf(); std::vector arguments; arguments.push_back(self); - arguments.push_back(macros); + arguments.push_back(user); + arguments.push_back(cr); arguments.push_back(type); ScriptTask::Ptr task = MakeMethodTask("notify", arguments); @@ -312,3 +306,15 @@ void Notification::OnAttributeChanged(const String& name) if (name == "host_name" || name == "service") Service::InvalidateNotificationsCache(); } + +bool Notification::ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const +{ + Dictionary::Ptr macros = GetMacros(); + + if (macros && macros->Contains(macro)) { + *result = macros->Get(macro); + return true; + } + + return false; +} diff --git a/lib/icinga/notification.h b/lib/icinga/notification.h index d9bf03e7d..1f6ed5fe0 100644 --- a/lib/icinga/notification.h +++ b/lib/icinga/notification.h @@ -52,7 +52,7 @@ class Service; * * @ingroup icinga */ -class I2_ICINGA_API Notification : public DynamicObject +class I2_ICINGA_API Notification : public DynamicObject, public MacroResolver { public: typedef shared_ptr Ptr; @@ -68,6 +68,7 @@ public: double GetNotificationInterval(void) const; TimePeriod::Ptr GetNotificationPeriod(void) const; Dictionary::Ptr GetMacros(void) const; + Array::Ptr GetExportMacros(void) const; std::set GetUsers(void) const; std::set GetGroups(void) const; @@ -81,6 +82,8 @@ public: static String NotificationTypeToString(NotificationType type); + virtual bool ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const; + protected: void OnAttributeChanged(const String& name); @@ -91,6 +94,7 @@ private: Attribute m_LastNotification; Attribute m_NextNotification; Attribute m_Macros; + Attribute m_ExportMacros; Attribute m_Users; Attribute m_Groups; Attribute m_HostName; @@ -100,8 +104,7 @@ private: void NotificationCompletedHandler(const ScriptTask::Ptr& task); - void BeginExecuteNotificationHelper(const Dictionary::Ptr& notificationMacros, - NotificationType type, const User::Ptr& user, bool ignore_timeperiod); + void BeginExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const Dictionary::Ptr& cr, bool ignore_timeperiod); }; } diff --git a/lib/icinga/perfdatawriter.cpp b/lib/icinga/perfdatawriter.cpp index 14c51ff58..3e0be141e 100644 --- a/lib/icinga/perfdatawriter.cpp +++ b/lib/icinga/perfdatawriter.cpp @@ -21,6 +21,7 @@ #include "icinga/checkresultmessage.h" #include "icinga/service.h" #include "icinga/macroprocessor.h" +#include "icinga/icingaapplication.h" #include "base/dynamictype.h" #include "base/objectlock.h" #include "base/logger_fwd.h" @@ -117,8 +118,12 @@ void PerfdataWriter::CheckResultRequestHandler(const RequestMessage& request) if (!cr) return; - Dictionary::Ptr macros = cr->Get("macros"); - String line = MacroProcessor::ResolveMacros(GetFormatTemplate(), macros); + std::vector resolvers; + resolvers.push_back(service); + resolvers.push_back(service->GetHost()); + resolvers.push_back(IcingaApplication::GetInstance()); + + String line = MacroProcessor::ResolveMacros(GetFormatTemplate(), resolvers, cr); ObjectLock olock(this); if (!m_OutputFile.good()) diff --git a/lib/icinga/pluginchecktask.cpp b/lib/icinga/pluginchecktask.cpp index 8cd2f8a03..328bc553e 100644 --- a/lib/icinga/pluginchecktask.cpp +++ b/lib/icinga/pluginchecktask.cpp @@ -19,7 +19,9 @@ #include "icinga/pluginchecktask.h" #include "icinga/macroprocessor.h" +#include "icinga/icingaapplication.h" #include "base/dynamictype.h" +#include "base/logger_fwd.h" #include #include #include @@ -38,16 +40,35 @@ void PluginCheckTask::ScriptFunc(const ScriptTask::Ptr& task, const std::vector< if (arguments.size() < 1) BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Service must be specified.")); - if (arguments.size() < 2) - BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Macros must be specified.")); - Service::Ptr service = arguments[0]; - Dictionary::Ptr macros = arguments[1]; Value raw_command = service->GetCheckCommand(); - Value command = MacroProcessor::ResolveMacros(raw_command, macros, Utility::EscapeShellCmd); - Process::Ptr process = boost::make_shared(Process::SplitCommand(command), macros); + std::vector resolvers; + resolvers.push_back(service); + resolvers.push_back(service->GetHost()); + resolvers.push_back(IcingaApplication::GetInstance()); + + Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, Dictionary::Ptr(), Utility::EscapeShellCmd); + + Dictionary::Ptr envMacros = boost::make_shared(); + + Array::Ptr export_macros = service->GetExportMacros(); + + if (export_macros) { + BOOST_FOREACH(const String& macro, export_macros) { + String value; + + if (!MacroProcessor::ResolveMacro(macro, resolvers, Dictionary::Ptr(), &value)) { + Log(LogWarning, "icinga", "export_macros for service '" + service->GetName() + "' refers to unknown macro '" + macro + "'"); + continue; + } + + envMacros->Set(macro, value); + } + } + + Process::Ptr process = boost::make_shared(Process::SplitCommand(command), envMacros); PluginCheckTask ct(task, process, command); diff --git a/lib/icinga/pluginnotificationtask.cpp b/lib/icinga/pluginnotificationtask.cpp index 77316c4e1..0e8670096 100644 --- a/lib/icinga/pluginnotificationtask.cpp +++ b/lib/icinga/pluginnotificationtask.cpp @@ -21,9 +21,11 @@ #include "icinga/notification.h" #include "icinga/service.h" #include "icinga/macroprocessor.h" +#include "icinga/icingaapplication.h" #include "base/scriptfunction.h" #include "base/logger_fwd.h" #include +#include using namespace icinga; @@ -40,14 +42,18 @@ void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const std:: BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Notification target must be specified.")); if (arguments.size() < 2) - BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Macros must be specified.")); + BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: User must be specified.")); if (arguments.size() < 3) + BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: CheckResult must be specified.")); + + if (arguments.size() < 4) BOOST_THROW_EXCEPTION(std::invalid_argument("Missing argument: Notification type must be specified.")); Notification::Ptr notification = arguments[0]; - Dictionary::Ptr macros = arguments[1]; - NotificationType type = static_cast(static_cast(arguments[2])); + User::Ptr user = arguments[1]; + Dictionary::Ptr cr = arguments[2]; + NotificationType type = static_cast(static_cast(arguments[3])); Value raw_command = notification->GetNotificationCommand(); @@ -57,18 +63,37 @@ void PluginNotificationTask::ScriptFunc(const ScriptTask::Ptr& task, const std:: if (service) service_name = service->GetName(); - Dictionary::Ptr notificationMacros = boost::make_shared(); - notificationMacros->Set("NOTIFICATIONTYPE", Notification::NotificationTypeToString(type)); + StaticMacroResolver::Ptr notificationMacroResolver = boost::make_shared(); + notificationMacroResolver->Add("NOTIFICATIONTYPE", Notification::NotificationTypeToString(type)); + + std::vector resolvers; + resolvers.push_back(user); + resolvers.push_back(notificationMacroResolver); + resolvers.push_back(notification); + resolvers.push_back(service); + resolvers.push_back(service->GetHost()); + resolvers.push_back(IcingaApplication::GetInstance()); + + Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, cr, Utility::EscapeShellCmd); + + Dictionary::Ptr envMacros = boost::make_shared(); - std::vector macroDicts; - macroDicts.push_back(notificationMacros); - macroDicts.push_back(macros); + Array::Ptr export_macros = notification->GetExportMacros(); - Dictionary::Ptr allMacros = MacroProcessor::MergeMacroDicts(macroDicts); + if (export_macros) { + BOOST_FOREACH(const String& macro, export_macros) { + String value; - Value command = MacroProcessor::ResolveMacros(raw_command, allMacros, Utility::EscapeShellCmd); + if (!MacroProcessor::ResolveMacro(macro, resolvers, cr, &value)) { + Log(LogWarning, "icinga", "export_macros for notification '" + notification->GetName() + "' refers to unknown macro '" + macro + "'"); + continue; + } + + envMacros->Set(macro, value); + } + } - Process::Ptr process = boost::make_shared(Process::SplitCommand(command), macros); + Process::Ptr process = boost::make_shared(Process::SplitCommand(command), envMacros); PluginNotificationTask ct(task, process, service_name, command); diff --git a/lib/icinga/service-check.cpp b/lib/icinga/service-check.cpp index be53baa49..f6c04a03e 100644 --- a/lib/icinga/service-check.cpp +++ b/lib/icinga/service-check.cpp @@ -429,9 +429,6 @@ void Service::ProcessCheckResult(const Dictionary::Ptr& cr) cr->Set("vars_after", vars_after); - /* Update macros - these are used by event handlers and notifications. */ - cr->Set("macros", CalculateAllMacros(cr)); - cr->Seal(); olock.Lock(); @@ -556,14 +553,10 @@ void Service::BeginExecuteCheck(const boost::function& callback) checkInfo->Set("schedule_start", GetNextCheck()); checkInfo->Set("execution_start", Utility::GetTime()); - Dictionary::Ptr macros = CalculateAllMacros(); - checkInfo->Set("macros", macros); - Service::Ptr self = GetSelf(); std::vector arguments; arguments.push_back(self); - arguments.push_back(macros); ScriptTask::Ptr task = MakeMethodTask("check", arguments); diff --git a/lib/icinga/service-notification.cpp b/lib/icinga/service-notification.cpp index 219623f08..65983b164 100644 --- a/lib/icinga/service-notification.cpp +++ b/lib/icinga/service-notification.cpp @@ -243,6 +243,7 @@ void Service::UpdateSlaveNotifications(void) keys.insert("groups"); keys.insert("notification_interval"); keys.insert("notification_period"); + keys.insert("export_macros"); ExpressionList::Ptr svc_exprl = boost::make_shared(); item->GetLinkedExpressionList()->ExtractFiltered(keys, svc_exprl); diff --git a/lib/icinga/service.cpp b/lib/icinga/service.cpp index 95b6dec05..13e810950 100644 --- a/lib/icinga/service.cpp +++ b/lib/icinga/service.cpp @@ -21,9 +21,10 @@ #include "icinga/servicegroup.h" #include "icinga/icingaapplication.h" #include "icinga/macroprocessor.h" +#include "config/configitembuilder.h" #include "base/dynamictype.h" #include "base/objectlock.h" -#include "config/configitembuilder.h" +#include "base/convert.h" #include #include @@ -37,6 +38,7 @@ Service::Service(const Dictionary::Ptr& serializedObject) RegisterAttribute("display_name", Attribute_Config, &m_DisplayName); RegisterAttribute("macros", Attribute_Config, &m_Macros); + RegisterAttribute("export_macros", Attribute_Config, &m_ExportMacros); RegisterAttribute("hostdependencies", Attribute_Config, &m_HostDependencies); RegisterAttribute("servicedependencies", Attribute_Config, &m_ServiceDependencies); RegisterAttribute("servicegroups", Attribute_Config, &m_ServiceGroups); @@ -136,6 +138,11 @@ Dictionary::Ptr Service::GetMacros(void) const return m_Macros; } +Array::Ptr Service::GetExportMacros(void) const +{ + return m_ExportMacros; +} + Array::Ptr Service::GetHostDependencies(void) const { return m_HostDependencies; @@ -369,68 +376,73 @@ std::set Service::GetParentServices(void) const return parents; } -Dictionary::Ptr Service::CalculateDynamicMacros(const Dictionary::Ptr& crOverride) const +bool Service::ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const { - Dictionary::Ptr macros = boost::make_shared(); - - Dictionary::Ptr cr; - - { - ObjectLock olock(this); - macros->Set("SERVICEDESC", GetShortName()); - macros->Set("SERVICEDISPLAYNAME", GetDisplayName()); - macros->Set("SERVICESTATE", StateToString(GetState())); - macros->Set("SERVICESTATEID", GetState()); - macros->Set("SERVICESTATETYPE", StateTypeToString(GetStateType())); - macros->Set("SERVICEATTEMPT", GetCurrentCheckAttempt()); - macros->Set("MAXSERVICEATTEMPT", GetMaxCheckAttempts()); - macros->Set("SERVICECHECKCOMMAND", "check_i2"); - macros->Set("LASTSERVICESTATE", StateToString(GetLastState())); - macros->Set("LASTSERVICESTATEID", GetLastState()); - macros->Set("LASTSERVICESTATETYPE", StateTypeToString(GetLastStateType())); - macros->Set("LASTSERVICESTATECHANGE", (long)GetLastStateChange()); - - cr = GetLastCheckResult(); + if (macro == "SERVICEDESC") { + *result = GetShortName(); + return true; + } else if (macro == "SERVICEDISPLAYNAME") { + *result = GetDisplayName(); + return true; + } else if (macro == "SERVICECHECKCOMMAND") { + *result = "check_i2"; + return true; } - if (crOverride) - cr = crOverride; + if (macro == "SERVICESTATE") { + *result = StateToString(GetState()); + return true; + } else if (macro == "SERVICESTATEID") { + *result = Convert::ToString(GetState()); + return true; + } else if (macro == "SERVICESTATETYPE") { + *result = StateTypeToString(GetStateType()); + return true; + } else if (macro == "SERVICEATTEMPT") { + *result = Convert::ToString(GetCurrentCheckAttempt()); + return true; + } else if (macro == "MAXSERVICEATTEMPT") { + *result = Convert::ToString(GetMaxCheckAttempts()); + return true; + } else if (macro == "LASTSERVICESTATE") { + *result = StateToString(GetLastState()); + return true; + } else if (macro == "LASTSERVICESTATEID") { + *result = Convert::ToString(GetLastState()); + return true; + } else if (macro == "LASTSERVICESTATETYPE") { + *result = StateTypeToString(GetLastStateType()); + return true; + } else if (macro == "LASTSERVICESTATECHANGE") { + *result = Convert::ToString((long)GetLastStateChange()); + return true; + } if (cr) { - ASSERT(crOverride || cr->IsSealed()); - - macros->Set("SERVICELATENCY", Service::CalculateLatency(cr)); - macros->Set("SERVICEEXECUTIONTIME", Service::CalculateExecutionTime(cr)); - - macros->Set("SERVICEOUTPUT", cr->Get("output")); - macros->Set("SERVICEPERFDATA", cr->Get("performance_data_raw")); - - macros->Set("LASTSERVICECHECK", (long)cr->Get("execution_end")); + if (macro == "SERVICELATENCY") { + *result = Convert::ToString(Service::CalculateLatency(cr)); + return true; + } else if (macro == "SERVICEEXECUTIONTIME") { + *result = Convert::ToString(Service::CalculateExecutionTime(cr)); + return true; + } else if (macro == "SERVICEOUTPUT") { + *result = cr->Get("output"); + return true; + } else if (macro == "SERVICEPERFDATA") { + *result = cr->Get("performance_data_raw"); + return true; + } else if (macro == "LASTSERVICECHECK") { + *result = Convert::ToString((long)cr->Get("execution_end")); + return true; + } } - macros->Seal(); - - return macros; -} - -Dictionary::Ptr Service::CalculateAllMacros(const Dictionary::Ptr& crOverride) const -{ - std::vector macroDicts; - macroDicts.push_back(GetMacros()); + Dictionary::Ptr macros = GetMacros(); - Host::Ptr host = GetHost(); - - macroDicts.push_back(CalculateDynamicMacros(crOverride)); - - if (host) { - macroDicts.push_back(host->GetMacros()); - macroDicts.push_back(host->CalculateDynamicMacros()); + if (macros && macros->Contains(macro)) { + *result = macros->Get(macro); + return true; } - IcingaApplication::Ptr app = IcingaApplication::GetInstance(); - macroDicts.push_back(app->GetMacros()); - - macroDicts.push_back(IcingaApplication::CalculateDynamicMacros()); - - return MacroProcessor::MergeMacroDicts(macroDicts); + return false; } diff --git a/lib/icinga/service.h b/lib/icinga/service.h index d71cc84d1..e8705377e 100644 --- a/lib/icinga/service.h +++ b/lib/icinga/service.h @@ -21,6 +21,7 @@ #define SERVICE_H #include "icinga/i2-icinga.h" +#include "icinga/macroresolver.h" #include "icinga/host.h" #include "icinga/timeperiod.h" #include "icinga/notification.h" @@ -63,7 +64,7 @@ class CheckResultMessage; * * @ingroup icinga */ -class I2_ICINGA_API Service : public DynamicObject +class I2_ICINGA_API Service : public DynamicObject, public MacroResolver { public: typedef shared_ptr Ptr; @@ -83,15 +84,13 @@ public: String GetDisplayName(void) const; Host::Ptr GetHost(void) const; Dictionary::Ptr GetMacros(void) const; + Array::Ptr GetExportMacros(void) const; Array::Ptr GetHostDependencies(void) const; Array::Ptr GetServiceDependencies(void) const; Array::Ptr GetGroups(void) const; String GetHostName(void) const; String GetShortName(void) const; - Dictionary::Ptr CalculateDynamicMacros(const Dictionary::Ptr& crOverride = Dictionary::Ptr()) const; - Dictionary::Ptr CalculateAllMacros(const Dictionary::Ptr& crOverride = Dictionary::Ptr()) const; - std::set GetParentHosts(void) const; std::set GetParentServices(void) const; @@ -179,6 +178,8 @@ public: static boost::signals2::signal OnCheckerChanged; static boost::signals2::signal OnNextCheckChanged; + virtual bool ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const; + /* Downtimes */ static int GetNextDowntimeID(void); @@ -249,6 +250,7 @@ private: Attribute m_DisplayName; Attribute m_Macros; + Attribute m_ExportMacros; Attribute m_HostDependencies; Attribute m_ServiceDependencies; Attribute m_ServiceGroups; diff --git a/lib/icinga/user.cpp b/lib/icinga/user.cpp index 2f679084b..a9a8b4495 100644 --- a/lib/icinga/user.cpp +++ b/lib/icinga/user.cpp @@ -78,14 +78,22 @@ TimePeriod::Ptr User::GetNotificationPeriod(void) const return TimePeriod::GetByName(m_NotificationPeriod); } -Dictionary::Ptr User::CalculateDynamicMacros(void) const +bool User::ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const { - Dictionary::Ptr macros = boost::make_shared(); - - macros->Set("CONTACTNAME", GetName()); - macros->Set("CONTACTALIAS", GetName()); - - macros->Seal(); - - return macros; + if (macro == "CONTACTNAME") { + *result = GetName(); + return true; + } else if (macro == "CONTACTALIAS") { + *result = GetName(); + return true; + } else { + Dictionary::Ptr macros = GetMacros(); + + if (macros && macros->Contains(macro)) { + *result = macros->Get(macro); + return true; + } + + return false; + } } diff --git a/lib/icinga/user.h b/lib/icinga/user.h index 53d33d243..f8f89a649 100644 --- a/lib/icinga/user.h +++ b/lib/icinga/user.h @@ -21,6 +21,7 @@ #define USER_H #include "icinga/i2-icinga.h" +#include "icinga/macroresolver.h" #include "icinga/timeperiod.h" #include "base/dynamicobject.h" #include "base/array.h" @@ -33,7 +34,7 @@ namespace icinga * * @ingroup icinga */ -class I2_ICINGA_API User : public DynamicObject +class I2_ICINGA_API User : public DynamicObject, public MacroResolver { public: typedef shared_ptr Ptr; @@ -49,7 +50,8 @@ public: TimePeriod::Ptr GetNotificationPeriod(void) const; Dictionary::Ptr GetMacros(void) const; - Dictionary::Ptr CalculateDynamicMacros(void) const; + + virtual bool ResolveMacro(const String& macro, const Dictionary::Ptr& cr, String *result) const; protected: virtual void OnAttributeChanged(const String& name); -- 2.40.0