]> granicus.if.org Git - icinga2/commitdiff
Introduce Dependency#parents
authorAlexander A. Klimov <alexander.klimov@icinga.com>
Fri, 30 Aug 2019 12:20:20 +0000 (14:20 +0200)
committerAlexander A. Klimov <alexander.klimov@icinga.com>
Fri, 30 Aug 2019 12:20:20 +0000 (14:20 +0200)
refs #1869

lib/icinga/dependency.cpp
lib/icinga/dependency.hpp
lib/icinga/dependency.ti

index d2261cfe136ae398e78a5929d757837d4a36a0d5..f9b0c52f14f263299e27235c7aae342b64221c4a 100644 (file)
@@ -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 <set>
+#include <sstream>
 
 using namespace icinga;
 
@@ -47,6 +50,118 @@ Dictionary::Ptr DependencyNameComposer::ParseName(const String& name) const
        return result;
 }
 
+void Dependency::BlameInvalidParents(const std::vector<size_t>& 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<String> RequireOptions {"all", "any"};
+} l_ParentsStrings;
+
+void Dependency::ValidateParentsRecursively(const Value& parents, std::vector<size_t>& currentBranch)
+{
+       if (parents.IsString()) {
+               if (parents.Get<String>().IsEmpty()) {
+                       BlameInvalidParents(currentBranch);
+               }
+       } else if (parents.IsObject()) {
+               auto dict (dynamic_pointer_cast<Dictionary>(parents.Get<Object::Ptr>()));
+
+               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<String>()) == l_ParentsStrings.RequireOptions.end()) {
+                               BlameInvalidParents(currentBranch);
+                       }
+
+                       auto array (dynamic_pointer_cast<Array>(of.Get<Object::Ptr>()));
+
+                       if (!array) {
+                               BlameInvalidParents(currentBranch);
+                       }
+
+                       ObjectLock arrayLock (array);
+                       size_t idx = 0;
+
+                       for (auto& val : array) {
+                               currentBranch.emplace_back(idx);
+                               Defer popBack ([&currentBranch](){ 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<String>().IsEmpty()) {
+                                               BlameInvalidParents(currentBranch);
+                                       }
+                               } else {
+                                       if (dict->GetLength() > 1) {
+                                               BlameInvalidParents(currentBranch);
+                                       }
+                               }
+
+                               if (!host.IsString() || host.Get<String>().IsEmpty()) {
+                                       BlameInvalidParents(currentBranch);
+                               }
+                       } else {
+                               BlameInvalidParents(currentBranch);
+                       }
+               }
+       } else {
+               BlameInvalidParents(currentBranch);
+       }
+}
+
+void Dependency::ValidateParents(const Lazy<Value>& lvalue, const ValidationUtils& utils)
+{
+       ObjectImpl<Dependency>::ValidateParents(lvalue, utils);
+
+       auto& parents (lvalue());
+
+       if (!parents.IsEmpty()) {
+               std::vector<size_t> 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<String>());
+
+               if (!(Service::GetByName(checkableName) || Host::GetByName(checkableName))) {
+                       BlameBadParents(std::move(checkableName));
+               }
+       } else {
+               auto dict (static_pointer_cast<Dictionary>(parents.Get<Object::Ptr>()));
+
+               Value hostSpec;
+               if (dict->Get(l_ParentsStrings.Host, &hostSpec)) {
+                       auto hostName (hostSpec.Get<String>());
+                       auto host (Host::GetByName(hostName));
+
+                       if (!host) {
+                               BlameBadParents(std::move(hostName));
+                       }
+
+                       Value serviceSpec;
+                       if (dict->Get(l_ParentsStrings.Service, &serviceSpec)) {
+                               auto serviceName (serviceSpec.Get<String>());
+
+                               if (!host->GetServiceByShortName(serviceName)) {
+                                       BlameBadParents(std::move(hostName) + "!" + std::move(serviceName));
+                               }
+                       }
+               } else {
+                       auto of (static_pointer_cast<Array>(dict->Get(l_ParentsStrings.Of).Get<Object::Ptr>()));
+                       ObjectLock ofLock (of);
+
+                       for (auto& val : of) {
+                               RequireParents(val);
+                       }
+               }
+       }
+}
+
 void Dependency::OnAllConfigLoaded()
 {
        ObjectImpl<Dependency>::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)
index 6a80d84f593c2b71430bf89cc068ae2120757c2f..64857a0a57caa77e6736f817094e9fdbfff79974 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "icinga/i2-icinga.hpp"
 #include "icinga/dependency-ti.hpp"
+#include <vector>
 
 namespace icinga
 {
@@ -38,6 +39,7 @@ public:
        static void EvaluateApplyRules(const intrusive_ptr<Service>& service);
 
 protected:
+       void ValidateParents(const Lazy<Value>& 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<size_t>& currentBranch);
+       void BlameInvalidParents(const std::vector<size_t>& currentBranch);
+       void RequireParents(const Value& parents);
+       void BlameBadParents(String checkable);
 };
 
 }
index 62e6d8b312f8a0770f8eb52300a2f7217d7bfb30..7ee988b764515cbe76aac91dbf9bab1ad22470fa 100644 (file)
@@ -77,6 +77,8 @@ class Dependency : CustomVarObject < DependencyNameComposer
                }}}
        };
 
+       [config] Value parents;
+
        [config, navigation] name(TimePeriod) period (PeriodRaw) {
                navigate {{{
                        return TimePeriod::GetByName(GetPeriodRaw());