Implement the 'apply template' directive.

Refs #5789
This commit is contained in:
Gunnar Beutner 2014-03-18 11:44:09 +01:00
parent 3587dbbd13
commit caad7a7973
10 changed files with 278 additions and 6 deletions

View File

@ -320,7 +320,7 @@ Simple calculations can be performed using the constant expression syntax:
check_interval = (15 * 60)
}
Valid operators include ~, +, -, * and /. The default precedence rules can be
Valid operators include ~, +, -, *, /, == and !=. The default precedence rules can be
overridden by grouping expressions using parentheses:
{
@ -339,8 +339,27 @@ Global constants may be used in constant expressions.
> **Note**
>
> Constant expressions are evaluated as soon as they're encountered in
> the configuration file.
> Constant expressions in attributes and variable definitions are evaluated as
> soon as they're encountered in the configuration file.
### <a id="apply"></a> Apply
The `apply` keyword can be used to associate a template with another group of
objects. The exact effect of this association depends on the two object types.
template Service "ping-service" {
short_name = "ping",
check_command = "ping4"
}
apply template Service "ping-service" to Host where (host == "localhost")
In this example the `where` condition is a constant expression which is
evaluated for all objects of type Host and a new service is created for each
matching host.
Depending on the object types used in the `apply` expression additional local
variables may be available for use in the `where` condition.
### <a id="comments"></a> Comments

View File

@ -27,7 +27,7 @@ mkembedconfig_target(base-type.conf base-type.cpp)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
add_library(config SHARED
aexpression.cpp avalue.cpp base-type.conf base-type.cpp
aexpression.cpp applyrule.cpp avalue.cpp base-type.conf base-type.cpp
configcompilercontext.cpp configcompiler.cpp configitembuilder.cpp
configitem.cpp ${FLEX_config_lexer_OUTPUTS} ${BISON_config_parser_OUTPUTS}
configtype.cpp expression.cpp expressionlist.cpp typerule.cpp typerulelist.cpp

72
lib/config/applyrule.cpp Normal file
View File

@ -0,0 +1,72 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-present 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 "config/applyrule.h"
using namespace icinga;
ApplyRule::RuleMap ApplyRule::m_Rules;
ApplyRule::CallbackMap ApplyRule::m_Callbacks;
ApplyRule::ApplyRule(const String& tmpl, const AExpression::Ptr& expression, const DebugInfo& di)
: m_Template(tmpl), m_Expression(expression), m_DebugInfo(di)
{ }
String ApplyRule::GetTemplate(void) const
{
return m_Template;
}
AExpression::Ptr ApplyRule::GetExpression(void) const
{
return m_Expression;
}
DebugInfo ApplyRule::GetDebugInfo(void) const
{
return m_DebugInfo;
}
void ApplyRule::AddRule(const String& sourceType, const String& tmpl, const String& targetType, const AExpression::Ptr& expression, const DebugInfo& di)
{
m_Rules[std::make_pair(sourceType, targetType)].push_back(ApplyRule(tmpl, expression, di));
}
void ApplyRule::EvaluateRules(void)
{
std::pair<TypeCombination, Callback> kv;
BOOST_FOREACH(kv, m_Callbacks) {
RuleMap::const_iterator it = m_Rules.find(kv.first);
if (it == m_Rules.end())
continue;
kv.second(it->second);
}
}
void ApplyRule::RegisterCombination(const String& sourceType, const String& targetType, const ApplyRule::Callback& callback)
{
m_Callbacks[std::make_pair(sourceType, targetType)] = callback;
}
bool ApplyRule::IsValidCombination(const String& sourceType, const String& targetType)
{
return m_Callbacks.find(std::make_pair(sourceType, targetType)) != m_Callbacks.end();
}

65
lib/config/applyrule.h Normal file
View File

@ -0,0 +1,65 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-present 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 APPLYRULE_H
#define APPLYRULE_H
#include "config/i2-config.h"
#include "config/aexpression.h"
#include "config/debuginfo.h"
#include "base/dynamictype.h"
namespace icinga
{
/**
* @ingroup config
*/
class I2_CONFIG_API ApplyRule
{
public:
typedef std::pair<String, String> TypeCombination;
typedef boost::function<void (const std::vector<ApplyRule>& rules)> Callback;
typedef std::map<TypeCombination, Callback> CallbackMap;
typedef std::map<TypeCombination, std::vector<ApplyRule> > RuleMap;
String GetTemplate(void) const;
AExpression::Ptr GetExpression(void) const;
DebugInfo GetDebugInfo(void) const;
static void AddRule(const String& sourceType, const String& tmpl, const String& targetType, const AExpression::Ptr& expression, const DebugInfo& di);
static void EvaluateRules(void);
static void RegisterCombination(const String& sourceType, const String& targetType, const ApplyRule::Callback& callback);
static bool IsValidCombination(const String& sourceType, const String& targetType);
private:
String m_Template;
AExpression::Ptr m_Expression;
DebugInfo m_DebugInfo;
static CallbackMap m_Callbacks;
static RuleMap m_Rules;
ApplyRule(const String& tmpl, const AExpression::Ptr& expression, const DebugInfo& di);
};
}
#endif /* APPLYRULE_H */

View File

@ -222,6 +222,9 @@ false { yylval->num = 0; return T_NUMBER; }
set return T_VAR;
var return T_VAR;
const return T_CONST;
apply return T_APPLY;
to return T_TO;
where return T_WHERE;
\<\< return T_SHIFT_LEFT;
\>\> return T_SHIFT_RIGHT;
[a-zA-Z_][:a-zA-Z0-9\-_]* { yylval->text = strdup(yytext); return T_IDENTIFIER; }

View File

@ -27,11 +27,13 @@
#include "config/typerule.h"
#include "config/typerulelist.h"
#include "config/aexpression.h"
#include "config/applyrule.h"
#include "base/value.h"
#include "base/utility.h"
#include "base/array.h"
#include "base/scriptvariable.h"
#include "base/exception.h"
#include "base/dynamictype.h"
#include <sstream>
#include <stack>
#include <boost/foreach.hpp>
@ -98,6 +100,9 @@ using namespace icinga;
%token T_LIBRARY "library (T_LIBRARY)"
%token T_INHERITS "inherits (T_INHERITS)"
%token T_PARTIAL "partial (T_PARTIAL)"
%token T_APPLY "apply (T_APPLY)"
%token T_TO "to (T_TO)"
%token T_WHERE "where (T_WHERE)"
%type <text> identifier
%type <array> array
%type <array> array_items
@ -158,7 +163,7 @@ statements: /* empty */
| statements statement
;
statement: object | type | include | include_recursive | library | variable
statement: object | type | include | include_recursive | library | variable | apply
;
include: T_INCLUDE value
@ -672,4 +677,18 @@ value: simplevalue
delete $1;
}
;
optional_template: /* empty */
| T_TEMPLATE
;
apply: T_APPLY optional_template identifier identifier T_TO identifier T_WHERE aterm
{
if (!ApplyRule::IsValidCombination($3, $6)) {
BOOST_THROW_EXCEPTION(std::invalid_argument("'apply' cannot be used with types '" + String($3) + "' and '" + String($6) + "'."));
}
ApplyRule::AddRule($3, $4, $6, *$8, yylloc);
delete $8;
}
%%

View File

@ -19,6 +19,7 @@
#include "config/configitem.h"
#include "config/configcompilercontext.h"
#include "config/applyrule.h"
#include "base/application.h"
#include "base/dynamictype.h"
#include "base/objectlock.h"
@ -303,6 +304,9 @@ bool ConfigItem::ActivateItems(bool validateOnly)
upq.Join();
Log(LogInformation, "config", "Evaluating 'apply' rules...");
ApplyRule::EvaluateRules();
Log(LogInformation, "config", "Validating config items (step 2)...");
BOOST_FOREACH(const ItemMap::value_type& kv, m_Items) {

View File

@ -43,7 +43,7 @@ add_library(icinga SHARED
api.cpp api.h checkcommand.cpp checkcommand.th checkresult.cpp checkresult.th
cib.cpp command.cpp command.th comment.cpp comment.th compatutility.cpp dependency.cpp dependency.th
domain.cpp domain.th downtime.cpp downtime.th eventcommand.cpp eventcommand.th
externalcommandprocessor.cpp host.cpp host.th hostgroup.cpp hostgroup.th
externalcommandprocessor.cpp host.cpp host.th host-apply.cpp hostgroup.cpp hostgroup.th
icingaapplication.cpp icingaapplication.th icingastatuswriter.cpp
icingastatuswriter.th legacytimeperiod.cpp
macroprocessor.cpp macroresolver.cpp notificationcommand.cpp notificationcommand.th

86
lib/icinga/host-apply.cpp Normal file
View File

@ -0,0 +1,86 @@
/******************************************************************************
* Icinga 2 *
* Copyright (C) 2012-present 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/host.h"
#include "config/configitembuilder.h"
#include "base/initialize.h"
#include "base/dynamictype.h"
#include "base/convert.h"
#include "base/logger_fwd.h"
#include "base/context.h"
#include <boost/foreach.hpp>
using namespace icinga;
INITIALIZE_ONCE(&Host::RegisterApplyRuleHandler);
void Host::RegisterApplyRuleHandler(void)
{
ApplyRule::RegisterCombination("Service", "Host", &Host::EvaluateApplyRules);
}
void Host::EvaluateApplyRules(const std::vector<ApplyRule>& rules)
{
BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects<Host>()) {
CONTEXT("Evaluating 'apply' rules for Host '" + host->GetName() + "'");
Dictionary::Ptr locals = make_shared<Dictionary>();
locals->Set("host", host->GetName());
BOOST_FOREACH(const ApplyRule& rule, rules) {
std::ostringstream msgbuf;
msgbuf << "Evaluating 'apply' rule (" << rule.GetDebugInfo() << ")";
CONTEXT(msgbuf.str());
Value result = rule.GetExpression()->Evaluate(locals);
try {
if (!static_cast<bool>(result))
continue;
} catch (...) {
std::ostringstream msgbuf;
msgbuf << "Apply rule (" << rule.GetDebugInfo() << ") returned invalid data type, expected bool: " + JsonSerialize(result);
Log(LogCritical, "icinga", msgbuf.str());
continue;
}
if (result.IsEmpty())
continue;
std::ostringstream namebuf;
namebuf << host->GetName() << "!apply!" << rule.GetTemplate();
String name = namebuf.str();
ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(rule.GetDebugInfo());
builder->SetType("Service");
builder->SetName(name);
builder->AddExpression("host", OperatorSet, host->GetName());
builder->AddParent(rule.GetTemplate());
ConfigItem::Ptr serviceItem = builder->Compile();
serviceItem->Register();
DynamicObject::Ptr dobj = serviceItem->Commit();
dobj->OnConfigLoaded();
Log(LogInformation, "icinga", "Rule result: " + Convert::ToString(result));
}
}
}

View File

@ -24,6 +24,7 @@
#include "icinga/host.th"
#include "icinga/macroresolver.h"
#include "icinga/checkresult.h"
#include "config/applyrule.h"
#include "base/array.h"
#include "base/dictionary.h"
@ -101,6 +102,9 @@ public:
virtual bool ResolveMacro(const String& macro, const CheckResult::Ptr& cr, String *result) const;
static void RegisterApplyRuleHandler(void);
static void EvaluateApplyRules(const std::vector<ApplyRule>& rules);
protected:
virtual void Stop(void);