Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fake data to indicate affected children #1070

Draft
wants to merge 13 commits into
base: dependencies
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions application/controllers/ServiceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@ public function init()
$name = $this->params->getRequired('name');
$hostName = $this->params->getRequired('host.name');

$query = Service::on($this->getDb())->with([
'state',
'icon_image',
'host',
'host.state',
'timeperiod'
]);
$query = Service::on($this->getDb())
->withColumns(['has_problematic_parent'])
->with([
'state',
'icon_image',
'host',
'host.state',
'timeperiod'
]);
$query
->setResultSetClass(VolatileStateResults::class)
->filter(Filter::all(
Expand Down
12 changes: 11 additions & 1 deletion library/Icingadb/Common/IcingaRedis.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Generator;
use Icinga\Application\Config;
use Icinga\Application\Logger;
use ipl\Sql\Expression;
use Predis\Client as Redis;

class IcingaRedis
Expand Down Expand Up @@ -163,7 +164,16 @@ protected static function fetchState(string $key, array $ids, array $columns): G
foreach ($results as $i => $json) {
if ($json !== null) {
$data = json_decode($json, true);
$keyMap = array_fill_keys($columns, null);
$keyMap = [];

foreach ($columns as $alias => $column) {
if ($column instanceof Expression) {
$keyMap[$alias] = $column->getStatement() ;
} else {
$keyMap[$alias] = null;
}
}

unset($keyMap['is_overdue']); // Is calculated by Icinga DB, not Icinga 2, hence it's never in redis

// TODO: Remove once https://github.com/Icinga/icinga2/issues/9427 is fixed
Expand Down
93 changes: 60 additions & 33 deletions library/Icingadb/Common/StateBadges.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

namespace Icinga\Module\Icingadb\Common;

use InvalidArgumentException;
use ipl\Html\BaseHtmlElement;
use ipl\Html\Html;
use ipl\Html\HtmlElement;
use ipl\Stdlib\BaseFilter;
use ipl\Stdlib\Filter;
use ipl\Web\Filter\QueryString;
use ipl\Web\Url;
use ipl\Web\Widget\Link;
use ipl\Web\Widget\StateBadge;
Expand All @@ -26,7 +27,7 @@ abstract class StateBadges extends BaseHtmlElement
/** @var string Prefix */
protected $prefix;

/** @var Url Badge link */
/** @var ?Url Badge link */
protected $url;

protected $tag = 'ul';
Expand All @@ -46,13 +47,6 @@ public function __construct($item)
$this->url = $this->getBaseUrl();
}

/**
* Get the badge base URL
*
* @return Url
*/
abstract protected function getBaseUrl(): Url;

/**
* Get the type of the items
*
Expand All @@ -67,14 +61,29 @@ abstract protected function getType(): string;
*/
abstract protected function getPrefix(): string;

/**
* Get the badge base URL
*
* @return ?Url
*/
protected function getBaseUrl(): ?Url
{
return null;
}

/**
* Get the integer of the given state text
*
* @param string $state
*
* @return int
*
* @throws InvalidArgumentException if the given state is not valid
*/
abstract protected function getStateInt(string $state): int;
protected function getStateInt(string $state): int
{
throw new InvalidArgumentException('%s is not a valid state', $state);
}

/**
* Get the badge URL
Expand Down Expand Up @@ -135,15 +144,21 @@ public function createLink($content, Filter\Rule $filter = null): Link
*
* @return ?BaseHtmlElement
*/
protected function createBadge(string $state)
protected function createBadge(string $state): ?BaseHtmlElement
{
$key = $this->prefix . "_{$state}";

if (isset($this->item->$key) && $this->item->$key) {
return Html::tag('li', $this->createLink(
new StateBadge($this->item->$key, $state),
Filter::equal($this->type . '.state.soft_state', $this->getStateInt($state))
));
$stateBadge = new StateBadge($this->item->$key, $state);

if ($this->url !== null) {
$this->createLink(
$stateBadge,
Filter::equal($this->type . '.state.soft_state', $this->getStateInt($state))
);
}

return new HtmlElement('li', null, $stateBadge);
}

return null;
Expand All @@ -156,34 +171,46 @@ protected function createBadge(string $state)
*
* @return ?BaseHtmlElement
*/
protected function createGroup(string $state)
protected function createGroup(string $state): ?BaseHtmlElement
{
$content = [];
$handledKey = $this->prefix . "_{$state}_handled";
$unhandledKey = $this->prefix . "_{$state}_unhandled";

if (isset($this->item->$unhandledKey) && $this->item->$unhandledKey) {
$content[] = Html::tag('li', $this->createLink(
new StateBadge($this->item->$unhandledKey, $state),
Filter::all(
Filter::equal($this->type . '.state.soft_state', $this->getStateInt($state)),
Filter::equal($this->type . '.state.is_handled', 'n'),
Filter::equal($this->type . '.state.is_reachable', 'y')
)
));
$unhandledStateBadge = new StateBadge($this->item->$unhandledKey, $state);

if ($this->url !== null) {
$unhandledStateBadge = $this->createLink(
$unhandledStateBadge,
Filter::all(
Filter::equal($this->type . '.state.soft_state', $this->getStateInt($state)),
Filter::equal($this->type . '.state.is_handled', 'n'),
Filter::equal($this->type . '.state.is_reachable', 'y')
)
);
}

$content[] = new HtmlElement('li', null, $unhandledStateBadge);
}

if (isset($this->item->$handledKey) && $this->item->$handledKey) {
$content[] = Html::tag('li', $this->createLink(
new StateBadge($this->item->$handledKey, $state, true),
Filter::all(
Filter::equal($this->type . '.state.soft_state', $this->getStateInt($state)),
Filter::any(
Filter::equal($this->type . '.state.is_handled', 'y'),
Filter::equal($this->type . '.state.is_reachable', 'n')
$handledStateBadge = new StateBadge($this->item->$handledKey, $state, true);

if ($this->url !== null) {
$handledStateBadge = $this->createLink(
$handledStateBadge,
Filter::all(
Filter::equal($this->type . '.state.soft_state', $this->getStateInt($state)),
Filter::any(
Filter::equal($this->type . '.state.is_handled', 'y'),
Filter::equal($this->type . '.state.is_reachable', 'n')
)
)
)
));
);
}

$content[] = new HtmlElement('li', null, $handledStateBadge);
}

if (empty($content)) {
Expand Down
94 changes: 94 additions & 0 deletions library/Icingadb/Model/Behavior/HasProblematicParent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

/* Icinga DB Web | (c) 2024 Icinga GmbH | GPLv2 */

namespace Icinga\Module\Icingadb\Model\Behavior;

use Icinga\Module\Icingadb\Model\DependencyEdge;
use ipl\Orm\AliasedExpression;
use ipl\Orm\ColumnDefinition;
use ipl\Orm\Exception\InvalidColumnException;
use ipl\Orm\Query;
use ipl\Sql\Expression;
use ipl\Stdlib\Filter;
use ipl\Orm\Contract\RewriteColumnBehavior;
use ipl\Orm\Contract\QueryAwareBehavior;

/**
* Behavior to check if the service has a problematic parent
*/
class HasProblematicParent implements RewriteColumnBehavior, QueryAwareBehavior
{
/** @var Query */
protected $query;

public function setQuery(Query $query): self
{
$this->query = $query;

return $this;
}

public function rewriteColumn($column, ?string $relation = null): ?AliasedExpression
{
if (! $this->isSelectableColumn($column)) {
return null;
}

$path = 'from.';
$subQueryRelation = $relation !== null ? $relation . $path : $path;
$subQuery = $this->query->createSubQuery(new DependencyEdge(), $subQueryRelation, null, false)
->limit(1)
->columns([new Expression('1')]);

$subQueryAlias = $subQuery->getResolver()->getAlias($subQuery->getModel());

$subQuery->getSelectBase()
->join(
['to_dependency_node' => 'dependency_node'],
["to_dependency_node.id = $subQueryAlias.to_node_id"]
)->joinLeft(
['root_dependency' => 'dependency'],
[ "$subQueryAlias.dependency_id = root_dependency.id"]
)->joinLeft(
['root_dependency_state' => 'dependency_state'],
['root_dependency.id = root_dependency_state.dependency_id']
)->joinLeft(
['root_group' => 'redundancy_group'],
['root_group.id = to_dependency_node.redundancy_group_id']
)->joinLeft(
['root_group_state' => 'redundancy_group_state'],
['root_group_state.redundancy_group_id = root_group.id']
)->where(
new Expression("root_dependency_state.failed = 'y' OR root_group_state.failed = 'y'")
)->where($subQueryAlias . '_from.service_id = service.id');

$column = $relation !== null ? str_replace('.', '_', $relation) . "_$column" : $column;

$alias = $this->query->getDb()->quoteIdentifier([$column]);

list($select, $values) = $this->query->getDb()
->getQueryBuilder()
->assembleSelect($subQuery->assembleSelect());

return new AliasedExpression($alias, "($select)", null, ...$values);
}

public function isSelectableColumn(string $name): bool
{
return $name === 'has_problematic_parent';
}

public function rewriteColumnDefinition(ColumnDefinition $def, string $relation): void
{
}

public function rewriteCondition(Filter\Condition $condition, $relation = null): void
{
$column = substr($condition->getColumn(), strlen($relation));

if ($this->isSelectableColumn($column)) {
throw new InvalidColumnException($column, $this->query->getModel());
}
}
}
3 changes: 2 additions & 1 deletion library/Icingadb/Model/Host.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use ipl\Orm\Model;
use ipl\Orm\Relations;
use ipl\Orm\ResultSet;
use ipl\Sql\Expression;

/**
* Host model.
Expand Down Expand Up @@ -113,7 +114,7 @@ public function getColumns()
'zone_id',
'command_endpoint_name',
'command_endpoint_id',
'affected_children'
'affected_children' => new Expression('200000')
];
}

Expand Down
3 changes: 2 additions & 1 deletion library/Icingadb/Model/RedundancyGroupState.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public function createRelations(Relations $relations): void

public function getStateText(): string
{
return $this->failed ? 'problem' : 'ok';
// The method should only be called to fake state balls and not to show the group's state
return $this->failed ? 'unreachable' : 'reachable';
}
}
Loading
Loading