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()) + ) + )) + ); + } +}