From a1c905bf6267e83c875e2b0cba51ac337b397873 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Tue, 4 Nov 2014 11:01:00 +0100 Subject: [PATCH] Implement dictionary support for apply+for fixes #7561 --- lib/config/applyrule.cpp | 21 ++- lib/config/applyrule.hpp | 10 +- lib/config/base-type.conf | 7 +- lib/config/config_lexer.ll | 2 + lib/config/config_parser.yy | 72 +++++++-- lib/config/expression.cpp | 51 ++++-- lib/icinga/dependency-apply.cpp | 132 +++++++++------ lib/icinga/dependency.hpp | 1 + lib/icinga/notification-apply.cpp | 122 ++++++++------ lib/icinga/notification.hpp | 1 + lib/icinga/scheduleddowntime-apply.cpp | 212 +++++++++++++++---------- lib/icinga/scheduleddowntime.hpp | 4 +- lib/icinga/service-apply.cpp | 113 +++++++------ lib/icinga/service.hpp | 1 + 14 files changed, 476 insertions(+), 273 deletions(-) diff --git a/lib/config/applyrule.cpp b/lib/config/applyrule.cpp index 037317e03..a8f24f720 100644 --- a/lib/config/applyrule.cpp +++ b/lib/config/applyrule.cpp @@ -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 diff --git a/lib/config/applyrule.hpp b/lib/config/applyrule.hpp index 13fabd55e..1a5375fab 100644 --- a/lib/config/applyrule.hpp +++ b/lib/config/applyrule.hpp @@ -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& 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); }; diff --git a/lib/config/base-type.conf b/lib/config/base-type.conf index f5cfe0b37..542026eec 100644 --- a/lib/config/base-type.conf +++ b/lib/config/base-type.conf @@ -32,12 +32,7 @@ %attribute %dictionary "methods", - %attribute %dictionary "vars" { - %attribute %string "*", - %attribute %array "*" { - %attribute %string "*" - } - }, + %attribute %dictionary "vars" } %type Logger { diff --git a/lib/config/config_lexer.ll b/lib/config/config_lexer.ll index 87691dd33..42868e71b 100644 --- a/lib/config/config_lexer.ll +++ b/lib/config/config_lexer.ll @@ -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; } diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 9f10adcf2..5f7bff67a 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -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 identifier %type rterm_items @@ -177,6 +179,7 @@ static void MakeRBinaryOp(Value** result, Expression::OpCallback& op, Value *lef %type lterm %type object %type apply +%type optional_rterm %type target_type_specifier %left T_LOGICAL_OR @@ -218,7 +221,8 @@ static std::stack m_ObjectAssign; static std::stack m_SeenAssign; static std::stack m_Assign; static std::stack m_Ignore; -static std::stack m_FVar; +static std::stack m_FKVar; +static std::stack m_FVVar; static std::stack m_FTerm; void ConfigCompiler::Compile(void) @@ -233,7 +237,8 @@ void ConfigCompiler::Compile(void) m_SeenAssign = std::stack(); m_Assign = std::stack(); m_Ignore = std::stack(); - m_FVar = std::stack(); + m_FKVar = std::stack(); + m_FVVar = std::stack(); m_FTerm = std::stack(); try { @@ -812,6 +817,25 @@ rterm: T_STRING $$ = new Value(make_shared(&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(); + + 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::OpFor, arr, ascope, DebugInfoRange(@1, @9))); + } | T_FOR '(' identifier T_IN rterm ')' rterm_scope { Array::Ptr arr = make_shared(); @@ -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_FVar.top() = $3; + 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_FKVar.top() = $3; + free($3); + + m_FVVar.top() = ""; + m_FTerm.top() = *$5; delete $5; } ; +optional_rterm: /* empty */ + { + $$ = new Value(make_shared(&Expression::OpLiteral, Empty, DebugInfo())); + } + | rterm + { + $$ = $1; + } + ; + apply: { m_Apply.push(true); m_SeenAssign.push(false); m_Assign.push(make_shared(&Expression::OpLiteral, false, DebugInfo())); m_Ignore.push(make_shared(&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::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::OpApply, args, exprl, DebugInfoRange(@2, @5))); diff --git a/lib/config/expression.cpp b/lib/config/expression.cpp index 538aad6a1..84537777b 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -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(); - xlocals->Set("__parent", locals); - xlocals->Set(varname, value); + if (value.IsObjectType()) { + 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(); + xlocals->Set("__parent", locals); + xlocals->Set(kvar, value); + + ascope->Evaluate(xlocals, dhint); + } + } else if (value.IsObjectType()) { + 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(); + 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; } diff --git a/lib/icinga/dependency-apply.cpp b/lib/icinga/dependency-apply.cpp index 919fc4343..04460ea29 100644 --- a/lib/icinga/dependency-apply.cpp +++ b/lib/icinga/dependency-apply.cpp @@ -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(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::OpSet, + make_shared(&Expression::OpLiteral, "parent_host_name", di), + make_shared(&Expression::OpLiteral, host->GetName(), di), + di)); + + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "child_host_name", di), + make_shared(&Expression::OpLiteral, host->GetName(), di), + di)); + + if (service) { + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "child_service_name", di), + make_shared(&Expression::OpLiteral, service->GetShortName(), di), + di)); + } + + String zone = checkable->GetZone(); + + if (!zone.IsEmpty()) { + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "zone", di), + make_shared(&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()) - BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - - instances = vinstances; + vinstances = rule.GetFTerm()->Evaluate(locals); } else { - instances = make_shared(); + Array::Ptr instances = make_shared(); instances->Add(""); + vinstances = instances; } - ObjectLock olock(instances); - BOOST_FOREACH(const String& instance, instances) { - String objName = rule.GetName(); + if (vinstances.IsObjectType()) { + 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; + + ObjectLock olock(arr); + BOOST_FOREACH(const String& instance, arr) { + String name = rule.GetName(); + + if (!rule.GetFKVar().IsEmpty()) { + locals->Set(rule.GetFKVar(), instance); + name += instance; + } + + EvaluateApplyRuleOneInstance(checkable, name, locals, rule); } + } else if (vinstances.IsObjectType()) { + if (rule.GetFVVar().IsEmpty()) + BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di)); + + Dictionary::Ptr dict = vinstances; - Log(LogDebug, "Dependency") - << "Applying dependency '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di; + ObjectLock olock(dict); + BOOST_FOREACH(const Dictionary::Pair& kv, dict) { + locals->Set(rule.GetFKVar(), kv.first); + locals->Set(rule.GetFVVar(), kv.second); - ConfigItemBuilder::Ptr builder = make_shared(di); - builder->SetType("Dependency"); - builder->SetName(objName); - builder->SetScope(locals); - - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "parent_host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); - - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "child_host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); - - if (service) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "child_service_name", di), - make_shared(&Expression::OpLiteral, service->GetShortName(), di), - di)); + EvaluateApplyRuleOneInstance(checkable, rule.GetName() + kv.first, locals, rule); } - - String zone = checkable->GetZone(); - - if (!zone.IsEmpty()) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "zone", di), - make_shared(&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; diff --git a/lib/icinga/dependency.hpp b/lib/icinga/dependency.hpp index b6a8e21e6..d9c22e64a 100644 --- a/lib/icinga/dependency.hpp +++ b/lib/icinga/dependency.hpp @@ -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& rules); diff --git a/lib/icinga/notification-apply.cpp b/lib/icinga/notification-apply.cpp index 06f4791bd..c5b48987b 100644 --- a/lib/icinga/notification-apply.cpp +++ b/lib/icinga/notification-apply.cpp @@ -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(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::OpSet, + make_shared(&Expression::OpLiteral, "host_name", di), + make_shared(&Expression::OpLiteral, host->GetName(), di), + di)); + + if (service) { + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "service_name", di), + make_shared(&Expression::OpLiteral, service->GetShortName(), di), + di)); + } + + String zone = checkable->GetZone(); + + if (!zone.IsEmpty()) { + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "zone", di), + make_shared(&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()) - BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - - instances = vinstances; + vinstances = rule.GetFTerm()->Evaluate(locals); } else { - instances = make_shared(); + Array::Ptr instances = make_shared(); instances->Add(""); + vinstances = instances; } - ObjectLock olock(instances); - BOOST_FOREACH(const String& instance, instances) { - String objName = rule.GetName(); + if (vinstances.IsObjectType()) { + 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; + + ObjectLock olock(arr); + BOOST_FOREACH(const String& instance, arr) { + String name = rule.GetName(); + + if (!rule.GetFKVar().IsEmpty()) { + locals->Set(rule.GetFKVar(), instance); + name += instance; + } + + EvaluateApplyRuleOneInstance(checkable, name, locals, rule); } + } else if (vinstances.IsObjectType()) { + if (rule.GetFVVar().IsEmpty()) + BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di)); + + Dictionary::Ptr dict = vinstances; - Log(LogDebug, "Notification") - << "Applying notification '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di; + ObjectLock olock(dict); + BOOST_FOREACH(const Dictionary::Pair& kv, dict) { + locals->Set(rule.GetFKVar(), kv.first); + locals->Set(rule.GetFVVar(), kv.second); - ConfigItemBuilder::Ptr builder = make_shared(di); - builder->SetType("Notification"); - builder->SetName(objName); - builder->SetScope(locals); - - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); - - if (service) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "service_name", di), - make_shared(&Expression::OpLiteral, service->GetShortName(), di), - di)); + EvaluateApplyRuleOneInstance(checkable, rule.GetName() + kv.first, locals, rule); } - - String zone = checkable->GetZone(); - - if (!zone.IsEmpty()) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "zone", di), - make_shared(&Expression::OpLiteral, zone, di), - di)); - } - - builder->AddExpression(rule.GetExpression()); - - ConfigItem::Ptr notificationItem = builder->Compile(); - notificationItem->Register(); - DynamicObject::Ptr dobj = notificationItem->Commit(); - dobj->OnConfigLoaded(); } return true; diff --git a/lib/icinga/notification.hpp b/lib/icinga/notification.hpp index 9202c074c..ab3230b27 100644 --- a/lib/icinga/notification.hpp +++ b/lib/icinga/notification.hpp @@ -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, const String& name, const Dictionary::Ptr& locals, const ApplyRule& rule); static bool EvaluateApplyRuleOne(const shared_ptr& checkable, const ApplyRule& rule); static void EvaluateApplyRule(const ApplyRule& rule); static void EvaluateApplyRules(const std::vector& rules); diff --git a/lib/icinga/scheduleddowntime-apply.cpp b/lib/icinga/scheduleddowntime-apply.cpp index 9251cd6c0..575ad603e 100644 --- a/lib/icinga/scheduleddowntime-apply.cpp +++ b/lib/icinga/scheduleddowntime-apply.cpp @@ -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(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::OpSet, + make_shared(&Expression::OpLiteral, "host_name", di), + make_shared(&Expression::OpLiteral, host->GetName(), di), + di)); + + if (service) { + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "service_name", di), + make_shared(&Expression::OpLiteral, service->GetShortName(), di), + di)); + } + + String zone = checkable->GetZone(); + + if (!zone.IsEmpty()) { + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "zone", di), + make_shared(&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()) - BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - - instances = vinstances; + vinstances = rule.GetFTerm()->Evaluate(locals); } else { - instances = make_shared(); + Array::Ptr instances = make_shared(); instances->Add(""); + vinstances = instances; } - ObjectLock olock(instances); - BOOST_FOREACH(const String& instance, instances) { - String objName = rule.GetName(); + if (vinstances.IsObjectType()) { + 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; + + ObjectLock olock(arr); + BOOST_FOREACH(const String& instance, arr) { + String name = rule.GetName(); + + if (!rule.GetFKVar().IsEmpty()) { + locals->Set(rule.GetFKVar(), instance); + name += instance; + } + + EvaluateApplyRuleOneInstance(checkable, name, locals, rule); } + } else if (vinstances.IsObjectType()) { + if (rule.GetFVVar().IsEmpty()) + BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di)); + + Dictionary::Ptr dict = vinstances; - Log(LogDebug, "ScheduledDowntime") - << "Applying scheduled downtime '" << rule.GetName() << "' to object '" << checkable->GetName() << "' for rule " << di; + ObjectLock olock(dict); + BOOST_FOREACH(const Dictionary::Pair& kv, dict) { + locals->Set(rule.GetFKVar(), kv.first); + locals->Set(rule.GetFVVar(), kv.second); - ConfigItemBuilder::Ptr builder = make_shared(di); - builder->SetType("ScheduledDowntime"); - builder->SetName(objName); - builder->SetScope(locals); - - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); - - if (service) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "service_name", di), - make_shared(&Expression::OpLiteral, service->GetShortName(), di), - di)); + EvaluateApplyRuleOneInstance(checkable, rule.GetName() + kv.first, locals, rule); } - - String zone = checkable->GetZone(); - - if (!zone.IsEmpty()) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "zone", di), - make_shared(&Expression::OpLiteral, zone, di), - di)); - } - - 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& rules) +void ScheduledDowntime::EvaluateApplyRule(const ApplyRule& rule) { int apply_count = 0; - BOOST_FOREACH(const ApplyRule& rule, rules) { - if (rule.GetTargetType() == "Host") { - apply_count = 0; + if (rule.GetTargetType() == "Host") { + apply_count = 0; - BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjectsByType()) { - CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'"); + BOOST_FOREACH(const Host::Ptr& host, DynamicType::GetObjectsByType()) { + 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(ex); - ConfigCompilerContext::GetInstance()->AddMessage(true, ex.what(), di ? *di : DebugInfo()); - } + try { + if (EvaluateApplyRuleOne(host, rule)) + apply_count++; + } catch (const ConfigError& ex) { + const DebugInfo *di = boost::get_error_info(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!"; - - } else if (rule.GetTargetType() == "Service") { - apply_count = 0; - - BOOST_FOREACH(const Service::Ptr& service, DynamicType::GetObjectsByType()) { - 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(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 { - Log(LogWarning, "ScheduledDowntime") - << "Wrong target type for apply rule '" << rule.GetName() << "'!"; } + + 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; + + BOOST_FOREACH(const Service::Ptr& service, DynamicType::GetObjectsByType()) { + CONTEXT("Evaluating 'apply' rules for Service '" + service->GetName() + "'"); + + try { + if(EvaluateApplyRuleOne(service, rule)) + apply_count++; + } catch (const ConfigError& ex) { + const DebugInfo *di = boost::get_error_info(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 { + Log(LogWarning, "ScheduledDowntime") + << "Wrong target type for apply rule '" << rule.GetName() << "'!"; } } + +void ScheduledDowntime::EvaluateApplyRules(const std::vector& rules) +{ + ParallelWorkQueue upq; + + BOOST_FOREACH(const ApplyRule& rule, rules) { + upq.Enqueue(boost::bind(&ScheduledDowntime::EvaluateApplyRule, boost::cref(rule))); + } + + upq.Join(); +} diff --git a/lib/icinga/scheduleddowntime.hpp b/lib/icinga/scheduleddowntime.hpp index abc90f078..7683f445b 100644 --- a/lib/icinga/scheduleddowntime.hpp +++ b/lib/icinga/scheduleddowntime.hpp @@ -56,7 +56,9 @@ private: std::pair 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& rules); }; diff --git a/lib/icinga/service-apply.cpp b/lib/icinga/service-apply.cpp index 5cf08ec3a..a2facbff8 100644 --- a/lib/icinga/service-apply.cpp +++ b/lib/icinga/service-apply.cpp @@ -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(di); + builder->SetType("Service"); + builder->SetName(name); + builder->SetScope(locals); + + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "host_name", di), + make_shared(&Expression::OpLiteral, host->GetName(), di), + di)); + + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "name", di), + make_shared(&Expression::OpLiteral, name, di), + di)); + + String zone = host->GetZone(); + + if (!zone.IsEmpty()) { + builder->AddExpression(make_shared(&Expression::OpSet, + make_shared(&Expression::OpLiteral, "zone", di), + make_shared(&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()) - BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - - instances = vinstances; + vinstances = rule.GetFTerm()->Evaluate(locals); } else { - instances = make_shared(); + Array::Ptr instances = make_shared(); instances->Add(""); + vinstances = instances; } - ObjectLock olock(instances); - BOOST_FOREACH(const String& instance, instances) { - String objName = rule.GetName(); + if (vinstances.IsObjectType()) { + 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; + + ObjectLock olock(arr); + BOOST_FOREACH(const String& instance, arr) { + String name = rule.GetName(); + + if (!rule.GetFKVar().IsEmpty()) { + locals->Set(rule.GetFKVar(), instance); + name += instance; + } + + EvaluateApplyRuleOneInstance(host, name, locals, rule); } + } else if (vinstances.IsObjectType()) { + if (rule.GetFVVar().IsEmpty()) + BOOST_THROW_EXCEPTION(ConfigError("Dictionary iterator requires value to be a dictionary.") << errinfo_debuginfo(di)); + + Dictionary::Ptr dict = vinstances; - Log(LogDebug, "Service") - << "Applying service '" << objName << "' to host '" << host->GetName() << "' for rule " << di; + ObjectLock olock(dict); + BOOST_FOREACH(const Dictionary::Pair& kv, dict) { + locals->Set(rule.GetFKVar(), kv.first); + locals->Set(rule.GetFVVar(), kv.second); - ConfigItemBuilder::Ptr builder = make_shared(di); - builder->SetType("Service"); - builder->SetName(objName); - builder->SetScope(locals); - - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); - - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "name", di), - make_shared(&Expression::OpLiteral, objName, di), - di)); - - String zone = host->GetZone(); - - if (!zone.IsEmpty()) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "zone", di), - make_shared(&Expression::OpLiteral, zone, di), - di)); + 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; diff --git a/lib/icinga/service.hpp b/lib/icinga/service.hpp index 2b51e9406..36fb1ea6e 100644 --- a/lib/icinga/service.hpp +++ b/lib/icinga/service.hpp @@ -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& rules);