Refactor the macro resolver

Fixes #3884
This commit is contained in:
Gunnar Beutner 2013-03-22 14:40:55 +01:00
parent 97fee26289
commit 6d69d6c639
22 changed files with 450 additions and 205 deletions

View File

@ -30,6 +30,8 @@ libicinga_la_SOURCES = \
icingaapplication.h \
macroprocessor.cpp \
macroprocessor.h \
macroresolver.cpp \
macroresolver.h \
notification.cpp \
notification.h \
notificationrequestmessage.cpp \

View File

@ -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 <boost/tuple/tuple.hpp>
@ -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<ExpressionList>();
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<Dictionary>();
{
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());
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;
}
macros->Set("LASTHOSTSTATE", StateToString(GetLastState()));
macros->Set("LASTHOSTSTATEID", GetLastState());
macros->Set("LASTHOSTSTATETYPE", Service::StateTypeToString(GetLastStateType()));
macros->Set("LASTHOSTSTATECHANGE", (long)hc->GetLastStateChange());
cr = hc->GetLastCheckResult();
hccr = hc->GetLastCheckResult();
}
if (cr) {
macros->Set("HOSTLATENCY", Service::CalculateLatency(cr));
macros->Set("HOSTEXECUTIONTIME", Service::CalculateExecutionTime(cr));
macros->Set("HOSTOUTPUT", cr->Get("output"));
macros->Set("HOSTPERFDATA", cr->Get("performance_data_raw"));
macros->Set("LASTHOSTCHECK", (long)cr->Get("schedule_start"));
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->Seal();
Dictionary::Ptr macros = GetMacros();
return macros;
if (macros && macros->Contains(macro)) {
*result = macros->Get(macro);
return true;
}
return false;
}

View File

@ -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<Host> Ptr;
@ -91,8 +92,6 @@ public:
Array::Ptr GetServiceDependencies(void) const;
String GetHostCheck(void) const;
Dictionary::Ptr CalculateDynamicMacros(void) const;
shared_ptr<Service> GetHostCheckService(void) const;
std::set<Host::Ptr> GetParentHosts(void) const;
std::set<shared_ptr<Service> > 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);

View File

@ -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 "*"
},

View File

@ -33,6 +33,7 @@
</ClCompile>
<ClCompile Include="icingaapplication.cpp" />
<ClCompile Include="macroprocessor.cpp" />
<ClCompile Include="macroresolver.cpp" />
<ClCompile Include="notification.cpp" />
<ClCompile Include="notificationrequestmessage.cpp" />
<ClCompile Include="pluginchecktask.cpp" />
@ -58,6 +59,7 @@
<ClInclude Include="i2-icinga.h" />
<ClInclude Include="icingaapplication.h" />
<ClInclude Include="macroprocessor.h" />
<ClInclude Include="macroresolver.h" />
<ClInclude Include="notification.h" />
<ClInclude Include="notificationrequestmessage.h" />
<ClInclude Include="pluginchecktask.h" />
@ -240,4 +242,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -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 <boost/smart_ptr/make_shared.hpp>
@ -184,17 +185,33 @@ shared_ptr<SSL_CTX> 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<Dictionary>();
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;
}
return macros;
Dictionary::Ptr macros = GetMacros();
if (macros && macros->Contains(macro)) {
*result = macros->Get(macro);
return true;
}
return false;
}

View File

@ -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<IcingaApplication> 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<String> m_CertPath;

View File

@ -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 <boost/tuple/tuple.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/foreach.hpp>
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<MacroResolver::Ptr>& 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>()) {
Array::Ptr resultArr = boost::make_shared<Array>();
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<MacroResolver::Ptr>& 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<MacroResolver::Ptr>& 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 resolved_macro;
bool found = ResolveMacro(name, resolvers, cr, &resolved_macro);
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<Dictionary::Ptr>& dicts)
{
Dictionary::Ptr result = boost::make_shared<Dictionary>();
BOOST_REVERSE_FOREACH(const Dictionary::Ptr& dict, dicts) {
if (!dict)
continue;
ObjectLock olock(dict);
String key;
Value value;
BOOST_FOREACH(boost::tie(key, value), dict) {
if (!value.IsScalar())
continue;
result->Set(key, value);
/* $$ is an escape sequence for $. */
if (name.IsEmpty()) {
resolved_macro = "$";
found = true;
}
}
result->Seal();
if (!found)
Log(LogWarning, "icinga", "Macro '" + name + "' is not defined.");
result.Replace(pos_first, pos_second - pos_first + 1, resolved_macro);
offset = pos_first + resolved_macro.GetLength();
}
return result;
}

View File

@ -21,6 +21,7 @@
#define MACROPROCESSOR_H
#include "icinga/i2-icinga.h"
#include "icinga/macroresolver.h"
#include "base/dictionary.h"
#include <boost/function.hpp>
#include <vector>
@ -38,15 +39,17 @@ class I2_ICINGA_API MacroProcessor
public:
typedef boost::function<String (const String&)> EscapeCallback;
static Value ResolveMacros(const Value& str, const Dictionary::Ptr& macros,
const EscapeCallback& escapeFn = EscapeCallback());
static Dictionary::Ptr MergeMacroDicts(const std::vector<Dictionary::Ptr>& macroDicts);
static Value ResolveMacros(const Value& str, const std::vector<MacroResolver::Ptr>& resolvers,
const Dictionary::Ptr& cr, const EscapeCallback& escapeFn = EscapeCallback());
static bool ResolveMacro(const String& macro, const std::vector<MacroResolver::Ptr>& 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<MacroResolver::Ptr>& resolvers, const Dictionary::Ptr& cr,
const EscapeCallback& escapeFn);
};
}

View File

@ -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 <boost/smart_ptr/make_shared.hpp>
using namespace icinga;
StaticMacroResolver::StaticMacroResolver(void)
: m_Macros(boost::make_shared<Dictionary>())
{ }
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;
}

View File

@ -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<MacroResolver> Ptr;
typedef weak_ptr<MacroResolver> 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<StaticMacroResolver> Ptr;
typedef weak_ptr<StaticMacroResolver> 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 */

View File

@ -81,6 +81,11 @@ Dictionary::Ptr Notification::GetMacros(void) const
return m_Macros;
}
Array::Ptr Notification::GetExportMacros(void) const
{
return m_ExportMacros;
}
std::set<User::Ptr> Notification::GetUsers(void) const
{
std::set<User::Ptr> result;
@ -214,8 +219,6 @@ void Notification::BeginExecuteNotification(NotificationType type, const Diction
SetLastNotification(Utility::GetTime());
}
Dictionary::Ptr macros = cr->Get("macros");
std::set<User::Ptr> allUsers;
std::set<User::Ptr> 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<Dictionary::Ptr> 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<Value> 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;
}

View File

@ -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<Notification> 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<User::Ptr> GetUsers(void) const;
std::set<UserGroup::Ptr> 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<double> m_LastNotification;
Attribute<double> m_NextNotification;
Attribute<Dictionary::Ptr> m_Macros;
Attribute<Array::Ptr> m_ExportMacros;
Attribute<Array::Ptr> m_Users;
Attribute<Array::Ptr> m_Groups;
Attribute<String> 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);
};
}

View File

@ -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<MacroResolver::Ptr> 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())

View File

@ -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 <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/smart_ptr/make_shared.hpp>
@ -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>(Process::SplitCommand(command), macros);
std::vector<MacroResolver::Ptr> 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<Dictionary>();
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>(Process::SplitCommand(command), envMacros);
PluginCheckTask ct(task, process, command);

View File

@ -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 <boost/smart_ptr/make_shared.hpp>
#include <boost/foreach.hpp>
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<NotificationType>(static_cast<int>(arguments[2]));
User::Ptr user = arguments[1];
Dictionary::Ptr cr = arguments[2];
NotificationType type = static_cast<NotificationType>(static_cast<int>(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<Dictionary>();
notificationMacros->Set("NOTIFICATIONTYPE", Notification::NotificationTypeToString(type));
StaticMacroResolver::Ptr notificationMacroResolver = boost::make_shared<StaticMacroResolver>();
notificationMacroResolver->Add("NOTIFICATIONTYPE", Notification::NotificationTypeToString(type));
std::vector<Dictionary::Ptr> macroDicts;
macroDicts.push_back(notificationMacros);
macroDicts.push_back(macros);
std::vector<MacroResolver::Ptr> 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());
Dictionary::Ptr allMacros = MacroProcessor::MergeMacroDicts(macroDicts);
Value command = MacroProcessor::ResolveMacros(raw_command, resolvers, cr, Utility::EscapeShellCmd);
Value command = MacroProcessor::ResolveMacros(raw_command, allMacros, Utility::EscapeShellCmd);
Dictionary::Ptr envMacros = boost::make_shared<Dictionary>();
Process::Ptr process = boost::make_shared<Process>(Process::SplitCommand(command), macros);
Array::Ptr export_macros = notification->GetExportMacros();
if (export_macros) {
BOOST_FOREACH(const String& macro, export_macros) {
String value;
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>(Process::SplitCommand(command), envMacros);
PluginNotificationTask ct(task, process, service_name, command);

View File

@ -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<void (void)>& 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<Value> arguments;
arguments.push_back(self);
arguments.push_back(macros);
ScriptTask::Ptr task = MakeMethodTask("check", arguments);

View File

@ -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<ExpressionList>();
item->GetLinkedExpressionList()->ExtractFiltered(keys, svc_exprl);

View File

@ -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 <boost/smart_ptr/make_shared.hpp>
#include <boost/foreach.hpp>
@ -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::Ptr> 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>();
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();
Dictionary::Ptr macros = GetMacros();
return macros;
}
Dictionary::Ptr Service::CalculateAllMacros(const Dictionary::Ptr& crOverride) const
{
std::vector<Dictionary::Ptr> macroDicts;
macroDicts.push_back(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;
}

View File

@ -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<Service> 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<Host::Ptr> GetParentHosts(void) const;
std::set<Service::Ptr> GetParentServices(void) const;
@ -179,6 +178,8 @@ public:
static boost::signals2::signal<void (const Service::Ptr&)> OnCheckerChanged;
static boost::signals2::signal<void (const Service::Ptr&)> 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<String> m_DisplayName;
Attribute<Dictionary::Ptr> m_Macros;
Attribute<Array::Ptr> m_ExportMacros;
Attribute<Array::Ptr> m_HostDependencies;
Attribute<Array::Ptr> m_ServiceDependencies;
Attribute<Array::Ptr> m_ServiceGroups;

View File

@ -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<Dictionary>();
if (macro == "CONTACTNAME") {
*result = GetName();
return true;
} else if (macro == "CONTACTALIAS") {
*result = GetName();
return true;
} else {
Dictionary::Ptr macros = GetMacros();
macros->Set("CONTACTNAME", GetName());
macros->Set("CONTACTALIAS", GetName());
if (macros && macros->Contains(macro)) {
*result = macros->Get(macro);
return true;
}
macros->Seal();
return macros;
return false;
}
}

View File

@ -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<User> 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);