2026-01-27 09:06:40 -05:00
|
|
|
|
// SPDX-FileCopyrightText: 2012 Icinga GmbH <https://icinga.com>
|
|
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2014-02-27 05:05:55 -05:00
|
|
|
|
|
|
|
|
|
|
#ifndef DEPENDENCY_H
|
|
|
|
|
|
#define DEPENDENCY_H
|
|
|
|
|
|
|
2024-12-05 04:42:21 -05:00
|
|
|
|
#include "base/shared-object.hpp"
|
|
|
|
|
|
#include "config/configitem.hpp"
|
2014-05-25 10:23:35 -04:00
|
|
|
|
#include "icinga/i2-icinga.hpp"
|
2018-01-18 07:50:38 -05:00
|
|
|
|
#include "icinga/dependency-ti.hpp"
|
2024-12-05 04:42:21 -05:00
|
|
|
|
#include "icinga/timeperiod.hpp"
|
|
|
|
|
|
#include <map>
|
|
|
|
|
|
#include <tuple>
|
|
|
|
|
|
#include <unordered_map>
|
2014-02-27 05:05:55 -05:00
|
|
|
|
|
|
|
|
|
|
namespace icinga
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2014-05-11 11:14:35 -04:00
|
|
|
|
class ApplyRule;
|
2014-12-12 09:33:02 -05:00
|
|
|
|
struct ScriptFrame;
|
2014-11-16 10:20:39 -05:00
|
|
|
|
class Host;
|
|
|
|
|
|
class Service;
|
2014-05-11 11:14:35 -04:00
|
|
|
|
|
2014-02-27 05:05:55 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* A service dependency..
|
|
|
|
|
|
*
|
|
|
|
|
|
* @ingroup icinga
|
|
|
|
|
|
*/
|
2018-01-04 00:11:04 -05:00
|
|
|
|
class Dependency final : public ObjectImpl<Dependency>
|
2014-02-27 05:05:55 -05:00
|
|
|
|
{
|
|
|
|
|
|
public:
|
2025-03-05 06:11:45 -05:00
|
|
|
|
static void StaticInitialize();
|
|
|
|
|
|
|
2014-11-02 18:44:04 -05:00
|
|
|
|
DECLARE_OBJECT(Dependency);
|
|
|
|
|
|
DECLARE_OBJECTNAME(Dependency);
|
2014-02-27 05:05:55 -05:00
|
|
|
|
|
2018-01-03 22:25:35 -05:00
|
|
|
|
intrusive_ptr<Checkable> GetParent() const;
|
|
|
|
|
|
intrusive_ptr<Checkable> GetChild() const;
|
2014-02-27 05:05:55 -05:00
|
|
|
|
|
2018-01-03 22:25:35 -05:00
|
|
|
|
TimePeriod::Ptr GetPeriod() const;
|
2014-02-27 05:05:55 -05:00
|
|
|
|
|
|
|
|
|
|
bool IsAvailable(DependencyType dt) const;
|
|
|
|
|
|
|
2018-01-11 01:08:09 -05:00
|
|
|
|
void ValidateStates(const Lazy<Array::Ptr>& lvalue, const ValidationUtils& utils) override;
|
2014-04-07 07:59:41 -04:00
|
|
|
|
|
2014-11-16 10:20:39 -05:00
|
|
|
|
static void EvaluateApplyRules(const intrusive_ptr<Host>& host);
|
|
|
|
|
|
static void EvaluateApplyRules(const intrusive_ptr<Service>& service);
|
|
|
|
|
|
|
2025-03-05 06:11:45 -05:00
|
|
|
|
/* Note: Only use them for unit test mocks. Prefer InitChildParentReferences(). */
|
2020-02-11 09:02:57 -05:00
|
|
|
|
void SetParent(intrusive_ptr<Checkable> parent);
|
|
|
|
|
|
void SetChild(intrusive_ptr<Checkable> child);
|
|
|
|
|
|
|
2025-07-28 09:29:24 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* The maximum number of allowed dependency recursion levels.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This is a subjective limit how deep the dependency tree should be allowed to go, as anything beyond this level
|
|
|
|
|
|
* is just madness and will likely result in a stack overflow or other undefined behavior.
|
|
|
|
|
|
*/
|
|
|
|
|
|
static constexpr int MaxDependencyRecursionLevel{256};
|
|
|
|
|
|
|
2014-02-27 05:05:55 -05:00
|
|
|
|
protected:
|
2018-01-03 23:12:56 -05:00
|
|
|
|
void OnConfigLoaded() override;
|
|
|
|
|
|
void OnAllConfigLoaded() override;
|
|
|
|
|
|
void Stop(bool runtimeRemoved) override;
|
2025-03-05 06:11:45 -05:00
|
|
|
|
void InitChildParentReferences();
|
2014-04-05 06:56:56 -04:00
|
|
|
|
|
|
|
|
|
|
private:
|
2014-06-24 05:46:53 -04:00
|
|
|
|
Checkable::Ptr m_Parent;
|
|
|
|
|
|
Checkable::Ptr m_Child;
|
|
|
|
|
|
|
2022-10-27 10:15:35 -04:00
|
|
|
|
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);
|
2025-03-05 06:11:45 -05:00
|
|
|
|
|
|
|
|
|
|
static void BeforeOnAllConfigLoadedHandler(const ConfigItems& items);
|
2014-02-27 05:05:55 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
2024-12-05 04:42:21 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* A DependencyGroup represents a set of dependencies that are somehow related to each other.
|
|
|
|
|
|
*
|
|
|
|
|
|
* Specifically, a DependencyGroup is a container for Dependency objects of different Checkables that share the same
|
|
|
|
|
|
* child -> parent relationship config, thus forming a group of dependencies. All dependencies of a Checkable that
|
|
|
|
|
|
* have the same "redundancy_group" attribute value set are guaranteed to be part of the same DependencyGroup object,
|
|
|
|
|
|
* and another Checkable will join that group if and only if it has identical set of dependencies, that is, the same
|
|
|
|
|
|
* parent(s), same redundancy group name and all other dependency attributes required to form a composite key.
|
|
|
|
|
|
*
|
|
|
|
|
|
* More specifically, let's say we have a dependency graph like this:
|
|
|
|
|
|
* @verbatim
|
|
|
|
|
|
* PP1 PP2
|
|
|
|
|
|
* /\ /\
|
|
|
|
|
|
* || ||
|
|
|
|
|
|
* ––––||–––––––––––––––||–––––
|
|
|
|
|
|
* P1 - ( "RG1" ) - P2
|
|
|
|
|
|
* ––––––––––––––––––––––––––––
|
|
|
|
|
|
* /\ /\
|
|
|
|
|
|
* || ||
|
|
|
|
|
|
* C1 C2
|
|
|
|
|
|
* @endverbatim
|
|
|
|
|
|
* The arrows represent a dependency relationship from bottom to top, i.e. both "C1" and "C2" depend on
|
|
|
|
|
|
* their "RG1" redundancy group, and "P1" and "P2" depend each on their respective parents (PP1, PP2 - no group).
|
|
|
|
|
|
* Now, as one can see, both "C1" and "C2" have identical dependencies, that is, they both depend on the same
|
|
|
|
|
|
* redundancy group "RG1" (these could e.g. be constructed through some Apply Rules).
|
|
|
|
|
|
*
|
|
|
|
|
|
* So, instead of having to maintain two separate copies of that graph, we can bring that imaginary redundancy group
|
|
|
|
|
|
* into reality by putting both "P1" and "P2" into an actual DependencyGroup object. However, we don't really put "P1"
|
|
|
|
|
|
* and "P2" objects into that group, but rather the actual Dependency objects of both child Checkables. Therefore, the
|
|
|
|
|
|
* group wouldn't just contain 2 dependencies, but 4 in total, i.e. 2 for each child Checkable (C1 -> {P1, P2} and
|
|
|
|
|
|
* C2 -> {P1, P2}). This way, both child Checkables can just refer to that very same DependencyGroup object.
|
|
|
|
|
|
*
|
|
|
|
|
|
* However, since not all dependencies are part of a redundancy group, we also have to consider the case where
|
|
|
|
|
|
* a Checkable has dependencies that are not part of any redundancy group, like P1 -> PP1. In such situations,
|
|
|
|
|
|
* each of the child Checkables (e.g. P1, P2) will have their own (sharable) DependencyGroup object just like for RGs.
|
|
|
|
|
|
* This allows us to keep the implementation simple and treat redundant and non-redundant dependencies in the same
|
|
|
|
|
|
* way, without having to introduce any special cases everywhere. So, in the end, we'd have 3 dependency groups in
|
|
|
|
|
|
* total, i.e. one for the redundancy group "RG1" (shared by C1 and C2), and two distinct groups for P1 and P2.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @ingroup icinga
|
|
|
|
|
|
*/
|
|
|
|
|
|
class DependencyGroup final : public SharedObject
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
DECLARE_PTR_TYPEDEFS(DependencyGroup);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Defines the key type of each dependency group members.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This tuple consists of the dependency parent Checkable, the dependency time period (nullptr if not configured),
|
|
|
|
|
|
* the state filter, and the ignore soft states flag. Each of these values influences the availability of the
|
|
|
|
|
|
* dependency object, and thus used to group similar dependencies from different Checkables together.
|
|
|
|
|
|
*/
|
|
|
|
|
|
using CompositeKeyType = std::tuple<Checkable*, TimePeriod*, int, bool>;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Represents the value type of each dependency group members.
|
|
|
|
|
|
*
|
|
|
|
|
|
* It stores the dependency objects of any given Checkable that produce the same composite key (CompositeKeyType).
|
|
|
|
|
|
* In other words, when looking at the dependency graph from the class description, the two dependency objects
|
|
|
|
|
|
* {C1, C2} -> P1 produce the same composite key, thus they are mapped to the same MemberValueType container with
|
|
|
|
|
|
* "C1" and "C2" as their keys respectively. Since Icinga 2 allows to construct different identical dependencies
|
|
|
|
|
|
* (duplicates), we're using a multimap instead of a simple map here.
|
|
|
|
|
|
*/
|
|
|
|
|
|
using MemberValueType = std::unordered_multimap<const Checkable*, Dependency*>;
|
|
|
|
|
|
using MembersMap = std::map<CompositeKeyType, MemberValueType>;
|
|
|
|
|
|
|
2025-02-07 05:52:15 -05:00
|
|
|
|
DependencyGroup(String name, const std::set<Dependency::Ptr>& dependencies);
|
2024-12-05 04:42:21 -05:00
|
|
|
|
|
2025-02-07 05:52:15 -05:00
|
|
|
|
static DependencyGroup::Ptr Register(const DependencyGroup::Ptr& dependencyGroup);
|
2025-02-07 10:51:00 -05:00
|
|
|
|
static std::pair<std::set<Dependency::Ptr>, bool> Unregister(const DependencyGroup::Ptr& dependencyGroup, const Checkable::Ptr& child);
|
2024-12-05 04:42:21 -05:00
|
|
|
|
static size_t GetRegistrySize();
|
|
|
|
|
|
|
|
|
|
|
|
static CompositeKeyType MakeCompositeKeyFor(const Dependency::Ptr& dependency);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Check whether the current dependency group represents an explicitly configured redundancy group.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @return bool - Returns true if it's a redundancy group, false otherwise.
|
|
|
|
|
|
*/
|
|
|
|
|
|
inline bool IsRedundancyGroup() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return !m_RedundancyGroupName.IsEmpty();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-03-13 08:13:06 -04:00
|
|
|
|
bool HasChildren() const;
|
2025-02-07 06:22:51 -05:00
|
|
|
|
void AddDependency(const Dependency::Ptr& dependency);
|
|
|
|
|
|
void RemoveDependency(const Dependency::Ptr& dependency);
|
2024-12-05 04:42:21 -05:00
|
|
|
|
std::vector<Dependency::Ptr> GetDependenciesForChild(const Checkable* child) const;
|
2025-02-10 02:30:32 -05:00
|
|
|
|
void LoadParents(std::set<Checkable::Ptr>& parents) const;
|
2025-10-30 12:22:07 -04:00
|
|
|
|
Checkable::Ptr GetAnyChild() const;
|
2024-12-05 04:42:21 -05:00
|
|
|
|
size_t GetDependenciesCount() const;
|
|
|
|
|
|
|
|
|
|
|
|
void SetIcingaDBIdentifier(const String& identifier);
|
|
|
|
|
|
String GetIcingaDBIdentifier() const;
|
|
|
|
|
|
|
|
|
|
|
|
const String& GetRedundancyGroupName() const;
|
|
|
|
|
|
String GetCompositeKey();
|
|
|
|
|
|
|
2025-03-13 10:02:56 -04:00
|
|
|
|
enum class State { Ok, Failed, Unreachable };
|
2025-07-28 09:29:24 -04:00
|
|
|
|
State GetState(const Checkable* child, DependencyType dt = DependencyState) const;
|
2024-12-05 05:21:01 -05:00
|
|
|
|
|
2025-02-07 10:51:00 -05:00
|
|
|
|
static boost::signals2::signal<void(const Checkable::Ptr&, const DependencyGroup::Ptr&)> OnChildRegistered;
|
|
|
|
|
|
static boost::signals2::signal<void(const DependencyGroup::Ptr&, const std::vector<Dependency::Ptr>&, bool)> OnChildRemoved;
|
|
|
|
|
|
|
2025-02-07 06:22:51 -05:00
|
|
|
|
private:
|
2024-12-05 04:42:21 -05:00
|
|
|
|
void CopyDependenciesTo(const DependencyGroup::Ptr& dest);
|
|
|
|
|
|
|
2025-02-07 05:52:15 -05:00
|
|
|
|
struct Hash
|
|
|
|
|
|
{
|
|
|
|
|
|
size_t operator()(const DependencyGroup::Ptr& dependencyGroup) const
|
|
|
|
|
|
{
|
2025-02-12 05:18:12 -05:00
|
|
|
|
size_t hash = std::hash<String>{}(dependencyGroup->GetRedundancyGroupName());
|
2025-02-07 05:52:15 -05:00
|
|
|
|
for (const auto& [key, group] : dependencyGroup->m_Members) {
|
|
|
|
|
|
boost::hash_combine(hash, key);
|
|
|
|
|
|
}
|
|
|
|
|
|
return hash;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct Equal
|
|
|
|
|
|
{
|
|
|
|
|
|
bool operator()(const DependencyGroup::Ptr& lhs, const DependencyGroup::Ptr& rhs) const
|
|
|
|
|
|
{
|
2025-02-12 05:18:12 -05:00
|
|
|
|
if (lhs->GetRedundancyGroupName() != rhs->GetRedundancyGroupName()) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-02-07 05:52:15 -05:00
|
|
|
|
return std::equal(
|
|
|
|
|
|
lhs->m_Members.begin(), lhs->m_Members.end(),
|
|
|
|
|
|
rhs->m_Members.begin(), rhs->m_Members.end(),
|
|
|
|
|
|
[](const auto& l, const auto& r) { return l.first == r.first; }
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2024-12-05 04:42:21 -05:00
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
mutable std::mutex m_Mutex;
|
2024-12-04 10:47:32 -05:00
|
|
|
|
/**
|
|
|
|
|
|
* This identifier is used by Icinga DB to cache the unique hash of this dependency group.
|
|
|
|
|
|
*
|
|
|
|
|
|
* For redundancy groups, once Icinga DB sets this identifier, it will never change again for the lifetime
|
|
|
|
|
|
* of the object. For non-redundant dependency groups, this identifier is (mis)used to cache the shared edge
|
|
|
|
|
|
* state ID of the group. Specifically, non-redundant dependency groups are irrelevant for Icinga DB, so since
|
|
|
|
|
|
* this field isn't going to be used for anything else, we use it to cache the computed shared edge state ID.
|
|
|
|
|
|
* Likewise, if that gets set, it will never change again for the lifetime of the object as well.
|
|
|
|
|
|
*/
|
2024-12-05 04:42:21 -05:00
|
|
|
|
String m_IcingaDBIdentifier;
|
|
|
|
|
|
String m_RedundancyGroupName;
|
|
|
|
|
|
MembersMap m_Members;
|
|
|
|
|
|
|
2025-02-07 05:52:15 -05:00
|
|
|
|
using RegistryType = std::unordered_set<DependencyGroup::Ptr, Hash, Equal>;
|
2024-12-05 04:42:21 -05:00
|
|
|
|
|
|
|
|
|
|
// The global registry of dependency groups.
|
|
|
|
|
|
static std::mutex m_RegistryMutex;
|
|
|
|
|
|
static RegistryType m_Registry;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-07-28 09:29:24 -04:00
|
|
|
|
/**
|
|
|
|
|
|
* Helper class to evaluate the reachability of checkables and state of dependency groups.
|
|
|
|
|
|
*
|
|
|
|
|
|
* This class is used for implementing Checkable::IsReachable() and DependencyGroup::GetState().
|
|
|
|
|
|
* For this, both methods call each other, traversing the dependency graph recursively. In order
|
|
|
|
|
|
* to achieve linear runtime in the graph size, the class internally caches state information
|
|
|
|
|
|
* (otherwise, evaluating the state of the same checkable multiple times can result in exponential
|
|
|
|
|
|
* worst-case complexity). Because of this cached information is not invalidated, the object is
|
|
|
|
|
|
* intended to be short-lived.
|
|
|
|
|
|
*/
|
|
|
|
|
|
class DependencyStateChecker
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
explicit DependencyStateChecker(DependencyType dt);
|
|
|
|
|
|
|
|
|
|
|
|
bool IsReachable(Checkable::ConstPtr checkable, int rstack = 0);
|
|
|
|
|
|
DependencyGroup::State GetState(const DependencyGroup::ConstPtr& group, const Checkable* child, int rstack = 0);
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
DependencyType m_DependencyType;
|
2025-07-28 09:32:05 -04:00
|
|
|
|
std::unordered_map<Checkable::ConstPtr, bool> m_Cache;
|
2025-07-28 09:29:24 -04:00
|
|
|
|
};
|
|
|
|
|
|
|
2014-02-27 05:05:55 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* DEPENDENCY_H */
|