]> granicus.if.org Git - icinga2/commitdiff
Implement dictionary support for apply+for
authorGunnar Beutner <gunnar.beutner@netways.de>
Tue, 4 Nov 2014 10:01:00 +0000 (11:01 +0100)
committerGunnar Beutner <gunnar.beutner@netways.de>
Tue, 4 Nov 2014 10:02:10 +0000 (11:02 +0100)
fixes #7561

14 files changed:
lib/config/applyrule.cpp
lib/config/applyrule.hpp
lib/config/base-type.conf
lib/config/config_lexer.ll
lib/config/config_parser.yy
lib/config/expression.cpp
lib/icinga/dependency-apply.cpp
lib/icinga/dependency.hpp
lib/icinga/notification-apply.cpp
lib/icinga/notification.hpp
lib/icinga/scheduleddowntime-apply.cpp
lib/icinga/scheduleddowntime.hpp
lib/icinga/service-apply.cpp
lib/icinga/service.hpp

index 037317e0320179387f1b2b1f7bc9d745dc8f61a3..a8f24f720d980bffb3a40be8d92e35cb7a6c13cd 100644 (file)
@@ -28,10 +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 String& fvar, const Expression::Ptr& fterm,
+    const Expression::Ptr& filter, const String& fkvar, const String& fvvar, 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)
+       : m_TargetType(targetType), m_Name(name), m_Expression(expression), m_Filter(filter), m_FKVar(fkvar),
+         m_FVVar(fvvar), m_FTerm(fterm), m_DebugInfo(di), m_Scope(scope)
 { }
 
 String ApplyRule::GetTargetType(void) const
@@ -54,9 +54,14 @@ Expression::Ptr ApplyRule::GetFilter(void) const
        return m_Filter;
 }
 
-String ApplyRule::GetFVar(void) const
+String ApplyRule::GetFKVar(void) const
 {
-       return m_FVar;
+       return m_FKVar;
+}
+
+String ApplyRule::GetFVVar(void) const
+{
+       return m_FVVar;
 }
 
 Expression::Ptr ApplyRule::GetFTerm(void) const
@@ -75,10 +80,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 String& fvar,
-    const Expression::Ptr& fterm, const DebugInfo& di, const Dictionary::Ptr& scope)
+    const Expression::Ptr& expression, const Expression::Ptr& filter, const String& fkvar,
+    const String& fvvar, const Expression::Ptr& fterm, const DebugInfo& di, const Dictionary::Ptr& scope)
 {
-       m_Rules[sourceType].push_back(ApplyRule(targetType, name, expression, filter, fvar, fterm, di, scope));
+       m_Rules[sourceType].push_back(ApplyRule(targetType, name, expression, filter, fkvar, fvvar, fterm, di, scope));
 }
 
 bool ApplyRule::EvaluateFilter(const Dictionary::Ptr& scope) const
index 13fabd55eff1641a730714860568cd9bef594917..1a5375fabad54c4c14776731bb932e8b98bd5746 100644 (file)
@@ -42,7 +42,8 @@ public:
        String GetName(void) const;
        Expression::Ptr GetExpression(void) const;
        Expression::Ptr GetFilter(void) const;
-       String GetFVar(void) const;
+       String GetFKVar(void) const;
+       String GetFVVar(void) const;
        Expression::Ptr GetFTerm(void) const;
        DebugInfo GetDebugInfo(void) const;
        Dictionary::Ptr GetScope(void) const;
@@ -50,7 +51,7 @@ public:
        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 String& fvar, const Expression::Ptr& fterm, const DebugInfo& di, const Dictionary::Ptr& scope);
+           const Expression::Ptr& filter, const String& fkvar, const String& fvvar, 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);
@@ -63,7 +64,8 @@ private:
        String m_Name;
        Expression::Ptr m_Expression;
        Expression::Ptr m_Filter;
-       String m_FVar;
+       String m_FKVar;
+       String m_FVVar;
        Expression::Ptr m_FTerm;
        DebugInfo m_DebugInfo;
        Dictionary::Ptr m_Scope;
@@ -72,7 +74,7 @@ private:
        static RuleMap m_Rules;
 
        ApplyRule(const String& targetType, const String& name, const Expression::Ptr& expression,
-           const Expression::Ptr& filter, const String& fvar, const Expression::Ptr& fterm,
+           const Expression::Ptr& filter, const String& fkvar, const String& fvvar, const Expression::Ptr& fterm,
            const DebugInfo& di, const Dictionary::Ptr& scope);
 };
 
index f5cfe0b37b4ac63d33e414612a8682df1c606239..542026eec79eeaa1959175718c76c4c9467a1cfc 100644 (file)
 
        %attribute %dictionary "methods",
 
-       %attribute %dictionary "vars" {
-               %attribute %string "*",
-               %attribute %array "*" {
-                       %attribute %string "*"
-               }
-       },
+       %attribute %dictionary "vars"
 }
 
 %type Logger {
index 87691dd33e41043b4653eb7401bebe8de2cff7f2..42868e71b6a918a4dee67ef4b2d0621e972df0fd 100644 (file)
@@ -228,9 +228,11 @@ where                              return T_WHERE;
 import                         return T_IMPORT;
 assign                         return T_ASSIGN;
 ignore                         return T_IGNORE;
+for                            return T_APPLY_FOR;
 __function                     return T_FUNCTION;
 __return                       return T_RETURN;
 __for                          return T_FOR;
+=\>                            return T_FOLLOWS;
 \<\<                           { yylval->op = &Expression::OpShiftLeft; return T_SHIFT_LEFT; }
 \>\>                           { yylval->op = &Expression::OpShiftRight; return T_SHIFT_RIGHT; }
 \<=                            { yylval->op = &Expression::OpLessThanOrEqual; return T_LESS_THAN_OR_EQUAL; }
index 9f10adcf2d68e5f269f557206a9ae506730f4445..5f7bff67a766a8d53712561ae542492e2b130c56 100644 (file)
@@ -157,9 +157,11 @@ static void MakeRBinaryOp(Value** result, Expression::OpCallback& op, Value *lef
 %token T_IMPORT "import (T_IMPORT)"
 %token T_ASSIGN "assign (T_ASSIGN)"
 %token T_IGNORE "ignore (T_IGNORE)"
+%token T_APPLY_FOR "for (T_APPLY_FOR)"
 %token T_FUNCTION "function (T_FUNCTION)"
 %token T_RETURN "return (T_RETURN)"
 %token T_FOR "for (T_FOR)"
+%token T_FOLLOWS "=> (T_FOLLOWS)"
 
 %type <text> identifier
 %type <array> rterm_items
@@ -177,6 +179,7 @@ static void MakeRBinaryOp(Value** result, Expression::OpCallback& op, Value *lef
 %type <variant> lterm
 %type <variant> object
 %type <variant> apply
+%type <variant> optional_rterm
 %type <text> target_type_specifier
 
 %left T_LOGICAL_OR
@@ -218,7 +221,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<String> m_FKVar;
+static std::stack<String> m_FVVar;
 static std::stack<Expression::Ptr> m_FTerm;
 
 void ConfigCompiler::Compile(void)
@@ -233,7 +237,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_FKVar = std::stack<String>();
+       m_FVVar = std::stack<String>();
        m_FTerm = std::stack<Expression::Ptr>();
 
        try {
@@ -812,6 +817,25 @@ rterm: T_STRING
 
                $$ = new Value(make_shared<Expression>(&Expression::OpFunction, arr, Array::Ptr($3), DebugInfoRange(@1, @5)));
        }
+       | T_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')' rterm_scope
+       {
+               Array::Ptr arr = make_shared<Array>();
+
+               arr->Add($3);
+               free($3);
+
+               arr->Add($5);
+               free($5);
+
+               Expression::Ptr aexpr = *$7;
+               delete $7;
+               arr->Add(aexpr);
+
+               Expression::Ptr ascope = *$9;
+               delete $9;
+
+               $$ = new Value(make_shared<Expression>(&Expression::OpFor, arr, ascope, DebugInfoRange(@1, @9)));
+       }
        | T_FOR '(' identifier T_IN rterm ')' rterm_scope
        {
                Array::Ptr arr = make_shared<Array>();
@@ -819,6 +843,8 @@ rterm: T_STRING
                arr->Add($3);
                free($3);
 
+               arr->Add(Empty);
+
                Expression::Ptr aexpr = *$5;
                delete $5;
                arr->Add(aexpr);
@@ -841,26 +867,50 @@ target_type_specifier: /* empty */
        ;
 
 apply_for_specifier: /* empty */
-       | T_FOR '(' identifier T_IN rterm ')'
+       | T_APPLY_FOR '(' identifier T_FOLLOWS identifier T_IN rterm ')'
+       {
+               m_FKVar.top() = $3;
+               free($3);
+
+               m_FVVar.top() = $5;
+               free($5);
+
+               m_FTerm.top() = *$7;
+               delete $7;
+       }
+       | T_APPLY_FOR '(' identifier T_IN rterm ')'
        {
-               m_FVar.top() = $3;
+               m_FKVar.top() = $3;
                free($3);
 
+               m_FVVar.top() = "";
+
                m_FTerm.top() = *$5;
                delete $5;
        }
        ;
 
+optional_rterm: /* empty */
+       {
+               $$ = new Value(make_shared<Expression>(&Expression::OpLiteral, Empty, DebugInfo()));
+       }
+       | rterm
+       {
+               $$ = $1;
+       }
+       ;
+
 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_FKVar.push("");
+               m_FVVar.push("");
                m_FTerm.push(Expression::Ptr());
        }
-       T_APPLY identifier rterm apply_for_specifier target_type_specifier rterm
+       T_APPLY identifier optional_rterm apply_for_specifier target_type_specifier rterm
        {
                m_Apply.pop();
 
@@ -912,8 +962,11 @@ 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();
+               String fkvar = m_FKVar.top();
+               m_FKVar.pop();
+
+               String fvvar = m_FVVar.top();
+               m_FVVar.pop();
 
                Expression::Ptr fterm = m_FTerm.top();
                m_FTerm.pop();
@@ -923,7 +976,8 @@ apply:
                args->Add(target);
                args->Add(aname);
                args->Add(filter);
-               args->Add(fvar);
+               args->Add(fkvar);
+               args->Add(fvvar);
                args->Add(fterm);
 
                $$ = new Value(make_shared<Expression>(&Expression::OpApply, args, exprl, DebugInfoRange(@2, @5)));
index 538aad6a188aea0870ff642e0301d867145a598e..84537777b6d09fac9d023edb40ea19090f961743 100644 (file)
@@ -551,12 +551,13 @@ 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 fkvar = left->Get(4);
+       String fvvar = left->Get(5);
+       Expression::Ptr fterm = left->Get(6);
 
        String name = aname->Evaluate(locals, dhint);
 
-       ApplyRule::AddRule(type, target, name, exprl, filter, fvar, fterm, expr->m_DebugInfo, locals);
+       ApplyRule::AddRule(type, target, name, exprl, filter, fkvar, fvvar, fterm, expr->m_DebugInfo, locals);
 
        return Empty;
 }
@@ -618,20 +619,44 @@ Value Expression::OpObject(const Expression* expr, const Dictionary::Ptr& locals
 Value Expression::OpFor(const Expression* expr, const Dictionary::Ptr& locals, DebugHint *dhint)
 {
        Array::Ptr left = expr->m_Operand1;
-       String varname = left->Get(0);
-       Expression::Ptr aexpr = left->Get(1);
+       String kvar = left->Get(0);
+       String vvar = left->Get(1);
+       Expression::Ptr aexpr = left->Get(2);
        Expression::Ptr ascope = expr->m_Operand2;
 
-       Array::Ptr arr = aexpr->Evaluate(locals, dhint);
+       Value value = aexpr->Evaluate(locals, dhint);
 
-       ObjectLock olock(arr);
-       BOOST_FOREACH(const Value& value, arr) {
-               Dictionary::Ptr xlocals = make_shared<Dictionary>();
-               xlocals->Set("__parent", locals);
-               xlocals->Set(varname, value);
+       if (value.IsObjectType<Array>()) {
+               if (!vvar.IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Cannot use dictionary iterator for array.") << errinfo_debuginfo(expr->m_DebugInfo));
 
-               ascope->Evaluate(xlocals, dhint);
-       }
+               Array::Ptr arr = value;
+
+               ObjectLock olock(arr);
+               BOOST_FOREACH(const Value& value, arr) {
+                       Dictionary::Ptr xlocals = make_shared<Dictionary>();
+                       xlocals->Set("__parent", locals);
+                       xlocals->Set(kvar, value);
+
+                       ascope->Evaluate(xlocals, dhint);
+               }
+       } else if (value.IsObjectType<Dictionary>()) {
+               if (vvar.IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Cannot use array iterator for dictionary.") << errinfo_debuginfo(expr->m_DebugInfo));
+
+               Dictionary::Ptr dict = value;
+
+               ObjectLock olock(dict);
+               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
+                       Dictionary::Ptr xlocals = make_shared<Dictionary>();
+                       xlocals->Set("__parent", locals);
+                       xlocals->Set(kvar, kv.first);
+                       xlocals->Set(vvar, kv.second);
+
+                       ascope->Evaluate(xlocals, dhint);
+               }
+       } else
+               BOOST_THROW_EXCEPTION(ConfigError("Invalid type in __for expression: " + value.GetTypeName()) << errinfo_debuginfo(expr->m_DebugInfo));
 
        return Empty;
 }
index 919fc43437b99b487192205476e38884544a29ee..04460ea29142f7f3feb9d1d2f8a861513e63da54 100644 (file)
@@ -42,6 +42,57 @@ void Dependency::RegisterApplyRuleHandler(void)
        ApplyRule::RegisterType("Dependency", targets, &Dependency::EvaluateApplyRules);
 }
 
+void Dependency::EvaluateApplyRuleOneInstance(const Checkable::Ptr& checkable, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule)
+{
+       DebugInfo di = rule.GetDebugInfo();
+
+       Log(LogDebug, "Dependency")
+               << "Applying dependency '" << name << "' to object '" << checkable->GetName() << "' for rule " << di;
+
+       ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+       builder->SetType("Dependency");
+       builder->SetName(name);
+       builder->SetScope(locals);
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       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));
+
+       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));
+       }
+
+       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();
+
+}
+
 bool Dependency::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const ApplyRule& rule)
 {
        DebugInfo di = rule.GetDebugInfo();
@@ -63,69 +114,46 @@ bool Dependency::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const App
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Array::Ptr instances;
+       Value vinstances;
 
        if (rule.GetFTerm()) {
-               Value vinstances = rule.GetFTerm()->Evaluate(locals);
-
-               if (!vinstances.IsObjectType<Array>())
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
-
-               instances = vinstances;
+               vinstances = rule.GetFTerm()->Evaluate(locals);
        } else {
-               instances = make_shared<Array>();
+               Array::Ptr instances = make_shared<Array>();
                instances->Add("");
+               vinstances = instances;
        }
 
-       ObjectLock olock(instances);
-       BOOST_FOREACH(const String& instance, instances) {
-               String objName = rule.GetName();
+       if (vinstances.IsObjectType<Array>()) {
+               if (!rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Array iterator requires value to be an array.") << errinfo_debuginfo(di));
 
-               if (!rule.GetFVar().IsEmpty()) {
-                       locals->Set(rule.GetFVar(), instance);
-                       objName += "-" + instance;
-               }
+               Array::Ptr arr = vinstances;
 
-               Log(LogDebug, "Dependency")
-                       << "Applying dependency '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+               ObjectLock olock(arr);
+               BOOST_FOREACH(const String& instance, arr) {
+                       String name = rule.GetName();
 
-               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
-               builder->SetType("Dependency");
-               builder->SetName(objName);
-               builder->SetScope(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));
-
-               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 (!rule.GetFKVar().IsEmpty()) {
+                               locals->Set(rule.GetFKVar(), instance);
+                               name += instance;
+                       }
 
-               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));
+                       EvaluateApplyRuleOneInstance(checkable, name, locals, rule);
                }
-
-               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));
+       } else if (vinstances.IsObjectType<Dictionary>()) {
+               if (rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di));
+       
+               Dictionary::Ptr dict = vinstances;
+
+               ObjectLock olock(dict);
+               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
+                       locals->Set(rule.GetFKVar(), kv.first);
+                       locals->Set(rule.GetFVVar(), kv.second);
+
+                       EvaluateApplyRuleOneInstance(checkable, rule.GetName() + kv.first, locals, rule);
                }
-
-               builder->AddExpression(rule.GetExpression());
-
-               ConfigItem::Ptr dependencyItem = builder->Compile();
-               dependencyItem->Register();
-               DynamicObject::Ptr dobj = dependencyItem->Commit();
-               dobj->OnConfigLoaded();
        }
 
        return true;
index b6a8e21e6052bd7c688915baa3a822e4dbf3260c..d9c22e64aaad25904cdfe358ee5c827f07d1c7f7 100644 (file)
@@ -60,6 +60,7 @@ private:
        Checkable::Ptr m_Parent;
        Checkable::Ptr m_Child;
 
+       static void EvaluateApplyRuleOneInstance(const Checkable::Ptr& checkable, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule);
        static bool EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const ApplyRule& rule);
        static void EvaluateApplyRule(const ApplyRule& rule);
        static void EvaluateApplyRules(const std::vector<ApplyRule>& rules);
index 06f4791bd8a17de966b71a2b3c1b0b052c38efec..c5b48987ba488135f0c4e966f21178c39e45f68a 100644 (file)
@@ -42,6 +42,52 @@ void Notification::RegisterApplyRuleHandler(void)
        ApplyRule::RegisterType("Notification", targets, &Notification::EvaluateApplyRules);
 }
 
+void Notification::EvaluateApplyRuleOneInstance(const Checkable::Ptr& checkable, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule)
+{
+       DebugInfo di = rule.GetDebugInfo();
+
+       Log(LogDebug, "Notification")
+           << "Applying notification '" << name << "' to object '" << checkable->GetName() << "' for rule " << di;
+
+       ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+       builder->SetType("Notification");
+       builder->SetName(name);
+       builder->SetScope(locals);
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       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 (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();
+
+       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();
+       
+}
+
 bool Notification::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const ApplyRule& rule)
 {
        DebugInfo di = rule.GetDebugInfo();
@@ -63,64 +109,46 @@ bool Notification::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const A
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Array::Ptr instances;
+       Value vinstances;
 
        if (rule.GetFTerm()) {
-               Value vinstances = rule.GetFTerm()->Evaluate(locals);
-
-               if (!vinstances.IsObjectType<Array>())
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
-
-               instances = vinstances;
+               vinstances = rule.GetFTerm()->Evaluate(locals);
        } else {
-               instances = make_shared<Array>();
+               Array::Ptr instances = make_shared<Array>();
                instances->Add("");
+               vinstances = instances;
        }
 
-       ObjectLock olock(instances);
-       BOOST_FOREACH(const String& instance, instances) {
-               String objName = rule.GetName();
-
-               if (!rule.GetFVar().IsEmpty()) {
-                       locals->Set(rule.GetFVar(), instance);
-                       objName += "-" + instance;
-               }
+       if (vinstances.IsObjectType<Array>()) {
+               if (!rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Array iterator requires value to be an array.") << errinfo_debuginfo(di));
 
-               Log(LogDebug, "Notification")
-                       << "Applying notification '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+               Array::Ptr arr = vinstances;
 
-               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
-               builder->SetType("Notification");
-               builder->SetName(objName);
-               builder->SetScope(locals);
+               ObjectLock olock(arr);
+               BOOST_FOREACH(const String& instance, arr) {
+                       String name = rule.GetName();
 
-               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 (!rule.GetFKVar().IsEmpty()) {
+                               locals->Set(rule.GetFKVar(), instance);
+                               name += instance;
+                       }
 
-               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));
+                       EvaluateApplyRuleOneInstance(checkable, name, locals, rule);
                }
-
-               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));
+       } else if (vinstances.IsObjectType<Dictionary>()) {
+               if (rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di));
+       
+               Dictionary::Ptr dict = vinstances;
+
+               ObjectLock olock(dict);
+               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
+                       locals->Set(rule.GetFKVar(), kv.first);
+                       locals->Set(rule.GetFVVar(), kv.second);
+
+                       EvaluateApplyRuleOneInstance(checkable, rule.GetName() + kv.first, locals, rule);
                }
-
-               builder->AddExpression(rule.GetExpression());
-
-               ConfigItem::Ptr notificationItem = builder->Compile();
-               notificationItem->Register();
-               DynamicObject::Ptr dobj = notificationItem->Commit();
-               dobj->OnConfigLoaded();
        }
 
        return true;
index 9202c074c661c3ec84b59e440baff7247a43c8d9..ab3230b27f1aaa58caa8bc1b60a9a764a4a4e582 100644 (file)
@@ -113,6 +113,7 @@ protected:
 private:
        void ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const CheckResult::Ptr& cr, bool force, const String& author = "", const String& text = "");
 
+       static void EvaluateApplyRuleOneInstance(const shared_ptr<Checkable>& checkable, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule);
        static bool EvaluateApplyRuleOne(const shared_ptr<Checkable>& checkable, const ApplyRule& rule);
        static void EvaluateApplyRule(const ApplyRule& rule);
        static void EvaluateApplyRules(const std::vector<ApplyRule>& rules);
index 9251cd6c0bf16861994a58f9ffd6ccb842e323cf..575ad603ef3655cebcaf74760e6f772b780d73c2 100644 (file)
@@ -41,7 +41,52 @@ void ScheduledDowntime::RegisterApplyRuleHandler(void)
        ApplyRule::RegisterType("ScheduledDowntime", targets, &ScheduledDowntime::EvaluateApplyRules);
 }
 
-bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule)
+void ScheduledDowntime::EvaluateApplyRuleOneInstance(const Checkable::Ptr& checkable, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule)
+{
+       DebugInfo di = rule.GetDebugInfo();
+
+       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(name);
+       builder->SetScope(locals);
+
+       Host::Ptr host;
+       Service::Ptr service;
+       tie(host, service) = GetHostService(checkable);
+
+       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 (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();
+
+       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 downtimeItem = builder->Compile();
+       downtimeItem->Register();
+       DynamicObject::Ptr dobj = downtimeItem->Commit();
+       dobj->OnConfigLoaded();
+}
+
+bool ScheduledDowntime::EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const ApplyRule& rule)
 {
        DebugInfo di = rule.GetDebugInfo();
 
@@ -62,115 +107,106 @@ bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Array::Ptr instances;
+       Value vinstances;
 
        if (rule.GetFTerm()) {
-               Value vinstances = rule.GetFTerm()->Evaluate(locals);
-
-               if (!vinstances.IsObjectType<Array>())
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
-
-               instances = vinstances;
+               vinstances = rule.GetFTerm()->Evaluate(locals);
        } else {
-               instances = make_shared<Array>();
+               Array::Ptr instances = make_shared<Array>();
                instances->Add("");
+               vinstances = instances;
        }
 
-       ObjectLock olock(instances);
-       BOOST_FOREACH(const String& instance, instances) {
-               String objName = rule.GetName();
-
-               if (!rule.GetFVar().IsEmpty()) {
-                       locals->Set(rule.GetFVar(), instance);
-                       objName += "-" + instance;
-               }
+       if (vinstances.IsObjectType<Array>()) {
+               if (!rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Array iterator requires value to be an array.") << errinfo_debuginfo(di));
 
-               Log(LogDebug, "ScheduledDowntime")
-                       << "Applying scheduled downtime '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di;
+               Array::Ptr arr = vinstances;
 
-               ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
-               builder->SetType("ScheduledDowntime");
-               builder->SetName(objName);
-               builder->SetScope(locals);
+               ObjectLock olock(arr);
+               BOOST_FOREACH(const String& instance, arr) {
+                       String name = rule.GetName();
 
-               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 (!rule.GetFKVar().IsEmpty()) {
+                               locals->Set(rule.GetFKVar(), instance);
+                               name += instance;
+                       }
 
-               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));
+                       EvaluateApplyRuleOneInstance(checkable, name, locals, rule);
                }
-
-               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));
+       } else if (vinstances.IsObjectType<Dictionary>()) {
+               if (rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di));
+       
+               Dictionary::Ptr dict = vinstances;
+
+               ObjectLock olock(dict);
+               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
+                       locals->Set(rule.GetFKVar(), kv.first);
+                       locals->Set(rule.GetFVVar(), kv.second);
+
+                       EvaluateApplyRuleOneInstance(checkable, rule.GetName() + kv.first, locals, rule);
                }
-
-               builder->AddExpression(rule.GetExpression());
-
-               ConfigItem::Ptr downtimeItem = builder->Compile();
-               downtimeItem->Register();
-               DynamicObject::Ptr dobj = downtimeItem->Commit();
-               dobj->OnConfigLoaded();
        }
 
        return true;
 }
 
-void ScheduledDowntime::EvaluateApplyRules(const std::vector<ApplyRule>& rules)
+void ScheduledDowntime::EvaluateApplyRule(const ApplyRule& rule)
 {
        int apply_count = 0;
 
-       BOOST_FOREACH(const ApplyRule& rule, rules) {
-               if (rule.GetTargetType() == "Host") {
-                       apply_count = 0;
-
-                       BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjectsByType<Host>()) {
-                               CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'");
-
-                               try {
-                                       if (EvaluateApplyRule(host, rule))
-                                               apply_count++;
-                               } catch (const ConfigError& ex) {
-                                       const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
-                                       ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
-                               }
+       if (rule.GetTargetType() == "Host") {
+               apply_count = 0;
+
+               BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjectsByType<Host>()) {
+                       CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'");
+
+                       try {
+                               if (EvaluateApplyRuleOne(host, rule))
+                                       apply_count++;
+                       } catch (const ConfigError& ex) {
+                               const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
+                               ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
                        }
+               }
 
-                       if (apply_count == 0)
-                               Log(LogWarning, "ScheduledDowntime")
-                                   << "Apply rule '" << rule.GetName() << "' for host does not match anywhere!";
+               if (apply_count == 0)
+                       Log(LogWarning, "ScheduledDowntime")
+                           << "Apply rule '" << rule.GetName() << "' for host does not match anywhere!";
 
-               } else if (rule.GetTargetType() == "Service") {
-                       apply_count = 0;
+       } else if (rule.GetTargetType() == "Service") {
+               apply_count = 0;
 
-                       BOOST_FOREACH(const Service::Ptr& service, DynamicType::GetObjectsByType<Service>()) {
-                               CONTEXT("Evaluating 'apply' rules for Service '" + service->GetName() + "'");
+               BOOST_FOREACH(const Service::Ptr& service, DynamicType::GetObjectsByType<Service>()) {
+                       CONTEXT("Evaluating 'apply' rules for Service '" + service->GetName() + "'");
 
-                               try {
-                                       if(EvaluateApplyRule(service, rule))
-                                               apply_count++;
-                               } catch (const ConfigError& ex) {
-                                       const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
-                                       ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
-                               }
+                       try {
+                               if(EvaluateApplyRuleOne(service, rule))
+                                       apply_count++;
+                       } catch (const ConfigError& ex) {
+                               const DebugInfo *di = boost::get_error_info<errinfo_debuginfo>(ex);
+                               ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo());
                        }
+               }
 
-                       if (apply_count == 0)
-                               Log(LogWarning, "ScheduledDowntime")
-                                   << "Apply rule '" << rule.GetName() << "' for service does not match anywhere!";
-
-               } else {
+               if (apply_count == 0)
                        Log(LogWarning, "ScheduledDowntime")
-                           << "Wrong target type for apply rule '" << rule.GetName() << "'!";
-               }
+                           << "Apply rule '" << rule.GetName() << "' for service does not match anywhere!";
+
+       } else {
+               Log(LogWarning, "ScheduledDowntime")
+                   << "Wrong target type for apply rule '" << rule.GetName() << "'!";
        }
 }
+
+void ScheduledDowntime::EvaluateApplyRules(const std::vector<ApplyRule>& rules)
+{
+       ParallelWorkQueue upq;
+
+       BOOST_FOREACH(const ApplyRule& rule, rules) {
+               upq.Enqueue(boost::bind(&ScheduledDowntime::EvaluateApplyRule, boost::cref(rule)));
+       }
+
+       upq.Join();
+}
index abc90f0781526fe85e07ca61f9dc2883e73e1335..7683f445b8fa86438106871a628b84183f59cc9f 100644 (file)
@@ -56,7 +56,9 @@ private:
        std::pair<double, double> FindNextSegment(void);
        void CreateNextDowntime(void);
 
-       static bool EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule);
+       static void EvaluateApplyRuleOneInstance(const Checkable::Ptr& checkable, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule);
+       static bool EvaluateApplyRuleOne(const Checkable::Ptr& checkable, const ApplyRule& rule);
+       static void EvaluateApplyRule(const ApplyRule& rule);
        static void EvaluateApplyRules(const std::vector<ApplyRule>& rules);
 };
 
index 5cf08ec3a56b7e787fbd5d265ed5d37994bd65f5..a2facbff814b4d6e6395ffd7650dfb6ce2888747 100644 (file)
@@ -40,6 +40,45 @@ void Service::RegisterApplyRuleHandler(void)
        ApplyRule::RegisterType("Service", targets, &Service::EvaluateApplyRules);
 }
 
+void Service::EvaluateApplyRuleOneInstance(const Host::Ptr& host, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule)
+{
+       DebugInfo di = rule.GetDebugInfo();
+
+       Log(LogDebug, "Service")
+           << "Applying service '" << name << "' to host '" << host->GetName() << "' for rule " << di;
+
+       ConfigItemBuilder::Ptr builder = make_shared<ConfigItemBuilder>(di);
+       builder->SetType("Service");
+       builder->SetName(name);
+       builder->SetScope(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));
+
+       builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
+           make_shared<Expression>(&Expression::OpLiteral, "name", di),
+           make_shared<Expression>(&Expression::OpLiteral, name, 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();
+}
+
 bool Service::EvaluateApplyRuleOne(const Host::Ptr& host, const ApplyRule& rule)
 {
        DebugInfo di = rule.GetDebugInfo();
@@ -55,62 +94,46 @@ bool Service::EvaluateApplyRuleOne(const Host::Ptr& host, const ApplyRule& rule)
        if (!rule.EvaluateFilter(locals))
                return false;
 
-       Array::Ptr instances;
+       Value vinstances;
 
        if (rule.GetFTerm()) {
-               Value vinstances = rule.GetFTerm()->Evaluate(locals);
-
-               if (!vinstances.IsObjectType<Array>())
-                       BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array"));
-
-               instances = vinstances;
+               vinstances = rule.GetFTerm()->Evaluate(locals);
        } else {
-               instances = make_shared<Array>();
+               Array::Ptr instances = make_shared<Array>();
                instances->Add("");
+               vinstances = instances;
        }
 
-       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, "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 (vinstances.IsObjectType<Array>()) {
+               if (!rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Array iterator requires value to be an array.") << errinfo_debuginfo(di));
 
-               builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "host_name", di),
-                   make_shared<Expression>(&Expression::OpLiteral, host->GetName(), di),
-                   di));
+               Array::Ptr arr = vinstances;
 
-               builder->AddExpression(make_shared<Expression>(&Expression::OpSet,
-                   make_shared<Expression>(&Expression::OpLiteral, "name", di),
-                   make_shared<Expression>(&Expression::OpLiteral, objName, di),
-                   di));
+               ObjectLock olock(arr);
+               BOOST_FOREACH(const String& instance, arr) {
+                       String name = rule.GetName();
 
-               String zone = host->GetZone();
+                       if (!rule.GetFKVar().IsEmpty()) {
+                               locals->Set(rule.GetFKVar(), instance);
+                               name += instance;
+                       }
 
-               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));
+                       EvaluateApplyRuleOneInstance(host, name, locals, rule);
+               }
+       } else if (vinstances.IsObjectType<Dictionary>()) {
+               if (rule.GetFVVar().IsEmpty())
+                       BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di));
+       
+               Dictionary::Ptr dict = vinstances;
+
+               ObjectLock olock(dict);
+               BOOST_FOREACH(const Dictionary::Pair& kv, dict) {
+                       locals->Set(rule.GetFKVar(), kv.first);
+                       locals->Set(rule.GetFVVar(), kv.second);
+
+                       EvaluateApplyRuleOneInstance(host, rule.GetName() + kv.first, locals, rule);
                }
-
-               builder->AddExpression(rule.GetExpression());
-
-               ConfigItem::Ptr serviceItem = builder->Compile();
-               serviceItem->Register();
-               DynamicObject::Ptr dobj = serviceItem->Commit();
-               dobj->OnConfigLoaded();
        }
 
        return true;
index 2b51e9406c5056a60ec5c2ffea2afec8fb8b7583..36fb1ea6eb76bb0996a7846cae6deef93c3774e9 100644 (file)
@@ -59,6 +59,7 @@ protected:
 private:
        Host::Ptr m_Host;
 
+       static void EvaluateApplyRuleOneInstance(const Host::Ptr& host, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule);
        static bool EvaluateApplyRuleOne(const Host::Ptr& host, const ApplyRule& rule);
        static void EvaluateApplyRule(const ApplyRule& rule);
        static void EvaluateApplyRules(const std::vector<ApplyRule>& rules);