From 4677014b6de5e2e1d2292ba5670dce4f4766d66f Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Sun, 2 Nov 2014 07:22:00 +0100 Subject: [PATCH] Implement support for arrays in custom variables fixes #6544 --- lib/base/utility.cpp | 31 +++++++++ lib/base/utility.hpp | 2 + lib/compat/statusdatawriter.cpp | 20 ++++-- lib/config/applyrule.cpp | 22 ++++-- lib/config/applyrule.hpp | 9 ++- lib/config/base-type.conf | 5 +- lib/config/config_parser.yy | 35 ++++++++-- lib/config/expression.cpp | 4 +- lib/db_ido/dbobject.cpp | 94 ++++++++++++++------------ lib/icinga/dependency-apply.cpp | 88 +++++++++++++++--------- lib/icinga/notification-apply.cpp | 78 +++++++++++++-------- lib/icinga/scheduleddowntime-apply.cpp | 78 +++++++++++++-------- lib/icinga/service-apply.cpp | 78 +++++++++++++-------- 13 files changed, 368 insertions(+), 176 deletions(-) diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index a834df587..d96139442 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -25,12 +25,14 @@ #include "base/socket.hpp" #include "base/utility.hpp" #include "base/json.hpp" +#include "base/objectlock.hpp" #include #include #include #include #include #include +#include #include #include #include @@ -772,6 +774,35 @@ String Utility::NaturalJoin(const std::vector& 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 tokens; diff --git a/lib/base/utility.hpp b/lib/base/utility.hpp index 126b41bdb..ec435d7fc 100644 --- a/lib/base/utility.hpp +++ b/lib/base/utility.hpp @@ -22,6 +22,7 @@ #include "base/i2-base.hpp" #include "base/string.hpp" +#include "base/array.hpp" #include #include #include @@ -96,6 +97,7 @@ public: static void QueueAsyncCallback(const boost::function& callback, SchedulerPolicy policy = DefaultScheduler); static String NaturalJoin(const std::vector& tokens); + static String Join(const Array::Ptr& tokens, char separator); static String FormatDuration(double duration); static String FormatDateTime(const char *format, double ts); diff --git a/lib/compat/statusdatawriter.cpp b/lib/compat/statusdatawriter.cpp index 4db6397c4..73f5e48be 100644 --- a/lib/compat/statusdatawriter.cpp +++ b/lib/compat/statusdatawriter.cpp @@ -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()) + value = Utility::Join(kv.second, ';'); + else + value = kv.second; + + fp << "\t"; + + if (!CompatUtility::IsLegacyAttribute(object, kv.first)) + fp << "_"; + + fp << kv.first << "\t" << value << "\n"; } } diff --git a/lib/config/applyrule.cpp b/lib/config/applyrule.cpp index 11870e70e..037317e03 100644 --- a/lib/config/applyrule.cpp +++ b/lib/config/applyrule.cpp @@ -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 diff --git a/lib/config/applyrule.hpp b/lib/config/applyrule.hpp index b7590f180..13fabd55e 100644 --- a/lib/config/applyrule.hpp +++ b/lib/config/applyrule.hpp @@ -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& 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); }; } diff --git a/lib/config/base-type.conf b/lib/config/base-type.conf index 1e689ff2d..f5cfe0b37 100644 --- a/lib/config/base-type.conf +++ b/lib/config/base-type.conf @@ -33,7 +33,10 @@ %attribute %dictionary "methods", %attribute %dictionary "vars" { - %attribute %string "*" + %attribute %string "*", + %attribute %array "*" { + %attribute %string "*" + } }, } diff --git a/lib/config/config_parser.yy b/lib/config/config_parser.yy index 72bcc7ba5..9f10adcf2 100644 --- a/lib/config/config_parser.yy +++ b/lib/config/config_parser.yy @@ -218,6 +218,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_FTerm; void ConfigCompiler::Compile(void) { @@ -231,6 +233,8 @@ void ConfigCompiler::Compile(void) m_SeenAssign = std::stack(); m_Assign = std::stack(); m_Ignore = std::stack(); + m_FVar = std::stack(); + m_FTerm = std::stack(); 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::OpLiteral, false, DebugInfo())); m_Ignore.push(make_shared(&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::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(); args->Add(type); args->Add(target); args->Add(aname); args->Add(filter); + args->Add(fvar); + 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 a8d4513c9..234a04526 100644 --- a/lib/config/expression.cpp +++ b/lib/config/expression.cpp @@ -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; } diff --git a/lib/db_ido/dbobject.cpp b/lib/db_ido/dbobject.cpp index d276513c9..7f3f3c47b 100644 --- a/lib/db_ido/dbobject.cpp +++ b/lib/db_ido/dbobject.cpp @@ -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; + if (kv.first.IsEmpty()) + continue; - Log(LogDebug, "DbObject") - << "object customvar key: '" << kv.first << "' value: '" << kv.second - << "' overridden: " << overridden; + String value; - Dictionary::Ptr fields = make_shared(); - 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 */ + if (kv.second.IsObjectType()) + value = Utility::Join(kv.second, ';'); + else + value = kv.second; - DbQuery query; - query.Table = "customvariables"; - query.Type = DbQueryInsert; - query.Category = DbCatConfig; - query.Fields = fields; - OnQuery(query); - } + 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(); + 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; + if (kv.first.IsEmpty() || kv.second.IsObject()) + continue; - Log(LogDebug, "DbObject") - << "object customvar key: '" << kv.first << "' value: '" << kv.second - << "' overridden: " << overridden; + int overridden = custom_var_object->IsVarOverridden(kv.first) ? 1 : 0; - Dictionary::Ptr fields = make_shared(); - 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 */ + Log(LogDebug, "DbObject") + << "object customvar key: '" << kv.first << "' value: '" << kv.second + << "' overridden: " << overridden; - DbQuery query; - query.Table = "customvariablestatus"; - query.Type = DbQueryInsert | DbQueryUpdate; - query.Category = DbCatState; - query.Fields = fields; + Dictionary::Ptr fields = make_shared(); + 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 */ - query.WhereCriteria = make_shared(); - query.WhereCriteria->Set("object_id", obj); - query.WhereCriteria->Set("varname", Convert::ToString(kv.first)); - query.Object = GetSelf(); + DbQuery query; + query.Table = "customvariablestatus"; + query.Type = DbQueryInsert | DbQueryUpdate; + query.Category = DbCatState; + query.Fields = fields; - OnQuery(query); - } + query.WhereCriteria = make_shared(); + query.WhereCriteria->Set("object_id", obj); + query.WhereCriteria->Set("varname", Convert::ToString(kv.first)); + query.Object = GetSelf(); + + OnQuery(query); } - } } diff --git a/lib/icinga/dependency-apply.cpp b/lib/icinga/dependency-apply.cpp index 7969d3484..919fc4343 100644 --- a/lib/icinga/dependency-apply.cpp +++ b/lib/icinga/dependency-apply.cpp @@ -63,47 +63,71 @@ 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(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::OpSet, - make_shared(&Expression::OpLiteral, "parent_host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); + if (!vinstances.IsObjectType()) + BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - 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)); + instances = vinstances; + } else { + instances = make_shared(); + 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(di); + builder->SetType("Dependency"); + builder->SetName(objName); + builder->SetScope(locals); - if (!zone.IsEmpty()) { builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "zone", di), - make_shared(&Expression::OpLiteral, zone, di), - di)); + 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(); } - 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/notification-apply.cpp b/lib/icinga/notification-apply.cpp index 7715bf40e..06f4791bd 100644 --- a/lib/icinga/notification-apply.cpp +++ b/lib/icinga/notification-apply.cpp @@ -63,42 +63,66 @@ 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(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::OpSet, - make_shared(&Expression::OpLiteral, "host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); + if (!vinstances.IsObjectType()) + BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - if (service) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "service_name", di), - make_shared(&Expression::OpLiteral, service->GetShortName(), di), - di)); + instances = vinstances; + } else { + instances = make_shared(); + 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(di); + builder->SetType("Notification"); + builder->SetName(objName); + builder->SetScope(locals); - if (!zone.IsEmpty()) { builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "zone", di), - make_shared(&Expression::OpLiteral, zone, di), - di)); + 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(); } - 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/scheduleddowntime-apply.cpp b/lib/icinga/scheduleddowntime-apply.cpp index 8774ed86c..9251cd6c0 100644 --- a/lib/icinga/scheduleddowntime-apply.cpp +++ b/lib/icinga/scheduleddowntime-apply.cpp @@ -62,42 +62,66 @@ 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(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::OpSet, - make_shared(&Expression::OpLiteral, "host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); + if (!vinstances.IsObjectType()) + BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - if (service) { - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "service_name", di), - make_shared(&Expression::OpLiteral, service->GetShortName(), di), - di)); + instances = vinstances; + } else { + instances = make_shared(); + 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(di); + builder->SetType("ScheduledDowntime"); + builder->SetName(objName); + builder->SetScope(locals); - if (!zone.IsEmpty()) { builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "zone", di), - make_shared(&Expression::OpLiteral, zone, di), - di)); + 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(); } - builder->AddExpression(rule.GetExpression()); - - ConfigItem::Ptr downtimeItem = builder->Compile(); - downtimeItem->Register(); - DynamicObject::Ptr dobj = downtimeItem->Commit(); - dobj->OnConfigLoaded(); - return true; } diff --git a/lib/icinga/service-apply.cpp b/lib/icinga/service-apply.cpp index f7c3fca94..5cf08ec3a 100644 --- a/lib/icinga/service-apply.cpp +++ b/lib/icinga/service-apply.cpp @@ -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(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::OpSet, - make_shared(&Expression::OpLiteral, "host_name", di), - make_shared(&Expression::OpLiteral, host->GetName(), di), - di)); + if (!vinstances.IsObjectType()) + BOOST_THROW_EXCEPTION(std::invalid_argument("for expression must be an array")); - builder->AddExpression(make_shared(&Expression::OpSet, - make_shared(&Expression::OpLiteral, "name", di), - make_shared(&Expression::OpLiteral, rule.GetName(), 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)); + instances = vinstances; + } else { + instances = make_shared(); + instances->Add(""); } - builder->AddExpression(rule.GetExpression()); + ObjectLock olock(instances); + BOOST_FOREACH(const String& instance, instances) { + String objName = rule.GetName(); - ConfigItem::Ptr serviceItem = builder->Compile(); - serviceItem->Register(); - DynamicObject::Ptr dobj = serviceItem->Commit(); - dobj->OnConfigLoaded(); + 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(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)); + } + + builder->AddExpression(rule.GetExpression()); + + ConfigItem::Ptr serviceItem = builder->Compile(); + serviceItem->Register(); + DynamicObject::Ptr dobj = serviceItem->Commit(); + dobj->OnConfigLoaded(); + } return true; }