From: Alexander A. Klimov Date: Fri, 30 Aug 2019 12:20:20 +0000 (+0200) Subject: Introduce Dependency#parents X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=234aad024c9dda7ce181084b7b9a6b7ca54a814a;p=icinga2 Introduce Dependency#parents refs #1869 --- diff --git a/lib/icinga/dependency.cpp b/lib/icinga/dependency.cpp index d2261cfe1..f9b0c52f1 100644 --- a/lib/icinga/dependency.cpp +++ b/lib/icinga/dependency.cpp @@ -3,8 +3,11 @@ #include "icinga/dependency.hpp" #include "icinga/dependency-ti.cpp" #include "icinga/service.hpp" +#include "base/defer.hpp" #include "base/logger.hpp" #include "base/exception.hpp" +#include +#include using namespace icinga; @@ -47,6 +50,118 @@ Dictionary::Ptr DependencyNameComposer::ParseName(const String& name) const return result; } +void Dependency::BlameInvalidParents(const std::vector& currentBranch) +{ + std::ostringstream oss; + + oss << "Dependency#parents"; + + for (auto idx : currentBranch) { + oss << ".of[" << idx << ']'; + } + + oss << R"EOF( must be like one of "host_name", "host_name!service_name", { host = "host_name" }, { host = "host_name"; service = "service_name" }, { require = "any"; of = [ ... ] }.)EOF"; + + BOOST_THROW_EXCEPTION(ScriptError(oss.str(), GetDebugInfo())); +} + +// We don't have to allocate these strings every time, once is enough. +static const struct { + String Require = "require"; + String Of = "of"; + String Host = "host"; + String Service = "service"; + + std::set RequireOptions {"all", "any"}; +} l_ParentsStrings; + +void Dependency::ValidateParentsRecursively(const Value& parents, std::vector& currentBranch) +{ + if (parents.IsString()) { + if (parents.Get().IsEmpty()) { + BlameInvalidParents(currentBranch); + } + } else if (parents.IsObject()) { + auto dict (dynamic_pointer_cast(parents.Get())); + + if (!dict) { + BlameInvalidParents(currentBranch); + } + + Value require; + if (dict->Get(l_ParentsStrings.Require, &require)) { + Value of; + if (!dict->Get(l_ParentsStrings.Of, &of) || dict->GetLength() > 2) { + BlameInvalidParents(currentBranch); + } + + if (!require.IsString() || !of.IsObject()) { + BlameInvalidParents(currentBranch); + } + + if (l_ParentsStrings.RequireOptions.find(require.Get()) == l_ParentsStrings.RequireOptions.end()) { + BlameInvalidParents(currentBranch); + } + + auto array (dynamic_pointer_cast(of.Get())); + + if (!array) { + BlameInvalidParents(currentBranch); + } + + ObjectLock arrayLock (array); + size_t idx = 0; + + for (auto& val : array) { + currentBranch.emplace_back(idx); + Defer popBack ([¤tBranch](){ currentBranch.pop_back(); }); + + ValidateParentsRecursively(val, currentBranch); + + ++idx; + } + } else { + Value host; + if (dict->Get(l_ParentsStrings.Host, &host)) { + Value service; + if (dict->Get(l_ParentsStrings.Service, &service)) { + if (dict->GetLength() > 2) { + BlameInvalidParents(currentBranch); + } + + if (!host.IsString() || host.Get().IsEmpty()) { + BlameInvalidParents(currentBranch); + } + } else { + if (dict->GetLength() > 1) { + BlameInvalidParents(currentBranch); + } + } + + if (!host.IsString() || host.Get().IsEmpty()) { + BlameInvalidParents(currentBranch); + } + } else { + BlameInvalidParents(currentBranch); + } + } + } else { + BlameInvalidParents(currentBranch); + } +} + +void Dependency::ValidateParents(const Lazy& lvalue, const ValidationUtils& utils) +{ + ObjectImpl::ValidateParents(lvalue, utils); + + auto& parents (lvalue()); + + if (!parents.IsEmpty()) { + std::vector currentBranch; + ValidateParentsRecursively(parents, currentBranch); + } +} + void Dependency::OnConfigLoaded() { Value defaultFilter; @@ -59,6 +174,50 @@ void Dependency::OnConfigLoaded() SetStateFilter(FilterArrayToInt(GetStates(), Notification::GetStateFilterMap(), defaultFilter)); } +void Dependency::BlameBadParents(String checkable) +{ + BOOST_THROW_EXCEPTION(ScriptError("Dependency '" + GetName() + "' references a parent host/service which doesn't exist: '" + std::move(checkable) + "'", GetDebugInfo())); +} + +void Dependency::RequireParents(const Value& parents) +{ + if (parents.IsString()) { + auto checkableName (parents.Get()); + + if (!(Service::GetByName(checkableName) || Host::GetByName(checkableName))) { + BlameBadParents(std::move(checkableName)); + } + } else { + auto dict (static_pointer_cast(parents.Get())); + + Value hostSpec; + if (dict->Get(l_ParentsStrings.Host, &hostSpec)) { + auto hostName (hostSpec.Get()); + auto host (Host::GetByName(hostName)); + + if (!host) { + BlameBadParents(std::move(hostName)); + } + + Value serviceSpec; + if (dict->Get(l_ParentsStrings.Service, &serviceSpec)) { + auto serviceName (serviceSpec.Get()); + + if (!host->GetServiceByShortName(serviceName)) { + BlameBadParents(std::move(hostName) + "!" + std::move(serviceName)); + } + } + } else { + auto of (static_pointer_cast(dict->Get(l_ParentsStrings.Of).Get())); + ObjectLock ofLock (of); + + for (auto& val : of) { + RequireParents(val); + } + } + } +} + void Dependency::OnAllConfigLoaded() { ObjectImpl::OnAllConfigLoaded(); @@ -90,6 +249,12 @@ void Dependency::OnAllConfigLoaded() BOOST_THROW_EXCEPTION(ScriptError("Dependency '" + GetName() + "' references a parent host/service which doesn't exist.", GetDebugInfo())); m_Parent->AddReverseDependency(this); + + auto parents (GetParents()); + + if (!parents.IsEmpty()) { + RequireParents(parents); + } } void Dependency::Stop(bool runtimeRemoved) diff --git a/lib/icinga/dependency.hpp b/lib/icinga/dependency.hpp index 6a80d84f5..64857a0a5 100644 --- a/lib/icinga/dependency.hpp +++ b/lib/icinga/dependency.hpp @@ -5,6 +5,7 @@ #include "icinga/i2-icinga.hpp" #include "icinga/dependency-ti.hpp" +#include namespace icinga { @@ -38,6 +39,7 @@ public: static void EvaluateApplyRules(const intrusive_ptr& service); protected: + void ValidateParents(const Lazy& lvalue, const ValidationUtils& utils) override; void OnConfigLoaded() override; void OnAllConfigLoaded() override; void Stop(bool runtimeRemoved) override; @@ -48,6 +50,11 @@ private: 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); + + void ValidateParentsRecursively(const Value& parents, std::vector& currentBranch); + void BlameInvalidParents(const std::vector& currentBranch); + void RequireParents(const Value& parents); + void BlameBadParents(String checkable); }; } diff --git a/lib/icinga/dependency.ti b/lib/icinga/dependency.ti index 62e6d8b31..7ee988b76 100644 --- a/lib/icinga/dependency.ti +++ b/lib/icinga/dependency.ti @@ -77,6 +77,8 @@ class Dependency : CustomVarObject < DependencyNameComposer }}} }; + [config] Value parents; + [config, navigation] name(TimePeriod) period (PeriodRaw) { navigate {{{ return TimePeriod::GetByName(GetPeriodRaw());