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

VolatileStateResults: Properly apply state changes in case of DependecyNode #1093

Merged
merged 1 commit into from
Nov 19, 2024
Merged
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
92 changes: 55 additions & 37 deletions library/Icingadb/Redis/VolatileStateResults.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Icinga\Module\Icingadb\Model\DependencyNode;
use Icinga\Module\Icingadb\Model\Host;
use Icinga\Module\Icingadb\Model\Service;
use Icinga\Module\Icingadb\Model\State;
use ipl\Orm\Query;
use ipl\Orm\Resolver;
use ipl\Orm\ResultSet;
Expand All @@ -28,6 +29,12 @@ class VolatileStateResults extends ResultSet
/** @var bool Whether Redis updates were applied */
private $updatesApplied = false;

/** @var string Object type host */
protected const TYPE_HOST = 'host';

/** @var string Object type service */
protected const TYPE_SERVICE = 'service';

public static function fromQuery(Query $query)
{
$self = parent::fromQuery($query);
Expand Down Expand Up @@ -99,68 +106,89 @@ public function rewind(): void
protected function applyRedisUpdates($rows)
{
$type = null;
$behaviors = null;

$keys = [];
$hostStateKeys = [];

$showSourceGranted = $this->getAuth()->hasPermission('icingadb/object/show-source');

$getKeysAndBehaviors = function (State $state): array {
return [$state->getColumns(), $this->resolver->getBehaviors($state)];
};

$states = [];
$hostStates = [];
foreach ($rows as $row) {
if ($row instanceof DependencyNode) {
if ($row->redundancy_group_id !== null) {
continue;
} elseif ($row->service_id !== null) {
$type = 'service';
$type = self::TYPE_SERVICE;
$row = $row->service;
} else {
$type = 'host';
$type = self::TYPE_HOST;
$row = $row->host;
}

$behaviors = $this->resolver->getBehaviors($row->state);
} elseif ($type === null) {
$behaviors = $this->resolver->getBehaviors($row->state);

switch (true) {
case $row instanceof Host:
$type = 'host';
$type = self::TYPE_HOST;
break;
case $row instanceof Service:
$type = 'service';
$type = self::TYPE_SERVICE;
break;
default:
throw new RuntimeException('Volatile states can only be fetched for hosts and services');
}
}

$states[bin2hex($row->id)] = $row->state;
if (empty($keys)) {
$keys = $row->state->getColumns();
$states[$type][bin2hex($row->id)] = $row->state;

if (! isset($states[$type]['keys'])) {
[$keys, $behaviors] = $getKeysAndBehaviors($row->state);

if (! $showSourceGranted) {
$keys = array_diff($keys, ['check_commandline']);
}

$states[$type]['keys'] = $keys;
$states[$type]['behaviors'] = $behaviors;
}

if ($type === 'service' && $row->host instanceof Host && isset($row->host->id)) {
$hostStates[bin2hex($row->host->id)] = $row->host->state;
if (empty($hostStateKeys)) {
$hostStateKeys = $row->host->state->getColumns();
if ($type === self::TYPE_SERVICE && $row->host instanceof Host && isset($row->host->id)) {
$states[self::TYPE_HOST][bin2hex($row->host->id)] = $row->host->state;

if (! isset($states[self::TYPE_HOST]['keys'])) {
[$keys, $behaviors] = $getKeysAndBehaviors($row->host->state);

$states[self::TYPE_HOST]['keys'] = $keys;
$states[self::TYPE_HOST]['behaviors'] = $behaviors;
}
}
}

if (empty($states)) {
return;
if (! empty($states[self::TYPE_SERVICE])) {
$this->apply($states[self::TYPE_SERVICE], self::TYPE_SERVICE);
}

if ($type === 'service') {
$results = IcingaRedis::fetchServiceState(array_keys($states), $keys);
} else {
$results = IcingaRedis::fetchHostState(array_keys($states), $keys);
if (! empty($states[self::TYPE_HOST])) {
$this->apply($states[self::TYPE_HOST], self::TYPE_HOST);
}
}

/**
* Apply the given states of given type to the results
*
* @param array $states
* @param string $type The object type ({@see self::TYPE_HOST} OR {@see self::TYPE_SERVICE})
*
* @return void
*/
protected function apply(array $states, string $type): void
{
$keys = $states['keys'];
$behaviors = $states['behaviors'];

unset($states['keys'], $states['behaviors']);

$results = $type === self::TYPE_SERVICE
? IcingaRedis::fetchServiceState(array_keys($states), $keys)
: IcingaRedis::fetchHostState(array_keys($states), $keys);

foreach ($results as $id => $data) {
foreach ($data as $key => $value) {
Expand All @@ -169,15 +197,5 @@ protected function applyRedisUpdates($rows)

$states[$id]->setProperties($data);
}

if ($type === 'service' && ! empty($hostStates)) {
foreach (IcingaRedis::fetchHostState(array_keys($hostStates), $hostStateKeys) as $id => $data) {
foreach ($data as $key => $value) {
$data[$key] = $behaviors->retrieveProperty($value, $key);
}

$hostStates[$id]->setProperties($data);
}
}
}
}
Loading