/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */ #include "icinga/service.hpp" #include "icinga/dependency.hpp" #include "base/logger.hpp" #include using namespace icinga; void Checkable::AddDependency(const Dependency::Ptr& dep) { std::unique_lock lock(m_DependencyMutex); m_Dependencies.insert(dep); } void Checkable::RemoveDependency(const Dependency::Ptr& dep) { std::unique_lock lock(m_DependencyMutex); m_Dependencies.erase(dep); } std::vector Checkable::GetDependencies() const { std::unique_lock lock(m_DependencyMutex); return std::vector(m_Dependencies.begin(), m_Dependencies.end()); } void Checkable::AddReverseDependency(const Dependency::Ptr& dep) { std::unique_lock lock(m_DependencyMutex); m_ReverseDependencies.insert(dep); } void Checkable::RemoveReverseDependency(const Dependency::Ptr& dep) { std::unique_lock lock(m_DependencyMutex); m_ReverseDependencies.erase(dep); } std::vector Checkable::GetReverseDependencies() const { std::unique_lock lock(m_DependencyMutex); return std::vector(m_ReverseDependencies.begin(), m_ReverseDependencies.end()); } bool Checkable::IsReachable(DependencyType dt, Dependency::Ptr *failedDependency, int rstack) const { /* Anything greater than 256 causes recursion bus errors. */ int limit = 256; if (rstack > limit) { Log(LogWarning, "Checkable") << "Too many nested dependencies (>" << limit << ") for checkable '" << GetName() << "': Dependency failed."; return false; } for (const Checkable::Ptr& checkable : GetParents()) { if (!checkable->IsReachable(dt, failedDependency, rstack + 1)) return false; } /* implicit dependency on host if this is a service */ const auto *service = dynamic_cast(this); if (service && (dt == DependencyState || dt == DependencyNotification)) { Host::Ptr host = service->GetHost(); if (host && host->GetState() != HostUp && host->GetStateType() == StateTypeHard) { if (failedDependency) *failedDependency = nullptr; return false; } } auto deps = GetDependencies(); std::unordered_map violated; // key: redundancy group, value: nullptr if satisfied, violating dependency otherwise for (const Dependency::Ptr& dep : deps) { std::string redundancy_group = dep->GetRedundancyGroup(); if (!dep->IsAvailable(dt)) { if (redundancy_group.empty()) { Log(LogDebug, "Checkable") << "Non-redundant dependency '" << dep->GetName() << "' failed for checkable '" << GetName() << "': Marking as unreachable."; if (failedDependency) *failedDependency = dep; return false; } // tentatively mark this dependency group as failed unless it is already marked; // so it either passed before (don't overwrite) or already failed (so don't care) // note that std::unordered_map::insert() will not overwrite an existing entry violated.insert(std::make_pair(redundancy_group, dep)); } else if (!redundancy_group.empty()) { violated[redundancy_group] = nullptr; } } auto violator = std::find_if(violated.begin(), violated.end(), [](auto& v) { return v.second != nullptr; }); if (violator != violated.end()) { Log(LogDebug, "Checkable") << "All dependencies in redundancy group '" << violator->first << "' have failed for checkable '" << GetName() << "': Marking as unreachable."; if (failedDependency) *failedDependency = violator->second; return false; } if (failedDependency) *failedDependency = nullptr; return true; } std::set Checkable::GetParents() const { std::set parents; for (const Dependency::Ptr& dep : GetDependencies()) { Checkable::Ptr parent = dep->GetParent(); if (parent && parent.get() != this) parents.insert(parent); } return parents; } std::set Checkable::GetChildren() const { std::set parents; for (const Dependency::Ptr& dep : GetReverseDependencies()) { Checkable::Ptr service = dep->GetChild(); if (service && service.get() != this) parents.insert(service); } return parents; } std::set Checkable::GetAllChildren() const { std::set children = GetChildren(); GetAllChildrenInternal(children, 0); return children; } void Checkable::GetAllChildrenInternal(std::set& children, int level) const { if (level > 32) return; std::set localChildren; for (const Checkable::Ptr& checkable : children) { std::set cChildren = checkable->GetChildren(); if (!cChildren.empty()) { GetAllChildrenInternal(cChildren, level + 1); localChildren.insert(cChildren.begin(), cChildren.end()); } localChildren.insert(checkable); } children.insert(localChildren.begin(), localChildren.end()); }