]> granicus.if.org Git - icinga2/commitdiff
Implement the 'apply template' directive.
authorGunnar Beutner <gunnar.beutner@netways.de>
Tue, 18 Mar 2014 10:44:09 +0000 (11:44 +0100)
committerGunnar Beutner <gunnar.beutner@netways.de>
Wed, 19 Mar 2014 11:46:23 +0000 (12:46 +0100)
Refs #5789

doc/4.1-configuration-syntax.md
lib/config/CMakeLists.txt
lib/config/applyrule.cpp [new file with mode: 0644]
lib/config/applyrule.h [new file with mode: 0644]
lib/config/config_lexer.ll
lib/config/config_parser.yy
lib/config/configitem.cpp
lib/icinga/CMakeLists.txt
lib/icinga/host-apply.cpp [new file with mode: 0644]
lib/icinga/host.h

index 59969d2f11ed6ea24ccb2c294e35a0ce9877a454..5f71388ef8da7425eea201e54caf282f6cc38bb0 100644 (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
 
index ab0f302db0d2819dd30e247c9c71e98f95e2a2c9..6f9dbeb50b4adac021c50debbf981cb65191c385 100644 (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
diff --git a/lib/config/applyrule.cpp b/lib/config/applyrule.cpp
new file mode 100644 (file)
index 0000000..23247db
--- /dev/null
@@ -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();
+}
diff --git a/lib/config/applyrule.h b/lib/config/applyrule.h
new file mode 100644 (file)
index 0000000..e08ad98
--- /dev/null
@@ -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 */
index 22da165757b061329248a250e24abc9c5baad76f..1e6aa4d3423ece174b52dd876edfd181ae71a630 100644 (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; }
index bbb73e2cceac7990e7a1bae867887358b512c39b..cf9cd2e49aed036e27d303dd9a9c6573ac80d9b7 100644 (file)
 #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;
+       }
 %%
index 8e76357761d65a77f9c72a093cea917df7eadc57..841075dffc859a89abbe95053cf61ffab447c216 100644 (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) {
index 8dab566a858ff786104fec85064b7cda2b26606c..5ea9d54ffb63f711cae256f7eca6c40e7fbebd3f 100644 (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
diff --git a/lib/icinga/host-apply.cpp b/lib/icinga/host-apply.cpp
new file mode 100644 (file)
index 0000000..d2796cb
--- /dev/null
@@ -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));
+               }
+       }
+}
index 21c6d7f3493a02352e86ec29498467ea169e7899..c49fab286255fa10a466f11a6cd2664a186c574d 100644 (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);