mirror of
https://github.com/Icinga/icingadb-web.git
synced 2026-05-28 04:36:06 -04:00
This was easy because only README.md and doc/01-About.md were redacted manually, everything else via: git ls-files -z |xargs -0 perl -pi -e 's/Icinga GmbH \| GPLv2/Icinga GmbH | GPLv2+/' This is legal because we have only merged PRs with label:cla/signed or made by Icinga staff: https://github.com/Icinga/icingadb-web/pulls?page=1&q=is%3Apr+is%3Aclosed+-label%3Acla%2Fsigned+-author%3Anilmerg This has no risk for us in people distributing their own version under GPLv3 only. After all, we won't take their patches anyway, unless they sign our CLA. This is the cleanest solution for having e.g. these in one address space: * Icinga Web, GPLv2+ * K8s Web, AGPLv3 * Thirdparty, some LGPLv3 and Apache-2.0 Apropos, K8s Web is even v3-licensed on purpose, to have a stronger protection against cloud ops.
185 lines
6.1 KiB
PHP
185 lines
6.1 KiB
PHP
<?php
|
|
|
|
/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2+ */
|
|
|
|
namespace Icinga\Module\Icingadb\Model;
|
|
|
|
use Icinga\Module\Icingadb\Model\Behavior\ReRoute;
|
|
use ipl\Orm\Behavior\Binary;
|
|
use ipl\Orm\Behaviors;
|
|
use Icinga\Module\Icingadb\Common\Model;
|
|
use ipl\Orm\Query;
|
|
use ipl\Orm\Relations;
|
|
use ipl\Sql\Connection;
|
|
use ipl\Sql\Expression;
|
|
use ipl\Sql\Filter\Exists;
|
|
use ipl\Stdlib\Filter;
|
|
|
|
/**
|
|
* Dependency node model.
|
|
*
|
|
* @property string $id
|
|
* @property string $environment_id
|
|
* @property ?string $host_id
|
|
* @property ?string $service_id
|
|
* @property ?string $redundancy_group_id
|
|
* @property string $name
|
|
* @property string $severity
|
|
* @property string $state
|
|
* @property string $last_state_change
|
|
*
|
|
* @property (?Host)|Query $host
|
|
* @property (?Service)|Query $service
|
|
* @property (?RedundancyGroup)|Query $redundancy_group
|
|
* @property (?DependencyEdge)|Query $from
|
|
* @property (?DependencyEdge)|Query $to
|
|
*/
|
|
class DependencyNode extends Model
|
|
{
|
|
public function getTableName(): string
|
|
{
|
|
return 'dependency_node';
|
|
}
|
|
|
|
public function getKeyName(): string
|
|
{
|
|
return 'id';
|
|
}
|
|
|
|
public function getColumns(): array
|
|
{
|
|
return [
|
|
'id',
|
|
'environment_id',
|
|
'host_id',
|
|
'service_id',
|
|
'redundancy_group_id',
|
|
'name' => new Expression(
|
|
'COALESCE(%s, %s, %s)',
|
|
['service.display_name', 'host.display_name', 'redundancy_group.display_name']
|
|
),
|
|
'severity' => new Expression(
|
|
"COALESCE(%s, %s, CASE WHEN %s = 'y' THEN 1 ELSE 0 END)",
|
|
['service.state.severity', 'host.state.severity', 'redundancy_group.state.failed']
|
|
),
|
|
'state' => new Expression(
|
|
"COALESCE(%s, %s, CASE WHEN %s = 'y' THEN 1 ELSE 0 END)",
|
|
['service.state.soft_state', 'host.state.soft_state', 'redundancy_group.state.failed']
|
|
),
|
|
'last_state_change' => new Expression(
|
|
'COALESCE(%s, %s, %s)',
|
|
[
|
|
'service.state.last_state_change',
|
|
'host.state.last_state_change',
|
|
'redundancy_group.state.last_state_change'
|
|
]
|
|
),
|
|
];
|
|
}
|
|
|
|
public function getSearchColumns(): array
|
|
{
|
|
return [
|
|
'host.name_ci',
|
|
'service.name_ci',
|
|
'redundancy_group.display_name'
|
|
];
|
|
}
|
|
|
|
public function getDefaultSort(): array
|
|
{
|
|
return ['severity DESC', 'last_state_change DESC'];
|
|
}
|
|
|
|
public function createBehaviors(Behaviors $behaviors): void
|
|
{
|
|
$behaviors->add(new Binary([
|
|
'id',
|
|
'environment_id',
|
|
'host_id',
|
|
'service_id',
|
|
'redundancy_group_id'
|
|
]));
|
|
$behaviors->add(new ReRoute([
|
|
'child' => 'to.from',
|
|
'parent' => 'from.to',
|
|
'hostgroup' => 'host.hostgroup',
|
|
'servicegroup' => 'service.servicegroup'
|
|
]));
|
|
}
|
|
|
|
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');
|
|
|
|
// TODO: This self join is only a work-around as when selecting nodes and filtering by child or parent,
|
|
// the ORM wants to join the base table as usual in case a sub-query is used. Though, in this case
|
|
// resolving e.g. child to "to.from" is reversed in a sub-query to "from.to" and the ORM does not
|
|
// detect that "to" is already the link to the base table.
|
|
// Given the path "dependency_node.to.from.host", the sub-query uses "host.from.to.dependency_node".
|
|
// "to.dependency_node" is the crucial part, as "dependency_node" is said self-join.
|
|
$relations->hasOne('dependency_node', self::class)
|
|
->setForeignKey('id');
|
|
}
|
|
|
|
/**
|
|
* Get the query with only (direct) parents or children of the given host id.
|
|
*
|
|
* @internal Bug: This fix is required for host based queries. Otherwise, ipl-orm makes two subqueries
|
|
* from the filter, which leads to incorrect results.
|
|
*
|
|
* @todo This is a workaround, remove it once https://github.com/Icinga/ipl-orm/issues/119 is fixed
|
|
*
|
|
* @param string $hostId Host id to fetch parents or children for
|
|
* @param Connection $db The database connection
|
|
* @param bool $fetchParents Fetch parents if true, children otherwise
|
|
*
|
|
* @return Query
|
|
*/
|
|
public static function forHost(string $hostId, Connection $db, bool $fetchParents = false): Query
|
|
{
|
|
$filterTable = $fetchParents ? 'child' : 'parent';
|
|
$utilizeType = $fetchParents ? 'parent' : 'child';
|
|
|
|
$edge = DependencyEdge::on($db)
|
|
->utilize($utilizeType)
|
|
->columns([new Expression('1')])
|
|
->filter(Filter::all(
|
|
Filter::equal("$filterTable.host.id", $hostId),
|
|
Filter::unlike("$filterTable.service.id", '*')
|
|
));
|
|
|
|
$edge->getFilter()->metaData()->set('forceOptimization', false);
|
|
|
|
$resolver = $edge->getResolver();
|
|
|
|
$edgeAlias = $resolver->getAlias(
|
|
$resolver->resolveRelation($resolver->qualifyPath($utilizeType, $edge->getModel()->getTableName()))
|
|
->getTarget()
|
|
);
|
|
|
|
$query = static::on($db);
|
|
|
|
$query->filter(new Exists(
|
|
$edge->assembleSelect()
|
|
->where(
|
|
"$edgeAlias.id = "
|
|
. $query->getResolver()->qualifyColumn('id', $query->getModel()->getTableName())
|
|
)
|
|
));
|
|
|
|
return $query;
|
|
}
|
|
}
|