From: Gunnar Beutner Date: Tue, 18 Mar 2014 10:44:09 +0000 (+0100) Subject: Implement the 'apply template' directive. X-Git-Tag: v0.0.9~74^2~7 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=caad7a797324f1bc842c5cce9b3cce155c4ea778;p=icinga2 Implement the 'apply template' directive. Refs #5789 --- diff --git a/doc/4.1-configuration-syntax.md b/doc/4.1-configuration-syntax.md index 59969d2f1..5f71388ef 100644 --- a/doc/4.1-configuration-syntax.md +++ b/doc/4.1-configuration-syntax.md @@ -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. + +### 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. ### Comments diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index ab0f302db..6f9dbeb50 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -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 diff --git a/lib/config/applyrule.cpp b/lib/config/applyrule.cpp new file mode 100644 index 000000000..23247db27 --- /dev/null +++ b/lib/config/applyrule.cpp @@ -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 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(); +} diff --git a/lib/config/applyrule.h b/lib/config/applyrule.h new file mode 100644 index 000000000..e08ad98fb --- /dev/null +++ b/lib/config/applyrule.h @@ -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 TypeCombination; + typedef boost::function& rules)> Callback; + typedef std::map CallbackMap; + typedef std::map > 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 */ diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index 22da16575..1e6aa4d34 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -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; } diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index bbb73e2cc..cf9cd2e49 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -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 #include #include @@ -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 identifier %type array %type 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; + } %% diff --git a/lib/config/configitem.cpp b/lib/config/configitem.cpp index 8e7635776..841075dff 100644 --- a/lib/config/configitem.cpp +++ b/lib/config/configitem.cpp @@ -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) { diff --git a/lib/icinga/CMakeLists.txt b/lib/icinga/CMakeLists.txt index 8dab566a8..5ea9d54ff 100644 --- a/lib/icinga/CMakeLists.txt +++ b/lib/icinga/CMakeLists.txt @@ -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 diff --git a/lib/icinga/host-apply.cpp b/lib/icinga/host-apply.cpp new file mode 100644 index 000000000..d2796cb2d --- /dev/null +++ b/lib/icinga/host-apply.cpp @@ -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 + +using namespace icinga; + +INITIALIZE_ONCE(&Host::RegisterApplyRuleHandler); + +void Host::RegisterApplyRuleHandler(void) +{ + ApplyRule::RegisterCombination("Service", "Host", &Host::EvaluateApplyRules); +} + +void Host::EvaluateApplyRules(const std::vector& rules) +{ + BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjects()) { + CONTEXT("Evaluating 'apply' rules for Host '" + host->GetName() + "'"); + + Dictionary::Ptr locals = make_shared(); + 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(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(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)); + } + } +} diff --git a/lib/icinga/host.h b/lib/icinga/host.h index 21c6d7f34..c49fab286 100644 --- a/lib/icinga/host.h +++ b/lib/icinga/host.h @@ -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& rules); + protected: virtual void Stop(void);