From a84610046ce8b574e405752243d93eb03e87403c Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:27:17 +0200 Subject: [PATCH 01/10] db: Add model for table `dependency` --- library/Icingadb/Model/Dependency.php | 95 +++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 library/Icingadb/Model/Dependency.php diff --git a/library/Icingadb/Model/Dependency.php b/library/Icingadb/Model/Dependency.php new file mode 100644 index 000000000..0a32a8096 --- /dev/null +++ b/library/Icingadb/Model/Dependency.php @@ -0,0 +1,95 @@ +add(new Binary([ + 'id', + 'redundancy_group_id', + 'timeperiod_id' + ])); + $behaviors->add(new BoolCast([ + 'disable_checks', + 'disable_notifications', + 'ignore_soft_states' + ])); + $behaviors->add(new Bitmask([ + 'states' => [ + 'ok' => 1, + 'warning' => 2, + 'critical' => 4, + 'unknown' => 8, + 'up' => 16, + 'down' => 32 + ], + ])); + } + + public function createRelations(Relations $relations): void + { + $relations->belongsTo('timeperiod', Timeperiod::class) + ->setJoinType('LEFT'); + $relations->belongsTo('redundancy_group', RedundancyGroup::class) + ->setJoinType('LEFT'); + + $relations->hasOne('state', DependencyState::class) + ->setJoinType('LEFT'); + + $relations->hasOne('edge', DependencyEdge::class); + } +} From 1f966c9d4ed62d52c53ae19c12648951d30c5363 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:27:28 +0200 Subject: [PATCH 02/10] db: Add model for table `dependency_edge` --- library/Icingadb/Model/DependencyEdge.php | 63 +++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 library/Icingadb/Model/DependencyEdge.php diff --git a/library/Icingadb/Model/DependencyEdge.php b/library/Icingadb/Model/DependencyEdge.php new file mode 100644 index 000000000..d6c6599c2 --- /dev/null +++ b/library/Icingadb/Model/DependencyEdge.php @@ -0,0 +1,63 @@ +add(new Binary([ + 'to_node_id', + 'from_node_id', + 'dependency_id' + ])); + } + + public function createRelations(Relations $relations): void + { + $relations->belongsTo('from', DependencyNode::class) + ->setCandidateKey('from_node_id'); + $relations->belongsTo('to', DependencyNode::class) + ->setCandidateKey('to_node_id'); + $relations->belongsTo('dependency', Dependency::class) + ->setJoinType('LEFT'); + } +} From 5358ae9bd7bb1b63110ae4ab60117b33e7911d5f Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:27:38 +0200 Subject: [PATCH 03/10] db: Add model for table `dependency_node` --- library/Icingadb/Model/DependencyNode.php | 88 +++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 library/Icingadb/Model/DependencyNode.php diff --git a/library/Icingadb/Model/DependencyNode.php b/library/Icingadb/Model/DependencyNode.php new file mode 100644 index 000000000..995e9be7e --- /dev/null +++ b/library/Icingadb/Model/DependencyNode.php @@ -0,0 +1,88 @@ +add(new Binary([ + 'id', + 'host_id', + 'service_id', + 'redundancy_group_id' + ])); + } + + public function createRelations(Relations $relations): void + { + $relations->belongsTo('host', Host::class) + ->setJoinType('LEFT'); + $relations->belongsTo('service', Service::class) + ->setJoinType('LEFT'); + $relations->belongsTo('redundancy_group', RedundancyGroup::class) + ->setJoinType('LEFT'); + + $relations->hasMany('from', DependencyEdge::class) + ->setForeignKey('from_node_id') + ->setJoinType('LEFT'); + $relations->hasMany('to', DependencyEdge::class) + ->setForeignKey('to_node_id') + ->setJoinType('LEFT'); + + $relations->belongsToMany('child', self::class) + ->through(DependencyEdge::class) + ->setForeignKey('to_node_id') + ->setTargetForeignKey('from_node_id') + ->setJoinType('LEFT'); + $relations->belongsToMany('parent', self::class) + ->through(DependencyEdge::class) + ->setForeignKey('from_node_id') + ->setTargetForeignKey('to_node_id') + ->setJoinType('LEFT'); + } +} From 86dddfd4dcd3eef4c081c531f2452f10e463a47b Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:27:55 +0200 Subject: [PATCH 04/10] db: Add model for table `dependency_state` --- library/Icingadb/Model/DependencyState.php | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 library/Icingadb/Model/DependencyState.php diff --git a/library/Icingadb/Model/DependencyState.php b/library/Icingadb/Model/DependencyState.php new file mode 100644 index 000000000..cf1eb5de0 --- /dev/null +++ b/library/Icingadb/Model/DependencyState.php @@ -0,0 +1,58 @@ +add(new Binary([ + 'id', + 'dependency_id' + ])); + $behaviors->add(new BoolCast([ + 'failed' + ])); + } + + public function createRelations(Relations $relations): void + { + $relations->belongsTo('dependency', Dependency::class); + } +} From 95db14497caa28e2efff9e1d0c473c4e6bddee45 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:28:12 +0200 Subject: [PATCH 05/10] db: Add model for table `redundancy_group` --- library/Icingadb/Model/RedundancyGroup.php | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 library/Icingadb/Model/RedundancyGroup.php diff --git a/library/Icingadb/Model/RedundancyGroup.php b/library/Icingadb/Model/RedundancyGroup.php new file mode 100644 index 000000000..c8a76e7bf --- /dev/null +++ b/library/Icingadb/Model/RedundancyGroup.php @@ -0,0 +1,67 @@ +add(new Binary([ + 'id' + ])); + $behaviors->add(new ReRoute([ + 'child' => 'dependency_node.child', + 'parent' => 'dependency_node.parent' + ])); + } + + public function createRelations(Relations $relations): void + { + $relations->belongsTo('dependency_node', DependencyNode::class) + ->setForeignKey('redundancy_group_id') + ->setCandidateKey('id'); + + $relations->hasOne('state', RedundancyGroupState::class) + ->setJoinType('LEFT'); + + $relations->hasMany('dependency', Dependency::class); + } +} From 1289e09f4873f7ed2d576c4c08a9ec8aff79ed0a Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:28:19 +0200 Subject: [PATCH 06/10] db: Add model for table `redundancy_group_state` --- .../Icingadb/Model/RedundancyGroupState.php | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 library/Icingadb/Model/RedundancyGroupState.php diff --git a/library/Icingadb/Model/RedundancyGroupState.php b/library/Icingadb/Model/RedundancyGroupState.php new file mode 100644 index 000000000..1d8da858f --- /dev/null +++ b/library/Icingadb/Model/RedundancyGroupState.php @@ -0,0 +1,58 @@ +add(new Binary([ + 'id', + 'redundancy_group_id' + ])); + $behaviors->add(new BoolCast([ + 'failed' + ])); + } + + public function createRelations(Relations $relations): void + { + $relations->belongsTo('redundancy_group', RedundancyGroup::class); + } +} From 65319a9c4b80e53b01b878f588011a150a434ce4 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:30:39 +0200 Subject: [PATCH 07/10] db: Add dependency relations to Host model --- library/Icingadb/Model/Host.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/Icingadb/Model/Host.php b/library/Icingadb/Model/Host.php index c6979839e..a68275950 100644 --- a/library/Icingadb/Model/Host.php +++ b/library/Icingadb/Model/Host.php @@ -181,6 +181,8 @@ public function createBehaviors(Behaviors $behaviors) ])); $behaviors->add(new ReRoute([ + 'child' => 'dependency_node.child', + 'parent' => 'dependency_node.parent', 'servicegroup' => 'service.servicegroup', 'user' => 'notification.user', 'usergroup' => 'notification.usergroup' @@ -235,6 +237,8 @@ public function createDefaults(Defaults $defaults) public function createRelations(Relations $relations) { + $relations->belongsTo('dependency_node', DependencyNode::class) + ->setJoinType('LEFT'); $relations->belongsTo('environment', Environment::class); $relations->belongsTo('eventcommand', Eventcommand::class); $relations->belongsTo('checkcommand', Checkcommand::class); From d19a47107e68eeb7449e852cc273c1b5f4e9dd63 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:31:05 +0200 Subject: [PATCH 08/10] db: Add dependency relations to Service model --- library/Icingadb/Model/Service.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/Icingadb/Model/Service.php b/library/Icingadb/Model/Service.php index 1d97b96ac..1383823ab 100644 --- a/library/Icingadb/Model/Service.php +++ b/library/Icingadb/Model/Service.php @@ -170,6 +170,8 @@ public function createBehaviors(Behaviors $behaviors) ])); $behaviors->add(new ReRoute([ + 'child' => 'dependency_node.child', + 'parent' => 'dependency_node.parent', 'user' => 'notification.user', 'usergroup' => 'notification.usergroup' ])); @@ -222,6 +224,8 @@ public function createDefaults(Defaults $defaults) public function createRelations(Relations $relations) { + $relations->belongsTo('dependency_node', DependencyNode::class) + ->setJoinType('LEFT'); $relations->belongsTo('environment', Environment::class); $relations->belongsTo('host', Host::class)->setJoinType('LEFT'); $relations->belongsTo('checkcommand', Checkcommand::class); From e124b58f27c33bc38c4e27b79943dca994192355 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 2 Sep 2024 15:31:29 +0200 Subject: [PATCH 09/10] db: Add dependency relation to Timeperiod model --- library/Icingadb/Model/Timeperiod.php | 1 + 1 file changed, 1 insertion(+) diff --git a/library/Icingadb/Model/Timeperiod.php b/library/Icingadb/Model/Timeperiod.php index e1664103b..34ff55096 100644 --- a/library/Icingadb/Model/Timeperiod.php +++ b/library/Icingadb/Model/Timeperiod.php @@ -98,5 +98,6 @@ public function createRelations(Relations $relations) $relations->hasMany('service', Service::class) ->setForeignKey('check_timeperiod_id'); $relations->hasMany('user', User::class); + $relations->hasMany('dependency', Dependency::class); } } From 3171bf565903001918d7a1e4a39026c2d679a77d Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Wed, 4 Sep 2024 11:07:44 +0200 Subject: [PATCH 10/10] db: Introduce model to fetch unreachable parents --- library/Icingadb/Model/UnreachableParent.php | 181 +++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 library/Icingadb/Model/UnreachableParent.php diff --git a/library/Icingadb/Model/UnreachableParent.php b/library/Icingadb/Model/UnreachableParent.php new file mode 100644 index 000000000..de3608a3c --- /dev/null +++ b/library/Icingadb/Model/UnreachableParent.php @@ -0,0 +1,181 @@ +belongsTo('host', Host::class) + ->setJoinType('LEFT'); + $relations->belongsTo('service', Service::class) + ->setJoinType('LEFT'); + $relations->belongsTo('redundancy_group', RedundancyGroup::class) + ->setJoinType('LEFT'); + $relations->belongsTo('dependency', Dependency::class) + ->setJoinType('LEFT'); + } + + public function createBehaviors(Behaviors $behaviors): void + { + $behaviors->add(new Binary([ + 'id', + 'host_id', + 'service_id', + 'redundancy_group_id', + 'dependency_id' + ])); + + $behaviors->add(new ReRoute([ + 'hostgroup' => 'host.hostgroup', + 'servicegroup' => 'service.servicegroup' + ])); + } + + public static function on(Connection $db, Model $root = null): Query + { + if ($root === null) { + throw new InvalidArgumentException('Root node must not be null'); + } + + $query = parent::on($db); + $query->getSelectBase()->with( + self::selectNodes($db, $root), + 'unreachable_parent', + true + )->where([ + 'unreachable_parent.level > ?' => 0, + 'unreachable_parent.is_group_member = ?' => 0 + ]); + + return $query; + } + + private static function selectNodes(Connection $db, Model $root): Select + { + $rootQuery = DependencyNode::on($db) + ->columns([ + 'id' => 'id', + 'level' => new Expression('0'), + 'host_id' => 'host_id', + 'service_id' => new Expression("COALESCE(%s, CAST('' as binary(20)))", ['service_id']), + 'redundancy_group_id' => new Expression("CAST('' as binary(20))"), + 'dependency_id' => new Expression("CAST('' as binary(20))"), + 'is_group_member' => new Expression('0') + ]); + if ($root instanceof Host) { + $rootQuery->filter(Filter::all( + Filter::equal('host_id', $root->id), + Filter::unlike('service_id', '*') + )); + } elseif ($root instanceof Service) { + $rootQuery->filter(Filter::all( + Filter::equal('host_id', $root->host_id), + Filter::equal('service_id', $root->id) + )); + } else { + throw new InvalidArgumentException('Root node must be either a host or a service'); + } + + $nodeQuery = DependencyEdge::on($db) + ->columns([ + 'id' => 'to_node_id', + 'level' => new Expression('urn.level + 1'), + 'host_id' => 'to.host_id', + 'service_id' => 'to.service_id', + 'redundancy_group_id' => 'to.redundancy_group_id', + 'dependency_id' => 'dependency_id', + 'is_group_member' => new Expression('urn.redundancy_group_id IS NOT NULL AND urn.level > 0') + ]); + $nodeQuery->filter(Filter::any( + Filter::equal('dependency.state.failed', 'y'), + Filter::equal('to.redundancy_group.state.failed', 'y') + )); + + $nodeSelect = $nodeQuery->assembleSelect(); + + // TODO: ipl-orm doesn't preserve key order :'( + $columnsProperty = (new \ReflectionClass($nodeSelect))->getProperty('columns'); + $columnsProperty->setAccessible(true); + $columnsProperty->setValue($nodeSelect, array_merge( + [ + 'id' => null, + 'level' => null, + 'host_id' => null, + 'service_id' => null, + 'redundancy_group_id' => null, + 'dependency_id' => null, + 'is_group_member' => null + ], + $nodeSelect->getColumns() + )); + + return $rootQuery->assembleSelect()->union( + $nodeSelect + ->join(['urn' => 'unreachable_parent'], sprintf( + 'urn.id = %s', + $nodeQuery + ->getResolver() + ->qualifyColumn( + 'from_node_id', + $nodeQuery + ->getResolver() + ->getAlias($nodeQuery->getModel()) + ) + )) + ); + } +}