diff --git a/lib/base/string.cpp b/lib/base/string.cpp index eec5b8372d6..e4e5c273c24 100644 --- a/lib/base/string.cpp +++ b/lib/base/string.cpp @@ -461,3 +461,8 @@ String::ConstIterator icinga::range_end(const String& x) { return x.End(); } + +std::size_t std::hash::operator()(const String& s) const noexcept +{ + return std::hash{}(s.GetData()); +} diff --git a/lib/base/string.hpp b/lib/base/string.hpp index b9290eeee7c..10ddaf9779e 100644 --- a/lib/base/string.hpp +++ b/lib/base/string.hpp @@ -7,6 +7,7 @@ #include "base/object.hpp" #include #include +#include #include #include @@ -178,6 +179,12 @@ String::ConstIterator range_end(const String& x); } +template<> +struct std::hash +{ + std::size_t operator()(const icinga::String& s) const noexcept; +}; + extern template class std::vector; namespace boost diff --git a/lib/config/CMakeLists.txt b/lib/config/CMakeLists.txt index 042668dc30b..80b8c2c4420 100644 --- a/lib/config/CMakeLists.txt +++ b/lib/config/CMakeLists.txt @@ -21,7 +21,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) set(config_SOURCES i2-config.hpp activationcontext.cpp activationcontext.hpp - applyrule.cpp applyrule.hpp + applyrule.cpp applyrule-targeted.cpp applyrule.hpp configcompiler.cpp configcompiler.hpp configcompilercontext.cpp configcompilercontext.hpp configfragment.hpp diff --git a/lib/config/applyrule-targeted.cpp b/lib/config/applyrule-targeted.cpp new file mode 100644 index 00000000000..210c6776436 --- /dev/null +++ b/lib/config/applyrule-targeted.cpp @@ -0,0 +1,254 @@ +/* Icinga 2 | (c) 2022 Icinga GmbH | GPLv2+ */ + +#include "base/string.hpp" +#include "config/applyrule.hpp" +#include "config/expression.hpp" +#include +#include + +using namespace icinga; + +/** + * @returns All ApplyRules targeting only specific parent objects including the given host. (See AddTargetedRule().) + */ +const std::set& ApplyRule::GetTargetedHostRules(const Type::Ptr& sourceType, const String& host) +{ + auto perSourceType (m_Rules.find(sourceType.get())); + + if (perSourceType != m_Rules.end()) { + auto perHost (perSourceType->second.Targeted.find(host)); + + if (perHost != perSourceType->second.Targeted.end()) { + return perHost->second.ForHost; + } + } + + static const std::set noRules; + return noRules; +} + +/** + * @returns All ApplyRules targeting only specific parent objects including the given service. (See AddTargetedRule().) + */ +const std::set& ApplyRule::GetTargetedServiceRules(const Type::Ptr& sourceType, const String& host, const String& service) +{ + auto perSourceType (m_Rules.find(sourceType.get())); + + if (perSourceType != m_Rules.end()) { + auto perHost (perSourceType->second.Targeted.find(host)); + + if (perHost != perSourceType->second.Targeted.end()) { + auto perService (perHost->second.ForServices.find(service)); + + if (perService != perHost->second.ForServices.end()) { + return perService->second; + } + } + } + + static const std::set noRules; + return noRules; +} + +/** + * If the given ApplyRule targets only specific parent objects, add it to the respective "index". + * + * - The above means for apply T "N" to Host: assign where host.name == "H" [ || host.name == "h" ... ] + * - For apply T "N" to Service it means: assign where host.name == "H" && service.name == "S" [ || host.name == "h" && service.name == "s" ... ] + * + * The order of operands of || && == doesn't matter. + * + * @returns Whether the rule has been added to the "index". + */ +bool ApplyRule::AddTargetedRule(const ApplyRule::Ptr& rule, const String& targetType, ApplyRule::PerSourceType& rules) +{ + if (targetType == "Host") { + std::vector hosts; + + if (GetTargetHosts(rule->m_Filter.get(), hosts)) { + for (auto host : hosts) { + rules.Targeted[*host].ForHost.emplace(rule); + } + + return true; + } + } else if (targetType == "Service") { + std::vector> services; + + if (GetTargetServices(rule->m_Filter.get(), services)) { + for (auto service : services) { + rules.Targeted[*service.first].ForServices[*service.second].emplace(rule); + } + + return true; + } + } + + return false; +} + +/** + * If the given assign filter is like the following, extract the host names ("H", "h", ...) into the vector: + * + * host.name == "H" [ || host.name == "h" ... ] + * + * The order of operands of || == doesn't matter. + * + * @returns Whether the given assign filter is like above. + */ +bool ApplyRule::GetTargetHosts(Expression* assignFilter, std::vector& hosts) +{ + auto lor (dynamic_cast(assignFilter)); + + if (lor) { + return GetTargetHosts(lor->GetOperand1().get(), hosts) + && GetTargetHosts(lor->GetOperand2().get(), hosts); + } + + auto name (GetComparedName(assignFilter, "host")); + + if (name) { + hosts.emplace_back(name); + return true; + } + + return false; +} + +/** + * If the given assign filter is like the following, extract the host+service names ("H"+"S", "h"+"s", ...) into the vector: + * + * host.name == "H" && service.name == "S" [ || host.name == "h" && service.name == "s" ... ] + * + * The order of operands of || && == doesn't matter. + * + * @returns Whether the given assign filter is like above. + */ +bool ApplyRule::GetTargetServices(Expression* assignFilter, std::vector>& services) +{ + auto lor (dynamic_cast(assignFilter)); + + if (lor) { + return GetTargetServices(lor->GetOperand1().get(), services) + && GetTargetServices(lor->GetOperand2().get(), services); + } + + auto service (GetTargetService(assignFilter)); + + if (service.first) { + services.emplace_back(service); + return true; + } + + return false; +} + +/** + * If the given filter is like the following, extract the host+service names ("H"+"S"): + * + * host.name == "H" && service.name == "S" + * + * The order of operands of && == doesn't matter. + * + * @returns {host, service} on success and {nullptr, nullptr} on failure. + */ +std::pair ApplyRule::GetTargetService(Expression* assignFilter) +{ + auto land (dynamic_cast(assignFilter)); + + if (!land) { + return {nullptr, nullptr}; + } + + auto op1 (land->GetOperand1().get()); + auto op2 (land->GetOperand2().get()); + auto host (GetComparedName(op1, "host")); + + if (!host) { + std::swap(op1, op2); + host = GetComparedName(op1, "host"); + } + + if (host) { + auto service (GetComparedName(op2, "service")); + + if (service) { + return {host, service}; + } + } + + return {nullptr, nullptr}; +} + +/** + * If the given filter is like the following, extract the object name ("N"): + * + * $lcType$.name == "N" + * + * The order of operands of == doesn't matter. + * + * @returns The object name on success and nullptr on failure. + */ +const String * ApplyRule::GetComparedName(Expression* assignFilter, const char * lcType) +{ + auto eq (dynamic_cast(assignFilter)); + + if (!eq) { + return nullptr; + } + + auto op1 (eq->GetOperand1().get()); + auto op2 (eq->GetOperand2().get()); + + if (IsNameIndexer(op1, lcType)) { + return GetLiteralStringValue(op2); + } + + if (IsNameIndexer(op2, lcType)) { + return GetLiteralStringValue(op1); + } + + return nullptr; +} + +/** + * @returns Whether the given expression is like $lcType$.name. + */ +bool ApplyRule::IsNameIndexer(Expression* exp, const char * lcType) +{ + auto ixr (dynamic_cast(exp)); + + if (!ixr) { + return false; + } + + auto var (dynamic_cast(ixr->GetOperand1().get())); + + if (!var || var->GetVariable() != lcType) { + return false; + } + + auto val (GetLiteralStringValue(ixr->GetOperand2().get())); + + return val && *val == "name"; +} + +/** + * @returns If the given expression is a string literal, the string. nullptr on failure. + */ +const String * ApplyRule::GetLiteralStringValue(Expression* exp) +{ + auto lit (dynamic_cast(exp)); + + if (!lit) { + return nullptr; + } + + auto& val (lit->GetValue()); + + if (!val.IsString()) { + return nullptr; + } + + return &val.Get(); +} diff --git a/lib/config/applyrule.cpp b/lib/config/applyrule.cpp index 363dce90b82..5cfbf9e35fd 100644 --- a/lib/config/applyrule.cpp +++ b/lib/config/applyrule.cpp @@ -3,6 +3,7 @@ #include "config/applyrule.hpp" #include "base/logger.hpp" #include +#include using namespace icinga; @@ -80,9 +81,12 @@ void ApplyRule::AddRule(const String& sourceType, const String& targetType, cons } } - m_Rules[Type::GetByName(sourceType).get()][Type::GetByName(*actualTargetType).get()].emplace_back(ApplyRule( - name, expression, filter, package, fkvar, fvvar, fterm, ignoreOnError, di, scope - )); + ApplyRule::Ptr rule = new ApplyRule(name, expression, filter, package, fkvar, fvvar, fterm, ignoreOnError, di, scope); + auto& rules (m_Rules[Type::GetByName(sourceType).get()]); + + if (!AddTargetedRule(rule, *actualTargetType, rules)) { + rules.Regular[Type::GetByName(*actualTargetType).get()].emplace_back(std::move(rule)); + } } bool ApplyRule::EvaluateFilter(ScriptFrame& frame) const @@ -140,33 +144,56 @@ bool ApplyRule::HasMatches() const return m_HasMatches; } -std::vector& ApplyRule::GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType) +const std::vector& ApplyRule::GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType) { auto perSourceType (m_Rules.find(sourceType.get())); if (perSourceType != m_Rules.end()) { - auto perTargetType (perSourceType->second.find(targetType.get())); + auto perTargetType (perSourceType->second.Regular.find(targetType.get())); - if (perTargetType != perSourceType->second.end()) { + if (perTargetType != perSourceType->second.Regular.end()) { return perTargetType->second; } } - static std::vector noRules; + static const std::vector noRules; return noRules; } void ApplyRule::CheckMatches(bool silent) { for (auto& perSourceType : m_Rules) { - for (auto& perTargetType : perSourceType.second) { + for (auto& perTargetType : perSourceType.second.Regular) { for (auto& rule : perTargetType.second) { - if (!rule.HasMatches() && !silent) { - Log(LogWarning, "ApplyRule") - << "Apply rule '" << rule.GetName() << "' (" << rule.GetDebugInfo() << ") for type '" - << perSourceType.first->GetName() << "' does not match anywhere!"; + CheckMatches(rule, perSourceType.first, silent); + } + } + + std::unordered_set targeted; + + for (auto& perHost : perSourceType.second.Targeted) { + for (auto& rule : perHost.second.ForHost) { + targeted.emplace(rule.get()); + } + + for (auto& perService : perHost.second.ForServices) { + for (auto& rule : perService.second) { + targeted.emplace(rule.get()); } } } + + for (auto rule : targeted) { + CheckMatches(rule, perSourceType.first, silent); + } + } +} + +void ApplyRule::CheckMatches(const ApplyRule::Ptr& rule, Type* sourceType, bool silent) +{ + if (!rule->HasMatches() && !silent) { + Log(LogWarning, "ApplyRule") + << "Apply rule '" << rule->GetName() << "' (" << rule->GetDebugInfo() << ") for type '" + << sourceType->GetName() << "' does not match anywhere!"; } } diff --git a/lib/config/applyrule.hpp b/lib/config/applyrule.hpp index 10520bfbd6c..6b6fca8f8fe 100644 --- a/lib/config/applyrule.hpp +++ b/lib/config/applyrule.hpp @@ -6,6 +6,7 @@ #include "config/i2-config.hpp" #include "config/expression.hpp" #include "base/debuginfo.hpp" +#include "base/shared-object.hpp" #include "base/type.hpp" #include @@ -15,11 +16,40 @@ namespace icinga /** * @ingroup config */ -class ApplyRule +class ApplyRule : public SharedObject { public: + DECLARE_PTR_TYPEDEFS(ApplyRule); + + struct PerHost + { + std::set ForHost; + std::unordered_map> ForServices; + }; + + struct PerSourceType + { + std::unordered_map> Regular; + std::unordered_map Targeted; + }; + + /* + * m_Rules[T::TypeInstance.get()].Targeted["H"].ForHost + * contains all apply rules like apply T "x" to Host { ... } + * which target only specific hosts incl. "H", e.g. via + * assign where host.name == "H" || host.name == "h". + * + * m_Rules[T::TypeInstance.get()].Targeted["H"].ForServices["S"] + * contains all apply rules like apply T "x" to Service { ... } + * which target only specific services on specific hosts, + * e.g. via assign where host.name == "H" && service.name == "S". + * + * m_Rules[T::TypeInstance.get()].Regular[C::TypeInstance.get()] + * contains all other apply rules like apply T "x" to C { ... }. + */ + typedef std::unordered_map RuleMap; + typedef std::map > TypeMap; - typedef std::unordered_map>> RuleMap; String GetName() const; Expression::Ptr GetExpression() const; @@ -39,7 +69,9 @@ class ApplyRule static void AddRule(const String& sourceType, const String& targetType, const String& name, const Expression::Ptr& expression, const Expression::Ptr& filter, const String& package, const String& fkvar, const String& fvvar, const Expression::Ptr& fterm, bool ignoreOnError, const DebugInfo& di, const Dictionary::Ptr& scope); - static std::vector& GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType); + static const std::vector& GetRules(const Type::Ptr& sourceType, const Type::Ptr& targetType); + static const std::set& GetTargetedHostRules(const Type::Ptr& sourceType, const String& host); + static const std::set& GetTargetedServiceRules(const Type::Ptr& sourceType, const String& host, const String& service); static void RegisterType(const String& sourceType, const std::vector& targetTypes); static bool IsValidSourceType(const String& sourceType); @@ -47,6 +79,7 @@ class ApplyRule static const std::vector& GetTargetTypes(const String& sourceType); static void CheckMatches(bool silent); + static void CheckMatches(const ApplyRule::Ptr& rule, Type* sourceType, bool silent); private: String m_Name; @@ -64,6 +97,14 @@ class ApplyRule static TypeMap m_Types; static RuleMap m_Rules; + static bool AddTargetedRule(const ApplyRule::Ptr& rule, const String& targetType, PerSourceType& rules); + static bool GetTargetHosts(Expression* assignFilter, std::vector& hosts); + static bool GetTargetServices(Expression* assignFilter, std::vector>& services); + static std::pair GetTargetService(Expression* assignFilter); + static const String * GetComparedName(Expression* assignFilter, const char * lcType); + static bool IsNameIndexer(Expression* exp, const char * lcType); + static const String * GetLiteralStringValue(Expression* exp); + ApplyRule(String name, Expression::Ptr expression, Expression::Ptr filter, String package, String fkvar, String fvvar, Expression::Ptr fterm, bool ignoreOnError, DebugInfo di, Dictionary::Ptr scope); diff --git a/lib/config/expression.hpp b/lib/config/expression.hpp index 21ab7a2e61a..7be667a2b99 100644 --- a/lib/config/expression.hpp +++ b/lib/config/expression.hpp @@ -283,6 +283,16 @@ class BinaryExpression : public DebuggableExpression : DebuggableExpression(debugInfo), m_Operand1(std::move(operand1)), m_Operand2(std::move(operand2)) { } + inline const std::unique_ptr& GetOperand1() const noexcept + { + return m_Operand1; + } + + inline const std::unique_ptr& GetOperand2() const noexcept + { + return m_Operand2; + } + protected: std::unique_ptr m_Operand1; std::unique_ptr m_Operand2; @@ -293,7 +303,7 @@ class VariableExpression final : public DebuggableExpression public: VariableExpression(String variable, std::vector imports, const DebugInfo& debugInfo = DebugInfo()); - String GetVariable() const + inline const String& GetVariable() const { return m_Variable; } diff --git a/lib/icinga/dependency-apply.cpp b/lib/icinga/dependency-apply.cpp index 07ab312541d..8f8840c1971 100644 --- a/lib/icinga/dependency-apply.cpp +++ b/lib/icinga/dependency-apply.cpp @@ -17,9 +17,9 @@ INITIALIZE_ONCE([]() { ApplyRule::RegisterType("Dependency", { "Host", "Service" }); }); -bool Dependency::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule) +bool Dependency::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter) { - if (!rule.EvaluateFilter(frame)) + if (!skipFilter && !rule.EvaluateFilter(frame)) return false; DebugInfo di = rule.GetDebugInfo(); @@ -62,7 +62,7 @@ bool Dependency::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, cons return true; } -bool Dependency::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule) +bool Dependency::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule, bool skipFilter) { DebugInfo di = rule.GetDebugInfo(); @@ -111,7 +111,7 @@ bool Dependency::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyR name += instance; } - if (EvaluateApplyRuleInstance(checkable, name, frame, rule)) + if (EvaluateApplyRuleInstance(checkable, name, frame, rule, skipFilter)) match = true; } } else if (vinstances.IsObjectType()) { @@ -124,7 +124,7 @@ bool Dependency::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyR frame.Locals->Set(rule.GetFKVar(), key); frame.Locals->Set(rule.GetFVVar(), dict->Get(key)); - if (EvaluateApplyRuleInstance(checkable, rule.GetName() + key, frame, rule)) + if (EvaluateApplyRuleInstance(checkable, rule.GetName() + key, frame, rule, skipFilter)) match = true; } } @@ -137,8 +137,13 @@ void Dependency::EvaluateApplyRules(const Host::Ptr& host) CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'"); for (auto& rule : ApplyRule::GetRules(Dependency::TypeInstance, Host::TypeInstance)) { - if (EvaluateApplyRule(host, rule)) - rule.AddMatch(); + if (EvaluateApplyRule(host, *rule)) + rule->AddMatch(); + } + + for (auto& rule : ApplyRule::GetTargetedHostRules(Dependency::TypeInstance, host->GetName())) { + if (EvaluateApplyRule(host, *rule, true)) + rule->AddMatch(); } } @@ -147,7 +152,12 @@ void Dependency::EvaluateApplyRules(const Service::Ptr& service) CONTEXT("Evaluating 'apply' rules for service '" + service->GetName() + "'"); for (auto& rule : ApplyRule::GetRules(Dependency::TypeInstance, Service::TypeInstance)) { - if (EvaluateApplyRule(service, rule)) - rule.AddMatch(); + if (EvaluateApplyRule(service, *rule)) + rule->AddMatch(); + } + + for (auto& rule : ApplyRule::GetTargetedServiceRules(Dependency::TypeInstance, service->GetHost()->GetName(), service->GetShortName())) { + if (EvaluateApplyRule(service, *rule, true)) + rule->AddMatch(); } } diff --git a/lib/icinga/dependency.hpp b/lib/icinga/dependency.hpp index bc3ae5388a5..75424cb8994 100644 --- a/lib/icinga/dependency.hpp +++ b/lib/icinga/dependency.hpp @@ -50,8 +50,8 @@ class Dependency final : public ObjectImpl Checkable::Ptr m_Parent; Checkable::Ptr m_Child; - static bool EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule); - static bool EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule); + static bool EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter); + static bool EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule, bool skipFilter = false); }; } diff --git a/lib/icinga/notification-apply.cpp b/lib/icinga/notification-apply.cpp index 61d21921515..0983077f0b7 100644 --- a/lib/icinga/notification-apply.cpp +++ b/lib/icinga/notification-apply.cpp @@ -17,9 +17,9 @@ INITIALIZE_ONCE([]() { ApplyRule::RegisterType("Notification", { "Host", "Service" }); }); -bool Notification::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule) +bool Notification::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter) { - if (!rule.EvaluateFilter(frame)) + if (!skipFilter && !rule.EvaluateFilter(frame)) return false; DebugInfo di = rule.GetDebugInfo(); @@ -61,7 +61,7 @@ bool Notification::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, co return true; } -bool Notification::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule) +bool Notification::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule, bool skipFilter) { DebugInfo di = rule.GetDebugInfo(); @@ -110,7 +110,7 @@ bool Notification::EvaluateApplyRule(const Checkable::Ptr& checkable, const Appl name += instance; } - if (EvaluateApplyRuleInstance(checkable, name, frame, rule)) + if (EvaluateApplyRuleInstance(checkable, name, frame, rule, skipFilter)) match = true; } } else if (vinstances.IsObjectType()) { @@ -123,7 +123,7 @@ bool Notification::EvaluateApplyRule(const Checkable::Ptr& checkable, const Appl frame.Locals->Set(rule.GetFKVar(), key); frame.Locals->Set(rule.GetFVVar(), dict->Get(key)); - if (EvaluateApplyRuleInstance(checkable, rule.GetName() + key, frame, rule)) + if (EvaluateApplyRuleInstance(checkable, rule.GetName() + key, frame, rule, skipFilter)) match = true; } } @@ -137,8 +137,13 @@ void Notification::EvaluateApplyRules(const Host::Ptr& host) for (auto& rule : ApplyRule::GetRules(Notification::TypeInstance, Host::TypeInstance)) { - if (EvaluateApplyRule(host, rule)) - rule.AddMatch(); + if (EvaluateApplyRule(host, *rule)) + rule->AddMatch(); + } + + for (auto& rule : ApplyRule::GetTargetedHostRules(Notification::TypeInstance, host->GetName())) { + if (EvaluateApplyRule(host, *rule, true)) + rule->AddMatch(); } } @@ -147,7 +152,12 @@ void Notification::EvaluateApplyRules(const Service::Ptr& service) CONTEXT("Evaluating 'apply' rules for service '" + service->GetName() + "'"); for (auto& rule : ApplyRule::GetRules(Notification::TypeInstance, Service::TypeInstance)) { - if (EvaluateApplyRule(service, rule)) - rule.AddMatch(); + if (EvaluateApplyRule(service, *rule)) + rule->AddMatch(); + } + + for (auto& rule : ApplyRule::GetTargetedServiceRules(Notification::TypeInstance, service->GetHost()->GetName(), service->GetShortName())) { + if (EvaluateApplyRule(service, *rule, true)) + rule->AddMatch(); } } diff --git a/lib/icinga/notification.hpp b/lib/icinga/notification.hpp index 2922308c8e7..ec9164f199a 100644 --- a/lib/icinga/notification.hpp +++ b/lib/icinga/notification.hpp @@ -118,8 +118,8 @@ class Notification final : public ObjectImpl void ExecuteNotificationHelper(NotificationType type, const User::Ptr& user, const CheckResult::Ptr& cr, bool force, const String& author = "", const String& text = ""); - static bool EvaluateApplyRuleInstance(const intrusive_ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule); - static bool EvaluateApplyRule(const intrusive_ptr& checkable, const ApplyRule& rule); + static bool EvaluateApplyRuleInstance(const intrusive_ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter); + static bool EvaluateApplyRule(const intrusive_ptr& checkable, const ApplyRule& rule, bool skipFilter = false); static std::map m_StateFilterMap; static std::map m_TypeFilterMap; diff --git a/lib/icinga/scheduleddowntime-apply.cpp b/lib/icinga/scheduleddowntime-apply.cpp index 0dfaf1b09b9..9571706a22d 100644 --- a/lib/icinga/scheduleddowntime-apply.cpp +++ b/lib/icinga/scheduleddowntime-apply.cpp @@ -16,9 +16,9 @@ INITIALIZE_ONCE([]() { ApplyRule::RegisterType("ScheduledDowntime", { "Host", "Service" }); }); -bool ScheduledDowntime::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule) +bool ScheduledDowntime::EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter) { - if (!rule.EvaluateFilter(frame)) + if (!skipFilter && !rule.EvaluateFilter(frame)) return false; DebugInfo di = rule.GetDebugInfo(); @@ -60,7 +60,7 @@ bool ScheduledDowntime::EvaluateApplyRuleInstance(const Checkable::Ptr& checkabl return true; } -bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule) +bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule, bool skipFilter) { DebugInfo di = rule.GetDebugInfo(); @@ -109,7 +109,7 @@ bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const name += instance; } - if (EvaluateApplyRuleInstance(checkable, name, frame, rule)) + if (EvaluateApplyRuleInstance(checkable, name, frame, rule, skipFilter)) match = true; } } else if (vinstances.IsObjectType()) { @@ -122,7 +122,7 @@ bool ScheduledDowntime::EvaluateApplyRule(const Checkable::Ptr& checkable, const frame.Locals->Set(rule.GetFKVar(), key); frame.Locals->Set(rule.GetFVVar(), dict->Get(key)); - if (EvaluateApplyRuleInstance(checkable, rule.GetName() + key, frame, rule)) + if (EvaluateApplyRuleInstance(checkable, rule.GetName() + key, frame, rule, skipFilter)) match = true; } } @@ -135,8 +135,13 @@ void ScheduledDowntime::EvaluateApplyRules(const Host::Ptr& host) CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'"); for (auto& rule : ApplyRule::GetRules(ScheduledDowntime::TypeInstance, Host::TypeInstance)) { - if (EvaluateApplyRule(host, rule)) - rule.AddMatch(); + if (EvaluateApplyRule(host, *rule)) + rule->AddMatch(); + } + + for (auto& rule : ApplyRule::GetTargetedHostRules(ScheduledDowntime::TypeInstance, host->GetName())) { + if (EvaluateApplyRule(host, *rule, true)) + rule->AddMatch(); } } @@ -145,7 +150,12 @@ void ScheduledDowntime::EvaluateApplyRules(const Service::Ptr& service) CONTEXT("Evaluating 'apply' rules for service '" + service->GetName() + "'"); for (auto& rule : ApplyRule::GetRules(ScheduledDowntime::TypeInstance, Service::TypeInstance)) { - if (EvaluateApplyRule(service, rule)) - rule.AddMatch(); + if (EvaluateApplyRule(service, *rule)) + rule->AddMatch(); + } + + for (auto& rule : ApplyRule::GetTargetedServiceRules(ScheduledDowntime::TypeInstance, service->GetHost()->GetName(), service->GetShortName())) { + if (EvaluateApplyRule(service, *rule, true)) + rule->AddMatch(); } } diff --git a/lib/icinga/scheduleddowntime.hpp b/lib/icinga/scheduleddowntime.hpp index f8d8e084083..e70123616b1 100644 --- a/lib/icinga/scheduleddowntime.hpp +++ b/lib/icinga/scheduleddowntime.hpp @@ -51,8 +51,8 @@ class ScheduledDowntime final : public ObjectImpl static std::atomic m_AllConfigLoaded; - static bool EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule); - static bool EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule); + static bool EvaluateApplyRuleInstance(const Checkable::Ptr& checkable, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter); + static bool EvaluateApplyRule(const Checkable::Ptr& checkable, const ApplyRule& rule, bool skipFilter = false); }; } diff --git a/lib/icinga/service-apply.cpp b/lib/icinga/service-apply.cpp index 24a19c95674..666847cabb0 100644 --- a/lib/icinga/service-apply.cpp +++ b/lib/icinga/service-apply.cpp @@ -16,9 +16,9 @@ INITIALIZE_ONCE([]() { ApplyRule::RegisterType("Service", { "Host" }); }); -bool Service::EvaluateApplyRuleInstance(const Host::Ptr& host, const String& name, ScriptFrame& frame, const ApplyRule& rule) +bool Service::EvaluateApplyRuleInstance(const Host::Ptr& host, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter) { - if (!rule.EvaluateFilter(frame)) + if (!skipFilter && !rule.EvaluateFilter(frame)) return false; DebugInfo di = rule.GetDebugInfo(); @@ -55,7 +55,7 @@ bool Service::EvaluateApplyRuleInstance(const Host::Ptr& host, const String& nam return true; } -bool Service::EvaluateApplyRule(const Host::Ptr& host, const ApplyRule& rule) +bool Service::EvaluateApplyRule(const Host::Ptr& host, const ApplyRule& rule, bool skipFilter) { DebugInfo di = rule.GetDebugInfo(); @@ -98,7 +98,7 @@ bool Service::EvaluateApplyRule(const Host::Ptr& host, const ApplyRule& rule) name += instance; } - if (EvaluateApplyRuleInstance(host, name, frame, rule)) + if (EvaluateApplyRuleInstance(host, name, frame, rule, skipFilter)) match = true; } } else if (vinstances.IsObjectType()) { @@ -111,7 +111,7 @@ bool Service::EvaluateApplyRule(const Host::Ptr& host, const ApplyRule& rule) frame.Locals->Set(rule.GetFKVar(), key); frame.Locals->Set(rule.GetFVVar(), dict->Get(key)); - if (EvaluateApplyRuleInstance(host, rule.GetName() + key, frame, rule)) + if (EvaluateApplyRuleInstance(host, rule.GetName() + key, frame, rule, skipFilter)) match = true; } } @@ -124,7 +124,12 @@ void Service::EvaluateApplyRules(const Host::Ptr& host) CONTEXT("Evaluating 'apply' rules for host '" + host->GetName() + "'"); for (auto& rule : ApplyRule::GetRules(Service::TypeInstance, Host::TypeInstance)) { - if (EvaluateApplyRule(host, rule)) - rule.AddMatch(); + if (EvaluateApplyRule(host, *rule)) + rule->AddMatch(); + } + + for (auto& rule : ApplyRule::GetTargetedHostRules(Service::TypeInstance, host->GetName())) { + if (EvaluateApplyRule(host, *rule, true)) + rule->AddMatch(); } } diff --git a/lib/icinga/service.hpp b/lib/icinga/service.hpp index 74237018bff..ac27c3d9373 100644 --- a/lib/icinga/service.hpp +++ b/lib/icinga/service.hpp @@ -54,8 +54,8 @@ class Service final : public ObjectImpl, public MacroResolver private: Host::Ptr m_Host; - static bool EvaluateApplyRuleInstance(const Host::Ptr& host, const String& name, ScriptFrame& frame, const ApplyRule& rule); - static bool EvaluateApplyRule(const Host::Ptr& host, const ApplyRule& rule); + static bool EvaluateApplyRuleInstance(const Host::Ptr& host, const String& name, ScriptFrame& frame, const ApplyRule& rule, bool skipFilter); + static bool EvaluateApplyRule(const Host::Ptr& host, const ApplyRule& rule, bool skipFilter = false); }; std::pair GetHostService(const Checkable::Ptr& checkable);