]> granicus.if.org Git - icinga2/commitdiff
Implement support for arrays in custom variables
authorGunnar Beutner <gunnar@beutner.name>
Sun, 2 Nov 2014 06:22:00 +0000 (07:22 +0100)
committerGunnar Beutner <gunnar@beutner.name>
Sun, 2 Nov 2014 06:27:50 +0000 (07:27 +0100)
fixes #6544

13 files changed:
lib/base/utility.cpp
lib/base/utility.hpp
lib/compat/statusdatawriter.cpp
lib/config/applyrule.cpp
lib/config/applyrule.hpp
lib/config/base-type.conf
lib/config/config_parser.yy
lib/config/expression.cpp
lib/db_ido/dbobject.cpp
lib/icinga/dependency-apply.cpp
lib/icinga/notification-apply.cpp
lib/icinga/scheduleddowntime-apply.cpp
lib/icinga/service-apply.cpp

index a834df587e03a99f926eb3be8151f77100e2f172..d96139442ac9b5266204a3ec3ac04ab021a68142 100644 (file)
 #include "base/socket.hpp"
 #include "base/utility.hpp"
 #include "base/json.hpp"
+#include "base/objectlock.hpp"
 #include <mmatch.h>
 #include <boost/lexical_cast.hpp>
 #include <boost/foreach.hpp>
 #include <boost/algorithm/string/split.hpp>
 #include <boost/algorithm/string/classification.hpp>
 #include <boost/algorithm/string/trim.hpp>
+#include <boost/algorithm/string/replace.hpp>
 #include <ios>
 #include <fstream>
 #include <iostream>
@@ -772,6 +774,35 @@ String Utility::NaturalJoin(const std::vector<String>& tokens)
        return result;
 }
 
+String Utility::Join(const Array::Ptr& tokens, char separator)
+{
+       String result;
+       bool first = true;
+
+       ObjectLock olock(tokens);
+       BOOST_FOREACH(const Value& vtoken, tokens) {
+               String token = Convert::ToString(vtoken);
+               boost::algorithm::replace_all(token, "\\", "\\\\");
+
+               char sep_before[2], sep_after[3];
+               sep_before[0] = separator;
+               sep_before[1] = '\0';
+               sep_after[0] = '\\';
+               sep_after[1] = separator;
+               sep_after[2] = '\0';
+               boost::algorithm::replace_all(token, sep_before, sep_after);
+
+               if (first)
+                       first = false;
+               else
+                       result += String(1, separator);
+
+               result += token;
+       }
+
+       return result;
+}
+
 String Utility::FormatDuration(double duration)
 {
        std::vector<String> tokens;
index 126b41bdb4fec4be54c7b717461b464fe7cd12fe..ec435d7fc5dc0f19a653229dde674cb943620390 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "base/i2-base.hpp"
 #include "base/string.hpp"
+#include "base/array.hpp"
 #include <typeinfo>
 #include <boost/function.hpp>
 #include <boost/thread/tss.hpp>
@@ -96,6 +97,7 @@ public:
        static void QueueAsyncCallback(const boost::function<void (void)>& callback, SchedulerPolicy policy = DefaultScheduler);
 
        static String NaturalJoin(const std::vector<String>& tokens);
+       static String Join(const Array::Ptr& tokens, char separator);
 
        static String FormatDuration(double duration);
        static String FormatDateTime(const char *format, double ts);
index 4db6397c412ab4f1f6e4aa760484d711c03818dc..73f5e48bec8d71e051837d85b669341ed3b2c94c 100644 (file)
@@ -516,14 +516,22 @@ void StatusDataWriter::DumpCustomAttributes(std::ostream& fp, const CustomVarObj
 
        ObjectLock olock(vars);
        BOOST_FOREACH(const Dictionary::Pair& kv, vars) {
-               if (!kv.first.IsEmpty()) {
-                       fp << "\t";
+               if (kv.first.IsEmpty())
+                       continue;
 
-                       if (!CompatUtility::IsLegacyAttribute(object, kv.first))
-                               fp << "_";
+               String value;
 
-                       fp << kv.first << "\t" << kv.second << "\n";
-               }
+               if (kv.second.IsObjectType<Array>())
+                       value = Utility::Join(kv.second, ';');
+               else
+                       value = kv.second;
+
+               fp << "\t";
+
+               if (!CompatUtility::IsLegacyAttribute(object, kv.first))
+                       fp << "_";
+
+               fp << kv.first << "\t" << value << "\n";
        }
 }
 
index 11870e70ed0a6b382c315cee46de3d41a5c8b724..037317e0320179387f1b2b1f7bc9d745dc8f61a3 100644 (file)
@@ -28,8 +28,10 @@ ApplyRule::RuleMap ApplyRule::m_Rules;
 ApplyRule::CallbackMap ApplyRule::m_Callbacks;
 
 ApplyRule::ApplyRule(const String& targetType, const String& name, const Expression::Ptr& expression,
-    const Expression::Ptr& filter, const DebugInfo& di, const Dictionary::Ptr& scope)
-       : m_TargetType(targetType), m_Name(name), m_Expression(expression), m_Filter(filter), m_DebugInfo(di), m_Scope(scope)
+    const Expression::Ptr& filter, const String& fvar, const Expression::Ptr& fterm,
+    const DebugInfo& di, const Dictionary::Ptr& scope)
+       : m_TargetType(targetType), m_Name(name), m_Expression(expression), m_Filter(filter), m_FVar(fvar),
+         m_FTerm(fterm), m_DebugInfo(di), m_Scope(scope)
 { }
 
 String ApplyRule::GetTargetType(void) const
@@ -52,6 +54,16 @@ Expression::Ptr ApplyRule::GetFilter(void) const
        return m_Filter;
 }
 
+String ApplyRule::GetFVar(void) const
+{
+       return m_FVar;
+}
+
+Expression::Ptr ApplyRule::GetFTerm(void) const
+{
+       return m_FTerm;
+}
+
 DebugInfo ApplyRule::GetDebugInfo(void) const
 {
        return m_DebugInfo;
@@ -63,10 +75,10 @@ Dictionary::Ptr ApplyRule::GetScope(void) const
 }
 
 void ApplyRule::AddRule(const String& sourceType, const String& targetType, const String& name,
-    const Expression::Ptr& expression, const Expression::Ptr& filter,
-    const DebugInfo& di, const Dictionary::Ptr& scope)
+    const Expression::Ptr& expression, const Expression::Ptr& filter, const String& fvar,
+    const Expression::Ptr& fterm, const DebugInfo& di, const Dictionary::Ptr& scope)
 {
-       m_Rules[sourceType].push_back(ApplyRule(targetType, name, expression, filter, di, scope));
+       m_Rules[sourceType].push_back(ApplyRule(targetType, name, expression, filter, fvar, fterm, di, scope));
 }
 
 bool ApplyRule::EvaluateFilter(const Dictionary::Ptr& scope) const
index b7590f1804037052b271845491ce0498c4249482..13fabd55eff1641a730714860568cd9bef594917 100644 (file)
@@ -42,13 +42,15 @@ public:
        String GetName(void) const;
        Expression::Ptr GetExpression(void) const;
        Expression::Ptr GetFilter(void) const;
+       String GetFVar(void) const;
+       Expression::Ptr GetFTerm(void) const;
        DebugInfo GetDebugInfo(void) const;
        Dictionary::Ptr GetScope(void) const;
 
        bool EvaluateFilter(const Dictionary::Ptr& scope) const;
 
        static void AddRule(const String& sourceType, const String& targetType, const String& name, const Expression::Ptr& expression,
-           const Expression::Ptr& filter, const DebugInfo& di, const Dictionary::Ptr& scope);
+           const Expression::Ptr& filter, const String& fvar, const Expression::Ptr& fterm, const DebugInfo& di, const Dictionary::Ptr& scope);
        static void EvaluateRules(bool clear);
 
        static void RegisterType(const String& sourceType, const std::vector<String>& targetTypes, const ApplyRule::Callback& callback);
@@ -61,6 +63,8 @@ private:
        String m_Name;
        Expression::Ptr m_Expression;
        Expression::Ptr m_Filter;
+       String m_FVar;
+       Expression::Ptr m_FTerm;
        DebugInfo m_DebugInfo;
        Dictionary::Ptr m_Scope;
 
@@ -68,7 +72,8 @@ private:
        static RuleMap m_Rules;
 
        ApplyRule(const String& targetType, const String& name, const Expression::Ptr& expression,
-           const Expression::Ptr& filter, const DebugInfo& di, const Dictionary::Ptr& scope);
+           const Expression::Ptr& filter, const String& fvar, const Expression::Ptr& fterm,
+           const DebugInfo& di, const Dictionary::Ptr& scope);
 };
 
 }
index 1e689ff2d6d5bd4fa36b1ca0b52925012e7473a8..f5cfe0b37b4ac63d33e414612a8682df1c606239 100644 (file)
        %attribute %dictionary "methods",
 
        %attribute %dictionary "vars" {
-               %attribute %string "*"
+               %attribute %string "*",
+               %attribute %array "*" {
+                       %attribute %string "*"
+               }
        },
 }
 
index 72bcc7ba5e69562445466336d293b6ea1be748d9..9f10adcf2d68e5f269f557206a9ae506730f4445 100644 (file)
@@ -218,6 +218,8 @@ static std::stack<bool> m_ObjectAssign;
 static std::stack<bool> m_SeenAssign;
 static std::stack<Expression::Ptr> m_Assign;
 static std::stack<Expression::Ptr> m_Ignore;
+static std::stack<String> m_FVar;
+static std::stack<Expression::Ptr> m_FTerm;
 
 void ConfigCompiler::Compile(void)
 {
@@ -231,6 +233,8 @@ void ConfigCompiler::Compile(void)
        m_SeenAssign = std::stack<bool>();
        m_Assign = std::stack<Expression::Ptr>();
        m_Ignore = std::stack<Expression::Ptr>();
+       m_FVar = std::stack<String>();
+       m_FTerm = std::stack<Expression::Ptr>();
 
        try {
                yyparse(this);
@@ -836,14 +840,27 @@ target_type_specifier: /* empty */
        }
        ;
 
+apply_for_specifier: /* empty */
+       | T_FOR '(' identifier T_IN rterm ')'
+       {
+               m_FVar.top() = $3;
+               free($3);
+
+               m_FTerm.top() = *$5;
+               delete $5;
+       }
+       ;
+
 apply:
        {
                m_Apply.push(true);
                m_SeenAssign.push(false);
                m_Assign.push(make_shared<Expression>(&Expression::OpLiteral, false, DebugInfo()));
                m_Ignore.push(make_shared<Expression>(&Expression::OpLiteral, false, DebugInfo()));
+               m_FVar.push("");
+               m_FTerm.push(Expression::Ptr());
        }
-       T_APPLY identifier rterm target_type_specifier rterm
+       T_APPLY identifier rterm apply_for_specifier target_type_specifier rterm
        {
                m_Apply.pop();
 
@@ -851,8 +868,8 @@ apply:
                free($3);
                Expression::Ptr aname = *$4;
                delete $4;
-               String target = $5;
-               free($5);
+               String target = $6;
+               free($6);
 
                if (!ApplyRule::IsValidSourceType(type))
                        BOOST_THROW_EXCEPTION(ConfigError("'apply' cannot be used with type '" + type + "'") << errinfo_debuginfo(DebugInfoRange(@2, @3)));
@@ -878,8 +895,8 @@ apply:
                                BOOST_THROW_EXCEPTION(ConfigError("'apply' target type '" + target + "' is invalid") << errinfo_debuginfo(DebugInfoRange(@2, @5)));
                }
 
-               Expression::Ptr exprl = *$6;
-               delete $6;
+               Expression::Ptr exprl = *$7;
+               delete $7;
 
                exprl->MakeInline();
 
@@ -895,11 +912,19 @@ apply:
                Expression::Ptr filter = make_shared<Expression>(&Expression::OpLogicalAnd, m_Assign.top(), rex, DebugInfoRange(@2, @5));
                m_Assign.pop();
 
+               String fvar = m_FVar.top();
+               m_FVar.pop();
+
+               Expression::Ptr fterm = m_FTerm.top();
+               m_FTerm.pop();
+
                Array::Ptr args = make_shared<Array>();
                args->Add(type);
                args->Add(target);
                args->Add(aname);
                args->Add(filter);
+               args->Add(fvar);
+               args->Add(fterm);
 
                $$ = new Value(make_shared<Expression>(&Expression::OpApply, args, exprl, DebugInfoRange(@2, @5)));
        }
index a8d4513c90cf7341f3b7e3e91b65a7ed24a21f60..234a045265cd8dccd74cc14753870f250924429e 100644 (file)
@@ -551,10 +551,12 @@ Value Expression::OpApply(const Expression* expr, const Dictionary::Ptr& locals,
        String target = left->Get(1);
        Expression::Ptr aname = left->Get(2);
        Expression::Ptr filter = left->Get(3);
+       String fvar = left->Get(4);
+       Expression::Ptr fterm = left->Get(5);
 
        String name = aname->Evaluate(locals, dhint);
 
-       ApplyRule::AddRule(type, target, name, exprl, filter, expr->m_DebugInfo, locals);
+       ApplyRule::AddRule(type, target, name, exprl, filter, fvar, fterm, expr->m_DebugInfo, locals);
 
        return Empty;
 }
index d276513c9431658540e9867e15a7d3c5d6672df0..7f3f3c47b87846560ba785589c8864986152a611 100644 (file)
@@ -167,28 +167,36 @@ void DbObject::SendVarsConfigUpdate(void)
                ObjectLock olock (vars);
 
                BOOST_FOREACH(const Dictionary::Pair& kv, vars) {
-                       if (!kv.first.IsEmpty()) {
-                               int overridden = custom_var_object->IsVarOverridden(kv.first) ? 1 : 0;
-
-                               Log(LogDebug, "DbObject")
-                                   << "object customvar key: '" << kv.first << "' value: '" << kv.second
-                                   << "' overridden: " << overridden;
-
-                               Dictionary::Ptr fields = make_shared<Dictionary>();
-                               fields->Set("varname", Convert::ToString(kv.first));
-                               fields->Set("varvalue", Convert::ToString(kv.second));
-                               fields->Set("config_type", 1);
-                               fields->Set("has_been_modified", overridden);
-                               fields->Set("object_id", obj);
-                               fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
-
-                               DbQuery query;
-                               query.Table = "customvariables";
-                               query.Type = DbQueryInsert;
-                               query.Category = DbCatConfig;
-                               query.Fields = fields;
-                               OnQuery(query);
-                       }
+                       if (kv.first.IsEmpty())
+                               continue;
+
+                       String value;
+
+                       if (kv.second.IsObjectType<Array>())
+                               value = Utility::Join(kv.second, ';');
+                       else
+                               value = kv.second;
+
+                       int overridden = custom_var_object->IsVarOverridden(kv.first) ? 1 : 0;
+
+                       Log(LogDebug, "DbObject")
+                           << "object customvar key: '" << kv.first << "' value: '" << kv.second
+                           << "' overridden: " << overridden;
+
+                       Dictionary::Ptr fields = make_shared<Dictionary>();
+                       fields->Set("varname", kv.first);
+                       fields->Set("varvalue", value);
+                       fields->Set("config_type", 1);
+                       fields->Set("has_been_modified", overridden);
+                       fields->Set("object_id", obj);
+                       fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+                       DbQuery query;
+                       query.Table = "customvariables";
+                       query.Type = DbQueryInsert;
+                       query.Category = DbCatConfig;
+                       query.Fields = fields;
+                       OnQuery(query);
                }
        }
 }
@@ -211,36 +219,36 @@ void DbObject::SendVarsStatusUpdate(void)
                ObjectLock olock (vars);
 
                BOOST_FOREACH(const Dictionary::Pair& kv, vars) {
-                       if (!kv.first.IsEmpty()) {
-                               int overridden = custom_var_object->IsVarOverridden(kv.first) ? 1 : 0;
-
-                               Log(LogDebug, "DbObject")
-                                   << "object customvar key: '" << kv.first << "' value: '" << kv.second
-                                   << "' overridden: " << overridden;
-
-                               Dictionary::Ptr fields = make_shared<Dictionary>();
-                               fields->Set("varname", Convert::ToString(kv.first));
-                               fields->Set("varvalue", Convert::ToString(kv.second));
-                               fields->Set("has_been_modified", overridden);
-                               fields->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
-                               fields->Set("object_id", obj);
-                               fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
-
-                               DbQuery query;
-                               query.Table = "customvariablestatus";
-                               query.Type = DbQueryInsert | DbQueryUpdate;
-                               query.Category = DbCatState;
-                               query.Fields = fields;
-
-                               query.WhereCriteria = make_shared<Dictionary>();
-                               query.WhereCriteria->Set("object_id", obj);
-                               query.WhereCriteria->Set("varname", Convert::ToString(kv.first));
-                               query.Object = GetSelf();
-
-                               OnQuery(query);
-                       }
+                       if (kv.first.IsEmpty() || kv.second.IsObject())
+                               continue;
+
+                       int overridden = custom_var_object->IsVarOverridden(kv.first) ? 1 : 0;
+
+                       Log(LogDebug, "DbObject")
+                           << "object customvar key: '" << kv.first << "' value: '" << kv.second
+                           << "' overridden: " << overridden;
+
+                       Dictionary::Ptr fields = make_shared<Dictionary>();
+                       fields->Set("varname", Convert::ToString(kv.first));
+                       fields->Set("varvalue", Convert::ToString(kv.second));
+                       fields->Set("has_been_modified", overridden);
+                       fields->Set("status_update_time", DbValue::FromTimestamp(Utility::GetTime()));
+                       fields->Set("object_id", obj);
+                       fields->Set("instance_id", 0); /* DbConnection class fills in real ID */
+
+                       DbQuery query;
+                       query.Table = "customvariablestatus";
+                       query.Type = DbQueryInsert | DbQueryUpdate;
+                       query.Category = DbCatState;
+                       query.Fields = fields;
+
+                       query.WhereCriteria = make_shared<Dictionary>();
+                       query.WhereCriteria->Set("object_id", obj);
+                       query.WhereCriteria->Set("varname", Convert::ToString(kv.first));
+                       query.Object = GetSelf();
+
+                       OnQuery(query);
                }
-
        }
 }
 
index 7969d3484c83d9f697ea66614dadf5bfedb7b7ba..919fc43437b99b487192205476e38884544a29ee 100644 (file)
@@ -63,46 +63,70 @@ bool Dependency::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const App
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Log(LogDebug, "Dependency")
-           << "Applying dependency '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+       Array::Ptr instances;
 
-       ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
-       builder->SetType("Dependency");
-       builder->SetName(rule.GetName());
-       builder->SetScope(locals);
+       if (rule.GetFTerm()) {
+               Value vinstances = rule.GetFTerm()->Evaluate(locals);
 
-       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-               make_shared<Expression>(&Expression::OpLiteral, "parent_host_name", di),
-               make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
-               di));
+               if (!vinstances.IsObjectType<Array>())
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
 
-       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-           make_shared<Expression>(&Expression::OpLiteral, "child_host_name", di),
-           make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
-           di));
-
-       if (service) {
-               builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "child_service_name", di),
-                   make_shared<Expression>(&Expression::OpLiteral, service->GetShortName(), di),
-                   di));
+               instances = vinstances;
+       } else {
+               instances = make_shared<Array>();
+               instances->Add("");
        }
 
-       String zone = checkable->GetZone();
+       ObjectLock olock(instances);
+       BOOST_FOREACH(const String& instance, instances) {
+               String objName = rule.GetName();
+
+               if (!rule.GetFVar().IsEmpty()) {
+                       locals->Set(rule.GetFVar(), instance);
+                       objName += "-" + instance;
+               }
+
+               Log(LogDebug, "Dependency")
+                       << "Applying dependency '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+
+               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+               builder->SetType("Dependency");
+               builder->SetName(objName);
+               builder->SetScope(locals);
 
-       if (!zone.IsEmpty()) {
                builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "zone", di),
-                   make_shared<Expression>(&Expression::OpLiteral, zone, di),
-                   di));
-       }
+                       make_shared<Expression>(&Expression::OpLiteral, "parent_host_name", di),
+                       make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
+                       di));
 
-       builder->AddExpression(rule.GetExpression());
+               builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                       make_shared<Expression>(&Expression::OpLiteral, "child_host_name", di),
+                       make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
+                       di));
+
+               if (service) {
+                       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                               make_shared<Expression>(&Expression::OpLiteral, "child_service_name", di),
+                               make_shared<Expression>(&Expression::OpLiteral, service->GetShortName(), di),
+                               di));
+               }
 
-       ConfigItem::Ptr dependencyItem = builder->Compile();
-       dependencyItem->Register();
-       DynamicObject::Ptr dobj = dependencyItem->Commit();
-       dobj->OnConfigLoaded();
+               String zone = checkable->GetZone();
+
+               if (!zone.IsEmpty()) {
+                       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                               make_shared<Expression>(&Expression::OpLiteral, "zone", di),
+                               make_shared<Expression>(&Expression::OpLiteral, zone, di),
+                               di));
+               }
+
+               builder->AddExpression(rule.GetExpression());
+
+               ConfigItem::Ptr dependencyItem = builder->Compile();
+               dependencyItem->Register();
+               DynamicObject::Ptr dobj = dependencyItem->Commit();
+               dobj->OnConfigLoaded();
+       }
 
        return true;
 }
index 7715bf40e2c2b0551d31e3c0ca4cd76e8fc7d2d8..06f4791bd8a17de966b71a2b3c1b0b052c38efec 100644 (file)
@@ -63,41 +63,65 @@ bool Notification::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const A
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Log(LogDebug, "Notification")
-           << "Applying notification '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+       Array::Ptr instances;
 
-       ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
-       builder->SetType("Notification");
-       builder->SetName(rule.GetName());
-       builder->SetScope(locals);
+       if (rule.GetFTerm()) {
+               Value vinstances = rule.GetFTerm()->Evaluate(locals);
 
-       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-           make_shared<Expression>(&Expression::OpLiteral, "host_name", di),
-           make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
-           di));
+               if (!vinstances.IsObjectType<Array>())
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
 
-       if (service) {
-               builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "service_name", di),
-                   make_shared<Expression>(&Expression::OpLiteral, service->GetShortName(), di),
-                   di));
+               instances = vinstances;
+       } else {
+               instances = make_shared<Array>();
+               instances->Add("");
        }
 
-       String zone = checkable->GetZone();
+       ObjectLock olock(instances);
+       BOOST_FOREACH(const String& instance, instances) {
+               String objName = rule.GetName();
+
+               if (!rule.GetFVar().IsEmpty()) {
+                       locals->Set(rule.GetFVar(), instance);
+                       objName += "-" + instance;
+               }
+
+               Log(LogDebug, "Notification")
+                       << "Applying notification '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+
+               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+               builder->SetType("Notification");
+               builder->SetName(objName);
+               builder->SetScope(locals);
 
-       if (!zone.IsEmpty()) {
                builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "zone", di),
-                   make_shared<Expression>(&Expression::OpLiteral, zone, di),
-                   di));
-       }
+                       make_shared<Expression>(&Expression::OpLiteral, "host_name", di),
+                       make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
+                       di));
+
+               if (service) {
+                       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                               make_shared<Expression>(&Expression::OpLiteral, "service_name", di),
+                               make_shared<Expression>(&Expression::OpLiteral, service->GetShortName(), di),
+                               di));
+               }
+
+               String zone = checkable->GetZone();
 
-       builder->AddExpression(rule.GetExpression());
+               if (!zone.IsEmpty()) {
+                       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                               make_shared<Expression>(&Expression::OpLiteral, "zone", di),
+                               make_shared<Expression>(&Expression::OpLiteral, zone, di),
+                               di));
+               }
+
+               builder->AddExpression(rule.GetExpression());
 
-       ConfigItem::Ptr notificationItem = builder->Compile();
-       notificationItem->Register();
-       DynamicObject::Ptr dobj = notificationItem->Commit();
-       dobj->OnConfigLoaded();
+               ConfigItem::Ptr notificationItem = builder->Compile();
+               notificationItem->Register();
+               DynamicObject::Ptr dobj = notificationItem->Commit();
+               dobj->OnConfigLoaded();
+       }
 
        return true;
 }
index 8774ed86cf09840711f20c046cf5aa7f22509d0c..9251cd6c0bf16861994a58f9ffd6ccb842e323cf 100644 (file)
@@ -62,41 +62,65 @@ bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Log(LogDebug, "ScheduledDowntime")
-           << "Applying scheduled downtime '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+       Array::Ptr instances;
 
-       ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
-       builder->SetType("ScheduledDowntime");
-       builder->SetName(rule.GetName());
-       builder->SetScope(locals);
+       if (rule.GetFTerm()) {
+               Value vinstances = rule.GetFTerm()->Evaluate(locals);
 
-       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-           make_shared<Expression>(&Expression::OpLiteral, "host_name", di),
-           make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
-           di));
+               if (!vinstances.IsObjectType<Array>())
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
 
-       if (service) {
-               builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "service_name", di),
-                   make_shared<Expression>(&Expression::OpLiteral, service->GetShortName(), di),
-                   di));
+               instances = vinstances;
+       } else {
+               instances = make_shared<Array>();
+               instances->Add("");
        }
 
-       String zone = checkable->GetZone();
+       ObjectLock olock(instances);
+       BOOST_FOREACH(const String& instance, instances) {
+               String objName = rule.GetName();
+
+               if (!rule.GetFVar().IsEmpty()) {
+                       locals->Set(rule.GetFVar(), instance);
+                       objName += "-" + instance;
+               }
+
+               Log(LogDebug, "ScheduledDowntime")
+                       << "Applying scheduled downtime '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+
+               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+               builder->SetType("ScheduledDowntime");
+               builder->SetName(objName);
+               builder->SetScope(locals);
 
-       if (!zone.IsEmpty()) {
                builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "zone", di),
-                   make_shared<Expression>(&Expression::OpLiteral, zone, di),
-                   di));
-       }
+                       make_shared<Expression>(&Expression::OpLiteral, "host_name", di),
+                       make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
+                       di));
+
+               if (service) {
+                       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                               make_shared<Expression>(&Expression::OpLiteral, "service_name", di),
+                               make_shared<Expression>(&Expression::OpLiteral, service->GetShortName(), di),
+                               di));
+               }
+
+               String zone = checkable->GetZone();
 
-       builder->AddExpression(rule.GetExpression());
+               if (!zone.IsEmpty()) {
+                       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                               make_shared<Expression>(&Expression::OpLiteral, "zone", di),
+                               make_shared<Expression>(&Expression::OpLiteral, zone, di),
+                               di));
+               }
 
-       ConfigItem::Ptr downtimeItem = builder->Compile();
-       downtimeItem->Register();
-       DynamicObject::Ptr dobj = downtimeItem->Commit();
-       dobj->OnConfigLoaded();
+               builder->AddExpression(rule.GetExpression());
+
+               ConfigItem::Ptr downtimeItem = builder->Compile();
+               downtimeItem->Register();
+               DynamicObject::Ptr dobj = downtimeItem->Commit();
+               dobj->OnConfigLoaded();
+       }
 
        return true;
 }
index f7c3fca943535a04583c499c6ead3d26b2cd70b7..5cf08ec3a56b7e787fbd5d265ed5d37994bd65f5 100644 (file)
@@ -55,39 +55,63 @@ bool Service::EvaluateApplyRuleOne(const Host::Ptr& host, const ApplyRule& rule)
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Log(LogDebug, "Service")
-           << "Applying service '" << rule.GetName() << "' to host '" << host->GetName() << "' for rule " << di;
+       Array::Ptr instances;
 
-       ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
-       builder->SetType("Service");
-       builder->SetName(rule.GetName());
-       builder->SetScope(locals);
+       if (rule.GetFTerm()) {
+               Value vinstances = rule.GetFTerm()->Evaluate(locals);
 
-       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-           make_shared<Expression>(&Expression::OpLiteral, "host_name", di), 
-           make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
-           di));
+               if (!vinstances.IsObjectType<Array>())
+                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
 
-       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-           make_shared<Expression>(&Expression::OpLiteral, "name", di),
-           make_shared<Expression>(&Expression::OpLiteral, rule.GetName(), di),
-           di));
+               instances = vinstances;
+       } else {
+               instances = make_shared<Array>();
+               instances->Add("");
+       }
+
+       ObjectLock olock(instances);
+       BOOST_FOREACH(const String& instance, instances) {
+               String objName = rule.GetName();
+
+               if (!rule.GetFVar().IsEmpty()) {
+                       locals->Set(rule.GetFVar(), instance);
+                       objName += "-" + instance;
+               }
 
-       String zone = host->GetZone();
+               Log(LogDebug, "Service")
+                   << "Applying service '" << objName << "' to host '" << host->GetName() << "' for rule " << di;
+
+               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+               builder->SetType("Service");
+               builder->SetName(objName);
+               builder->SetScope(locals);
 
-       if (!zone.IsEmpty()) {
                builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "zone", di),
-                   make_shared<Expression>(&Expression::OpLiteral, zone, di),
+                   make_shared<Expression>(&Expression::OpLiteral, "host_name", di),
+                   make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
                    di));
-       }
 
-       builder->AddExpression(rule.GetExpression());
+               builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                   make_shared<Expression>(&Expression::OpLiteral, "name", di),
+                   make_shared<Expression>(&Expression::OpLiteral, objName, di),
+                   di));
+
+               String zone = host->GetZone();
+
+               if (!zone.IsEmpty()) {
+                       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+                           make_shared<Expression>(&Expression::OpLiteral, "zone", di),
+                           make_shared<Expression>(&Expression::OpLiteral, zone, di),
+                           di));
+               }
+
+               builder->AddExpression(rule.GetExpression());
 
-       ConfigItem::Ptr serviceItem = builder->Compile();
-       serviceItem->Register();
-       DynamicObject::Ptr dobj = serviceItem->Commit();
-       dobj->OnConfigLoaded();
+               ConfigItem::Ptr serviceItem = builder->Compile();
+               serviceItem->Register();
+               DynamicObject::Ptr dobj = serviceItem->Commit();
+               dobj->OnConfigLoaded();
+       }
 
        return true;
 }