config = $config; $this->backend = IcingaDbObject::fetchDb(); } public static function apply(BpConfig $config) { $self = new static($config); $self->retrieveStatesFromBackend(); return $config; } public function retrieveStatesFromBackend() { $config = $this->config; try { $this->reallyRetrieveStatesFromBackend(); } catch (Exception $e) { $config->addError( $config->translate('Could not retrieve process state: %s'), $e->getMessage() ); } } public function reallyRetrieveStatesFromBackend() { $config = $this->config; $involvedHostNames = $config->listInvolvedHostNames(); if (empty($involvedHostNames)) { return $this; } Benchmark::measure(sprintf( 'Retrieving states for business process %s using Icinga DB backend', $config->getName() )); $hosts = Host::on($this->backend)->columns([ 'id' => 'host.id', 'name' => 'host.name', 'display_name' => 'host.display_name', 'hard_state' => 'host.state.hard_state', 'soft_state' => 'host.state.soft_state', 'last_state_change' => 'host.state.last_state_change', 'in_downtime' => 'host.state.in_downtime', 'is_acknowledged' => 'host.state.is_acknowledged' ])->filter(Filter::equal('host.name', $involvedHostNames)); $services = Service::on($this->backend)->columns([ 'id' => 'service.id', 'name' => 'service.name', 'display_name' => 'service.display_name', 'host_name' => 'host.name', 'host_display_name' => 'host.display_name', 'hard_state' => 'service.state.hard_state', 'soft_state' => 'service.state.soft_state', 'last_state_change' => 'service.state.last_state_change', 'in_downtime' => 'service.state.in_downtime', 'is_acknowledged' => 'service.state.is_acknowledged' ])->filter(Filter::equal('host.name', $involvedHostNames)); // All of this is ipl-sql now, for performance reasons foreach ($config->listInvolvedConfigs() as $cfg) { $serviceIds = []; $serviceResults = []; foreach ($this->backend->yieldAll($services->assembleSelect()) as $row) { $row->hex_id = bin2hex(is_resource($row->id) ? stream_get_contents($row->id) : $row->id); $serviceIds[] = $row->hex_id; $serviceResults[] = $row; } $redisServiceResults = iterator_to_array(IcingaRedis::fetchServiceState($serviceIds, [ 'hard_state', 'soft_state', 'last_state_change', 'in_downtime', 'is_acknowledged' ])); foreach ($serviceResults as $row) { if (isset($redisServiceResults[$row->hex_id])) { $row = (object) array_merge( (array) $row, $redisServiceResults[$row->hex_id] ); } $this->handleDbRow($row, $cfg, 'service'); } Benchmark::measure('Retrieved states for ' . count($serviceIds) . ' services in ' . $config->getName()); $hostIds = []; $hostResults = []; foreach ($this->backend->yieldAll($hosts->assembleSelect()) as $row) { $row->hex_id = bin2hex(is_resource($row->id) ? stream_get_contents($row->id) : $row->id); $hostIds[] = $row->hex_id; $hostResults[] = $row; } $redisHostResults = iterator_to_array(IcingaRedis::fetchHostState($hostIds, [ 'hard_state', 'soft_state', 'last_state_change', 'in_downtime', 'is_acknowledged' ])); foreach ($hostResults as $row) { if (isset($redisHostResults[$row->hex_id])) { $row = (object) array_merge( (array) $row, $redisHostResults[$row->hex_id] ); } $this->handleDbRow($row, $cfg, 'host'); } Benchmark::measure('Retrieved states for ' . count($hostIds) . ' hosts in ' . $config->getName()); } Benchmark::measure('Got states for business process ' . $config->getName()); return $this; } protected function handleDbRow($row, BpConfig $config, $type) { if ($type === 'service') { $key = BpConfig::joinNodeName($row->host_name, $row->name); } else { $key = BpConfig::joinNodeName($row->name, 'Hoststatus'); } // We fetch more states than we need, so skip unknown ones if (! $config->hasNode($key)) { return; } $node = $config->getNode($key); if ($this->config->usesHardStates()) { if ($row->hard_state !== null) { $node->setState($row->hard_state)->setMissing(false); } } else { if ($row->soft_state !== null) { $node->setState($row->soft_state)->setMissing(false); } } if ($row->last_state_change !== null) { $node->setLastStateChange($row->last_state_change / 1000.0); } $node->setDowntime($this->toBoolean($row->in_downtime)); $node->setAck($this->toBoolean($row->is_acknowledged)); $node->setAlias($row->display_name); if ($node instanceof ServiceNode) { $node->setHostAlias($row->host_display_name); } } private function toBoolean($value): bool { switch (true) { // Icinga Redis >= 6 (is_acknowledged, in_downtime) case $value === true: case $value === false: return $value; // Icinga Redis < 6 (is_acknowledged) case $value === 1: case $value === 2: return true; case $value === 0: return false; // Icinga DB < 1.4 (is_acknowledged) case $value === 'sticky': return true; // Icinga DB >= * (is_acknowledged, in_downtime) case $value === 'y': return true; case $value === 'n': return false; } return false; } }