summaryrefslogtreecommitdiffstats
path: root/library/Businessprocess/State
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:15:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:15:40 +0000
commitb7fd908d538ed19fe41f03c0a3f93351d8da64e9 (patch)
tree46e14f318948cd4f5d7e874f83e7dfcc5d42fc64 /library/Businessprocess/State
parentInitial commit. (diff)
downloadicingaweb2-module-businessprocess-upstream.tar.xz
icingaweb2-module-businessprocess-upstream.zip
Adding upstream version 2.5.0.upstream/2.5.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/Businessprocess/State')
-rw-r--r--library/Businessprocess/State/IcingaDbState.php191
-rw-r--r--library/Businessprocess/State/MonitoringState.php151
2 files changed, 342 insertions, 0 deletions
diff --git a/library/Businessprocess/State/IcingaDbState.php b/library/Businessprocess/State/IcingaDbState.php
new file mode 100644
index 0000000..1a66900
--- /dev/null
+++ b/library/Businessprocess/State/IcingaDbState.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace Icinga\Module\Businessprocess\State;
+
+use Exception;
+use Icinga\Application\Benchmark;
+use Icinga\Module\Businessprocess\BpConfig;
+use Icinga\Module\Businessprocess\IcingaDbObject;
+use Icinga\Module\Businessprocess\ServiceNode;
+use Icinga\Module\Icingadb\Common\IcingaRedis;
+use Icinga\Module\Icingadb\Model\Host;
+use Icinga\Module\Icingadb\Model\Service;
+use ipl\Sql\Connection as IcingaDbConnection;
+use ipl\Stdlib\Filter;
+
+class IcingaDbState
+{
+ /** @var BpConfig */
+ protected $config;
+
+ /** @var IcingaDbConnection */
+ protected $backend;
+
+ public function __construct(BpConfig $config)
+ {
+ $this->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($row->in_downtime === 'y');
+ $node->setAck($row->is_acknowledged === 'y');
+ $node->setAlias($row->display_name);
+
+ if ($node instanceof ServiceNode) {
+ $node->setHostAlias($row->host_display_name);
+ }
+ }
+}
diff --git a/library/Businessprocess/State/MonitoringState.php b/library/Businessprocess/State/MonitoringState.php
new file mode 100644
index 0000000..b6a2391
--- /dev/null
+++ b/library/Businessprocess/State/MonitoringState.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace Icinga\Module\Businessprocess\State;
+
+use Exception;
+use Icinga\Application\Benchmark;
+use Icinga\Data\Filter\Filter;
+use Icinga\Module\Businessprocess\BpConfig;
+use Icinga\Module\Businessprocess\ServiceNode;
+use Icinga\Module\Monitoring\Backend\MonitoringBackend;
+
+class MonitoringState
+{
+ /** @var BpConfig */
+ protected $config;
+
+ /** @var MonitoringBackend */
+ protected $backend;
+
+ private function __construct(BpConfig $config)
+ {
+ $this->config = $config;
+ $this->backend = $config->getBackend();
+ }
+
+ 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;
+
+ Benchmark::measure('Retrieving states for business process ' . $config->getName());
+ $backend = $this->backend;
+
+ if ($config->usesHardStates()) {
+ $hostStateColumn = 'host_hard_state';
+ $hostStateChangeColumn = 'host_last_hard_state_change';
+ $serviceStateColumn = 'service_hard_state';
+ $serviceStateChangeColumn = 'service_last_hard_state_change';
+ } else {
+ $hostStateColumn = 'host_state';
+ $hostStateChangeColumn = 'host_last_state_change';
+ $serviceStateColumn = 'service_state';
+ $serviceStateChangeColumn = 'service_last_state_change';
+ }
+
+ $hosts = $config->listInvolvedHostNames();
+ if (empty($hosts)) {
+ return $this;
+ }
+
+ $hostFilter = Filter::expression('host_name', '=', $hosts);
+
+ $hostStatus = $backend->select()->from('hostStatus', array(
+ 'hostname' => 'host_name',
+ 'last_state_change' => $hostStateChangeColumn,
+ 'in_downtime' => 'host_in_downtime',
+ 'ack' => 'host_acknowledged',
+ 'state' => $hostStateColumn,
+ 'display_name' => 'host_display_name'
+ ))->applyFilter($hostFilter)->getQuery()->fetchAll();
+
+ Benchmark::measure('Retrieved states for ' . count($hostStatus) . ' hosts in ' . $config->getName());
+
+ // NOTE: we intentionally filter by host_name ONLY
+ // Tests with host IN ... AND service IN shows longer query times
+ // while retrieving 1635 (in 5ms) vs. 1388 (in ~430ms) services
+ $serviceStatus = $backend->select()->from('serviceStatus', array(
+ 'hostname' => 'host_name',
+ 'service' => 'service_description',
+ 'last_state_change' => $serviceStateChangeColumn,
+ 'in_downtime' => 'service_in_downtime',
+ 'ack' => 'service_acknowledged',
+ 'state' => $serviceStateColumn,
+ 'display_name' => 'service_display_name',
+ 'host_display_name' => 'host_display_name'
+ ))->applyFilter($hostFilter)->getQuery()->fetchAll();
+
+ Benchmark::measure('Retrieved states for ' . count($serviceStatus) . ' services in ' . $config->getName());
+
+ $configs = $config->listInvolvedConfigs();
+ foreach ($configs as $cfg) {
+ foreach ($serviceStatus as $row) {
+ $this->handleDbRow($row, $cfg);
+ }
+ foreach ($hostStatus as $row) {
+ $this->handleDbRow($row, $cfg);
+ }
+ }
+
+ // TODO: Union, single query?
+ Benchmark::measure('Got states for business process ' . $config->getName());
+
+ return $this;
+ }
+
+ protected function handleDbRow($row, BpConfig $config)
+ {
+ $key = BpConfig::joinNodeName(
+ $row->hostname,
+ property_exists($row, 'service')
+ ? $row->service
+ : 'Hoststatus'
+ );
+
+ // We fetch more states than we need, so skip unknown ones
+ if (! $config->hasNode($key)) {
+ return;
+ }
+
+ $node = $config->getNode($key);
+
+ if ($row->state !== null) {
+ $node->setState($row->state)->setMissing(false);
+ }
+ if ($row->last_state_change !== null) {
+ $node->setLastStateChange($row->last_state_change);
+ }
+ if ((int) $row->in_downtime === 1) {
+ $node->setDowntime(true);
+ }
+ if ((int) $row->ack === 1) {
+ $node->setAck(true);
+ }
+
+ $node->setAlias($row->display_name);
+
+ if ($node instanceof ServiceNode) {
+ $node->setHostAlias($row->host_display_name);
+ }
+ }
+}