summaryrefslogtreecommitdiffstats
path: root/library/Icingadb/Compat
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/Icingadb/Compat/CompatHost.php106
-rw-r--r--library/Icingadb/Compat/CompatObject.php373
-rw-r--r--library/Icingadb/Compat/CompatService.php159
-rw-r--r--library/Icingadb/Compat/UrlMigrator.php1254
4 files changed, 1892 insertions, 0 deletions
diff --git a/library/Icingadb/Compat/CompatHost.php b/library/Icingadb/Compat/CompatHost.php
new file mode 100644
index 0000000..0494573
--- /dev/null
+++ b/library/Icingadb/Compat/CompatHost.php
@@ -0,0 +1,106 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Compat;
+
+use Icinga\Module\Monitoring\Object\Host;
+
+class CompatHost extends Host
+{
+ use CompatObject;
+
+ private $legacyColumns = [
+ 'host_action_url' => ['path' => ['action_url', 'action_url']],
+ 'action_url' => ['path' => ['action_url', 'action_url']],
+ 'host_address' => ['path' => ['address']],
+ 'host_address6' => ['path' => ['address6']],
+ 'host_alias' => ['path' => ['display_name']],
+ 'host_check_interval' => ['path' => ['check_interval']],
+ 'host_display_name' => ['path' => ['display_name']],
+ 'host_icon_image' => ['path' => ['icon_image', 'icon_image']],
+ 'host_icon_image_alt' => ['path' => ['icon_image_alt']],
+ 'host_name' => ['path' => ['name']],
+ 'host_notes' => ['path' => ['notes']],
+ 'host_notes_url' => ['path' => ['notes_url', 'notes_url']],
+ 'host_acknowledged' => [
+ 'path' => ['state', 'is_acknowledged'],
+ 'type' => 'bool'
+ ],
+ 'host_acknowledgement_type' => [
+ 'path' => ['state', 'is_acknowledged'],
+ 'type' => 'bool'
+ ],
+ 'host_active_checks_enabled' => [
+ 'path' => ['active_checks_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_active_checks_enabled_changed' => null,
+ 'host_attempt' => null,
+ 'host_check_command' => ['path' => ['checkcommand_name']],
+ 'host_check_execution_time' => ['path' => ['state', 'execution_time']],
+ 'host_check_latency' => ['path' => ['state', 'latency']],
+ 'host_check_source' => ['path' => ['state', 'check_source']],
+ 'host_check_timeperiod' => ['path' => ['check_timeperiod_name']],
+ 'host_current_check_attempt' => ['path' => ['state', 'check_attempt']],
+ 'host_current_notification_number' => null,
+ 'host_event_handler_enabled' => [
+ 'path' => ['event_handler_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_event_handler_enabled_changed' => null,
+ 'host_flap_detection_enabled' => [
+ 'path' => ['flapping_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_flap_detection_enabled_changed' => null,
+ 'host_handled' => [
+ 'path' => ['state', 'is_handled'],
+ 'type' => 'bool'
+ ],
+ 'host_in_downtime' => [
+ 'path' => ['state', 'in_downtime'],
+ 'type' => 'bool'
+ ],
+ 'host_is_flapping' => [
+ 'path' => ['state', 'is_flapping'],
+ 'type' => 'bool'
+ ],
+ 'host_is_reachable' => [
+ 'path' => ['state', 'is_reachable'],
+ 'type' => 'bool'
+ ],
+ 'host_last_check' => ['path' => ['state', 'last_update']],
+ 'host_last_notification' => null,
+ 'host_last_state_change' => ['path' => ['state', 'last_state_change']],
+ 'host_long_output' => ['path' => ['state', 'long_output']],
+ 'host_max_check_attempts' => ['path' => ['max_check_attempts']],
+ 'host_next_check' => ['path' => ['state', 'next_check']],
+ 'host_next_update' => ['path' => ['state', 'next_update']],
+ 'host_notifications_enabled' => [
+ 'path' => ['notifications_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_notifications_enabled_changed' => null,
+ 'host_obsessing' => null,
+ 'host_obsessing_changed' => null,
+ 'host_output' => ['path' => ['state', 'output']],
+ 'host_passive_checks_enabled' => [
+ 'path' => ['passive_checks_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_passive_checks_enabled_changed' => null,
+ 'host_percent_state_change' => null,
+ 'host_perfdata' => [
+ 'path' => ['state', 'performance_data'],
+ 'type' => 'bool'
+ ],
+ 'host_process_perfdata' => [
+ 'path' => ['perfdata_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_state' => ['path' => ['state', 'soft_state']],
+ 'host_state_type' => ['path' => ['state', 'state_type']],
+ 'instance_name' => null
+ ];
+}
diff --git a/library/Icingadb/Compat/CompatObject.php b/library/Icingadb/Compat/CompatObject.php
new file mode 100644
index 0000000..d9d72a3
--- /dev/null
+++ b/library/Icingadb/Compat/CompatObject.php
@@ -0,0 +1,373 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Compat;
+
+use Icinga\Exception\NotImplementedError;
+use Icinga\Module\Icingadb\Common\Auth;
+use Icinga\Module\Icingadb\Common\Database;
+use Icinga\Module\Icingadb\Model\CustomvarFlat;
+use Icinga\Module\Icingadb\Model\Host;
+use Icinga\Module\Icingadb\Model\Service;
+use Icinga\Module\Icingadb\Model\Servicegroup;
+use Icinga\Module\Icingadb\Model\ServicestateSummary;
+use InvalidArgumentException;
+use ipl\Orm\Model;
+use ipl\Orm\Query;
+use ipl\Stdlib\Filter;
+use LogicException;
+
+use function ipl\Stdlib\get_php_type;
+
+trait CompatObject
+{
+ use Auth;
+ use Database;
+
+ /** @var array Non-obscured custom variables */
+ protected $rawCustomvars;
+
+ /** @var array Non-obscured host custom variables */
+ protected $rawHostCustomvars;
+
+ /** @var Model $object */
+ private $object;
+
+ public function __construct(Model $object)
+ {
+ $this->object = $object;
+ }
+
+ public static function fromModel(Model $object)
+ {
+ switch (true) {
+ case $object instanceof Host:
+ return new CompatHost($object);
+ case $object instanceof Service:
+ return new CompatService($object);
+ default:
+ throw new InvalidArgumentException(sprintf(
+ 'Host or Service Model instance expected, got "%s" instead.',
+ get_php_type($object)
+ ));
+ }
+ }
+
+ /**
+ * Get this object's name
+ *
+ * @return string
+ */
+ public function getName(): string
+ {
+ return $this->object->name;
+ }
+
+ public function fetch(): bool
+ {
+ return true;
+ }
+
+ protected function fetchRawCustomvars(): self
+ {
+ if ($this->rawCustomvars !== null) {
+ return $this;
+ }
+
+ $vars = $this->object->customvar->execute();
+
+ $customVars = [];
+ foreach ($vars as $row) {
+ $customVars[$row->name] = $row->value;
+ }
+
+ $this->rawCustomvars = $customVars;
+
+ return $this;
+ }
+
+ protected function fetchRawHostCustomvars(): self
+ {
+ if ($this->rawHostCustomvars !== null) {
+ return $this;
+ }
+
+ $vars = $this->object->host->customvar->execute();
+
+ $customVars = [];
+ foreach ($vars as $row) {
+ $customVars[$row->name] = $row->value;
+ }
+
+ $this->rawHostCustomvars = $customVars;
+
+ return $this;
+ }
+
+ public function fetchComments()
+ {
+ $this->comments = [];
+
+ return $this;
+ }
+
+ public function fetchContactgroups()
+ {
+ $this->contactgroups = [];
+
+ return $this;
+ }
+
+ public function fetchContacts()
+ {
+ $this->contacts = [];
+
+ return $this;
+ }
+
+ public function fetchCustomvars(): self
+ {
+ if ($this->customvars !== null) {
+ return $this;
+ }
+
+ $this->customvars = (new CustomvarFlat())->unflattenVars($this->object->customvar_flat);
+
+ return $this;
+ }
+
+ public function fetchHostVariables()
+ {
+ if (isset($this->hostVariables)) {
+ return $this;
+ }
+
+ $this->hostVariables = [];
+ foreach ($this->object->customvar as $customvar) {
+ $this->hostVariables[strtolower($customvar->name)] = json_decode($customvar->value);
+ }
+
+ return $this;
+ }
+
+ public function fetchServiceVariables()
+ {
+ if (isset($this->serviceVariables)) {
+ return $this;
+ }
+
+ $this->serviceVariables = [];
+ foreach ($this->object->customvar as $customvar) {
+ $this->serviceVariables[strtolower($customvar->name)] = json_decode($customvar->value);
+ }
+
+ return $this;
+ }
+
+ public function fetchDowntimes()
+ {
+ $this->downtimes = [];
+
+ return $this;
+ }
+
+ public function fetchEventhistory()
+ {
+ $this->eventhistory = [];
+
+ return $this;
+ }
+
+ public function fetchHostgroups()
+ {
+ if ($this->type === self::TYPE_HOST) {
+ $hostname = $this->object->name;
+ $hostgroupQuery = clone $this->object->hostgroup;
+ } else {
+ $hostname = $this->object->host->name;
+ $hostgroupQuery = clone $this->object->host->hostgroup;
+ }
+
+ $hostgroupQuery
+ ->columns(['name', 'display_name'])
+ ->filter(Filter::equal('host.name', $hostname));
+
+ /** @var Query $hostgroupQuery */
+ $this->hostgroups = [];
+ foreach ($hostgroupQuery as $hostgroup) {
+ $this->hostgroups[$hostgroup->name] = $hostgroup->display_name;
+ }
+
+ return $this;
+ }
+
+ public function fetchServicegroups()
+ {
+ if ($this->type === self::TYPE_HOST) {
+ $hostname = $this->object->name;
+ $query = Servicegroup::on($this->getDb());
+ } else {
+ $hostname = $this->object->host->name;
+ $query = (clone $this->object->servicegroup);
+ }
+
+ $query
+ ->columns(['name', 'display_name'])
+ ->filter(Filter::equal('host.name', $hostname));
+
+ if ($this->type === self::TYPE_SERVICE) {
+ $query->filter(Filter::equal('service.name', $this->object->name));
+ }
+
+ $this->servicegroups = [];
+ foreach ($query as $serviceGroup) {
+ $this->servicegroups[$serviceGroup->name] = $serviceGroup->display_name;
+ }
+
+ return $this;
+ }
+
+ public function fetchStats()
+ {
+ $query = ServicestateSummary::on($this->getDb());
+
+ if ($this->type === self::TYPE_HOST) {
+ $query->filter(Filter::equal('host.name', $this->object->name));
+ } else {
+ $query->filter(Filter::all(
+ Filter::equal('host.name', $this->object->host->name),
+ Filter::equal('service.name', $this->object->name)
+ ));
+ }
+
+ $result = $query->first();
+
+ $this->stats = (object) [
+ 'services_total' => $result->services_total,
+ 'services_ok' => $result->services_ok,
+ 'services_critical' => $result->services_critical_handled + $result->services_critical_unhandled,
+ 'services_critical_unhandled' => $result->services_critical_unhandled,
+ 'services_critical_handled' => $result->services_critical_handled,
+ 'services_warning' => $result->services_warning_handled + $result->services_warning_unhandled,
+ 'services_warning_unhandled' => $result->services_warning_unhandled,
+ 'services_warning_handled' => $result->services_warning_handled,
+ 'services_unknown' => $result->services_unknown_handled + $result->services_unknown_unhandled,
+ 'services_unknown_unhandled' => $result->services_unknown_unhandled,
+ 'services_unknown_handled' => $result->services_unknown_handled,
+ 'services_pending' => $result->services_pending
+ ];
+
+ return $this;
+ }
+
+ public function __get($name)
+ {
+ if (property_exists($this, $name)) {
+ if ($this->$name === null) {
+ $fetchMethod = 'fetch' . ucfirst($name);
+ $this->$fetchMethod();
+ }
+
+ return $this->$name;
+ }
+
+ if (preg_match('/^_(host|service)_(.+)/i', $name, $matches)) {
+ switch (strtolower($matches[1])) {
+ case $this->type:
+ $customvars = $this->fetchRawCustomvars()->rawCustomvars;
+ break;
+ case self::TYPE_HOST:
+ $customvars = $this->fetchRawHostCustomvars()->rawHostCustomvars;
+ break;
+ case self::TYPE_SERVICE:
+ throw new LogicException('Cannot fetch service custom variables for non-service objects');
+ }
+
+ $variableName = strtolower($matches[2]);
+ if (isset($customvars[$variableName])) {
+ return $customvars[$variableName];
+ }
+
+ return null; // Unknown custom variables MUST NOT throw an error
+ }
+
+ if (! array_key_exists($name, $this->legacyColumns) && ! $this->object->hasProperty($name)) {
+ if (isset($this->customvars[$name])) {
+ return $this->customvars[$name];
+ }
+
+ if (substr($name, 0, strlen($this->prefix)) !== $this->prefix) {
+ $name = $this->prefix . $name;
+ }
+ }
+
+ if (array_key_exists($name, $this->legacyColumns)) {
+ $opts = $this->legacyColumns[$name];
+ if ($opts === null) {
+ return null;
+ }
+
+ $path = $opts['path'];
+ $value = null;
+
+ if (! empty($path)) {
+ $value = $this->object;
+
+ do {
+ $col = array_shift($path);
+ $value = $value->$col;
+ } while (! empty($path) && $value !== null);
+ }
+
+ if (isset($opts['type'])) {
+ $method = 'get' . ucfirst($opts['type']) . 'Type';
+ $value = $this->$method($value);
+ }
+
+ return $value;
+ }
+
+ return $this->object->$name;
+ }
+
+ public function __isset($name)
+ {
+ if (property_exists($this, $name)) {
+ return isset($this->$name);
+ }
+
+ if (isset($this->legacyColumns[$name]) || isset($this->object->$name)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * @throws NotImplementedError Don't use!
+ */
+ protected function getDataView()
+ {
+ throw new NotImplementedError('getDataView() is not supported');
+ }
+
+ /**
+ * Get the bool type of the given value as an int
+ *
+ * @param bool|string $value
+ *
+ * @return ?int
+ */
+ private function getBoolType($value)
+ {
+ switch ($value) {
+ case false:
+ return 0;
+ case true:
+ return 1;
+ case 'sticky':
+ return 2;
+ }
+ }
+}
diff --git a/library/Icingadb/Compat/CompatService.php b/library/Icingadb/Compat/CompatService.php
new file mode 100644
index 0000000..8d95950
--- /dev/null
+++ b/library/Icingadb/Compat/CompatService.php
@@ -0,0 +1,159 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Compat;
+
+use Icinga\Module\Monitoring\Object\Service;
+
+class CompatService extends Service
+{
+ use CompatObject;
+
+ private $legacyColumns = [
+ 'instance_name' => null,
+ 'host_attempt' => null,
+ 'host_icon_image' => ['path' => ['host', 'icon_image', 'icon_image']],
+ 'host_icon_image_alt' => ['path' => ['host', 'icon_image_alt']],
+ 'host_acknowledged' => [
+ 'path' => ['host', 'state', 'is_acknowledged'],
+ 'type' => 'bool'
+ ],
+ 'host_active_checks_enabled' => [
+ 'path' => ['host', 'active_checks_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_address' => ['path' => ['host', 'address']],
+ 'host_address6' => ['path' => ['host', 'address6']],
+ 'host_alias' => ['path' => ['host', 'display_name']],
+ 'host_display_name' => ['path' => ['host', 'display_name']],
+ 'host_handled' => [
+ 'path' => ['host', 'state', 'is_handled'],
+ 'type' => 'bool'
+ ],
+ 'host_in_downtime' => [
+ 'path' => ['host', 'state', 'in_downtime'],
+ 'type' => 'bool'
+ ],
+ 'host_is_flapping' => [
+ 'path' => ['host', 'state', 'is_flapping'],
+ 'type' => 'bool'
+ ],
+ 'host_last_state_change' => ['path' => ['host', 'state', 'last_state_change']],
+ 'host_name' => ['path' => ['host', 'name']],
+ 'host_notifications_enabled' => [
+ 'path' => ['host', 'notifications_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_passive_checks_enabled' => [
+ 'path' => ['host', 'passive_checks_enabled'],
+ 'type' => 'bool'
+ ],
+ 'host_state' => ['path' => ['host', 'state', 'soft_state']],
+ 'host_state_type' => ['path' => ['host', 'state', 'state_type']],
+ 'service_icon_image' => ['path' => ['icon_image', 'icon_image']],
+ 'service_icon_image_alt' => ['path' => ['icon_image_alt']],
+ 'service_acknowledged' => [
+ 'path' => ['state', 'is_acknowledged'],
+ 'type' => 'bool'
+ ],
+ 'service_acknowledgement_type' => [
+ 'path' => ['state', 'is_acknowledged'],
+ 'type' => 'bool'
+ ],
+ 'service_action_url' => ['path' => ['action_url', 'action_url']],
+ 'action_url' => ['path' => ['action_url', 'action_url']],
+ 'service_active_checks_enabled' => [
+ 'path' => ['active_checks_enabled'],
+ 'type' => 'bool'
+ ],
+ 'service_active_checks_enabled_changed' => null,
+ 'service_attempt' => null,
+ 'service_check_command' => ['path' => ['checkcommand_name']],
+ 'service_check_execution_time' => ['path' => ['state', 'execution_time']],
+ 'service_check_interval' => ['path' => ['check_interval']],
+ 'service_check_latency' => ['path' => ['state', 'latency']],
+ 'service_check_source' => ['path' => ['state', 'check_source']],
+ 'service_check_timeperiod' => ['path' => ['check_timeperiod_name']],
+ 'service_current_notification_number' => null,
+ 'service_description' => ['path' => ['name']],
+ 'service_display_name' => ['path' => ['display_name']],
+ 'service_event_handler_enabled' => [
+ 'path' => ['event_handler_enabled'],
+ 'type' => 'bool'
+ ],
+ 'service_event_handler_enabled_changed' => null,
+ 'service_flap_detection_enabled' => [
+ 'path' => ['flapping_enabled'],
+ 'type' => 'bool'
+ ],
+ 'service_flap_detection_enabled_changed' => null,
+ 'service_handled' => [
+ 'path' => ['state', 'is_handled'],
+ 'type' => 'bool'
+ ],
+ 'service_in_downtime' => [
+ 'path' => ['state', 'in_downtime'],
+ 'type' => 'bool'
+ ],
+ 'service_is_flapping' => [
+ 'path' => ['state', 'is_flapping'],
+ 'type' => 'bool'
+ ],
+ 'service_is_reachable' => [
+ 'path' => ['state', 'is_reachable'],
+ 'type' => 'bool'
+ ],
+ 'service_last_check' => ['path' => ['state', 'last_update']],
+ 'service_last_notification' => null,
+ 'service_last_state_change' => ['path' => ['state', 'last_state_change']],
+ 'service_long_output' => ['path' => ['state', 'long_output']],
+ 'service_next_check' => ['path' => ['state', 'next_check']],
+ 'service_next_update' => ['path' => ['state', 'next_update']],
+ 'service_notes' => ['path' => ['notes']],
+ 'service_notes_url' => ['path' => ['notes_url', 'notes_url']],
+ 'service_notifications_enabled' => [
+ 'path' => ['notifications_enabled'],
+ 'type' => 'bool'
+ ],
+ 'service_notifications_enabled_changed' => null,
+ 'service_obsessing' => null,
+ 'service_obsessing_changed' => null,
+ 'service_output' => ['path' => ['state', 'output']],
+ 'service_passive_checks_enabled' => [
+ 'path' => ['passive_checks_enabled'],
+ 'type' => 'bool'
+ ],
+ 'service_passive_checks_enabled_changed' => null,
+ 'service_percent_state_change' => null,
+ 'service_perfdata' => [
+ 'path' => ['state', 'performance_data'],
+ 'type' => 'bool'
+ ],
+ 'service_process_perfdata' => [
+ 'path' => ['perfdata_enabled'],
+ 'type' => 'bool'
+ ],
+ 'service_state' => ['path' => ['state', 'soft_state']],
+ 'service_state_type' => ['path' => ['state', 'state_type']]
+ ];
+
+ /**
+ * Get this service's host
+ *
+ * @return CompatHost
+ */
+ public function getHost(): CompatHost
+ {
+ if ($this->host === null) {
+ $this->host = new CompatHost($this->object->host);
+ }
+
+ return $this->host;
+ }
+
+ protected function fetchHost()
+ {
+ $this->getHost();
+ }
+}
diff --git a/library/Icingadb/Compat/UrlMigrator.php b/library/Icingadb/Compat/UrlMigrator.php
new file mode 100644
index 0000000..6eef3ca
--- /dev/null
+++ b/library/Icingadb/Compat/UrlMigrator.php
@@ -0,0 +1,1254 @@
+<?php
+
+/* Icinga DB Web | (c) 2020 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Icingadb\Compat;
+
+use InvalidArgumentException;
+use ipl\Stdlib\Filter;
+use ipl\Web\Filter\QueryString;
+use ipl\Web\Url;
+
+class UrlMigrator
+{
+ const NO_YES = ['n', 'y'];
+ const USE_EXPR = 'use-expr';
+ const SORT_ONLY = 'sort-only';
+ const LOWER_EXPR = 'lower-expr';
+ const DROP = 'drop';
+
+ const SUPPORTED_PATHS = [
+ 'monitoring/list/hosts' => ['hosts', 'icingadb/hosts'],
+ 'monitoring/hosts/show' => ['multipleHosts', 'icingadb/hosts/details'],
+ 'monitoring/host/show' => ['host', 'icingadb/host'],
+ 'monitoring/host/services' => ['host', 'icingadb/host/services'],
+ 'monitoring/host/history' => ['host', 'icingadb/host/history'],
+ 'monitoring/list/services' => ['services', 'icingadb/services'],
+ 'monitoring/list/servicegrid' => ['servicegrid', 'icingadb/services/grid'],
+ 'monitoring/services/show' => ['multipleServices', 'icingadb/services/details'],
+ 'monitoring/service/show' => ['service', 'icingadb/service'],
+ 'monitoring/service/history' => ['service', 'icingadb/service/history'],
+ 'monitoring/list/hostgroups' => ['hostgroups', 'icingadb/hostgroups'],
+ 'monitoring/list/servicegroups' => ['servicegroups', 'icingadb/servicegroups'],
+ 'monitoring/list/contactgroups' => ['contactgroups', 'icingadb/usergroups'],
+ 'monitoring/list/contacts' => ['contacts', 'icingadb/users'],
+ 'monitoring/list/comments' => ['comments', 'icingadb/comments'],
+ 'monitoring/list/downtimes' => ['downtimes', 'icingadb/downtimes'],
+ 'monitoring/list/eventhistory' => ['history', 'icingadb/history'],
+ 'monitoring/list/notifications' => ['notificationHistory', 'icingadb/notifications'],
+ 'monitoring/health/info' => [null, 'icingadb/health'],
+ 'monitoring/health/stats' => [null, 'icingadb/health'],
+ 'monitoring/tactical' => ['services', 'icingadb/tactical']
+ ];
+
+ public static function isSupportedUrl(Url $url): bool
+ {
+ $supportedPaths = self::SUPPORTED_PATHS;
+ return isset($supportedPaths[$url->getPath()]);
+ }
+
+ public static function hasQueryTransformer(string $name): bool
+ {
+ return method_exists(new self(), $name . 'Columns');
+ }
+
+ public static function transformUrl(Url $url): Url
+ {
+ if (! self::isSupportedUrl($url)) {
+ throw new InvalidArgumentException(sprintf('Url path "%s" is not supported', $url->getPath()));
+ }
+
+ list($queryTransformer, $dbRoute) = self::SUPPORTED_PATHS[$url->getPath()];
+
+ $url = clone $url;
+ $url->setPath($dbRoute);
+
+ if (! $url->getParams()->isEmpty()) {
+ $filter = QueryString::parse((string) $url->getParams());
+ $filter = self::transformFilter($filter, $queryTransformer);
+ if ($filter) {
+ $url->setQueryString(QueryString::render($filter));
+ }
+ }
+
+ return $url;
+ }
+
+ /**
+ * Transform the given legacy filter
+ *
+ * @param Filter\Rule $filter
+ * @param string|null $queryTransformer
+ *
+ * @return Filter\Rule|false
+ */
+ public static function transformFilter(Filter\Rule $filter, string $queryTransformer = null)
+ {
+ $transformer = new self();
+
+ $columns = $transformer::commonColumns();
+ if ($queryTransformer !== null) {
+ if (! self::hasQueryTransformer($queryTransformer)) {
+ throw new InvalidArgumentException(sprintf('Transformer "%s" is not supported', $queryTransformer));
+ }
+
+ $columns = array_merge($columns, $transformer->{$queryTransformer . 'Columns'}());
+ }
+
+ $rewritten = $transformer->rewrite($filter, $columns);
+ return $rewritten === false ? false : ($rewritten instanceof Filter\Rule ? $rewritten : $filter);
+ }
+
+ /**
+ * Rewrite the given filter and legacy columns
+ *
+ * @param Filter\Rule $filter
+ * @param array $legacyColumns
+ * @param Filter\Chain|null $parent
+ *
+ * @return ?mixed
+ */
+ protected function rewrite(Filter\Rule $filter, array $legacyColumns, Filter\Chain $parent = null)
+ {
+ $rewritten = null;
+ if ($filter instanceof Filter\Condition) {
+ $column = $filter->getColumn();
+
+ if (isset($legacyColumns[$column])) {
+ if ($legacyColumns[$column] === self::DROP) {
+ return false;
+ } elseif (is_callable($legacyColumns[$column])) {
+ return $legacyColumns[$column]($filter);
+ } elseif (! is_array($legacyColumns[$column])) {
+ return null;
+ }
+
+ foreach ($legacyColumns[$column] as $modelPath => $exprRule) {
+ break;
+ }
+
+ $rewritten = $filter->setColumn($modelPath);
+
+ switch (true) {
+ case $exprRule === self::USE_EXPR:
+ break;
+ case $exprRule === self::LOWER_EXPR:
+ $filter->setValue(strtolower($filter->getValue()));
+ break;
+ case is_array($exprRule) && isset($exprRule[$filter->getValue()]):
+ $filter->setValue($exprRule[$filter->getValue()]);
+ break;
+ default:
+ $filter->setValue($exprRule);
+ }
+ } elseif ($column === 'sort') {
+ $column = $filter->getValue();
+ if (isset($legacyColumns[$column])) {
+ if ($legacyColumns[$column] === self::DROP) {
+ return false;
+ } elseif (! is_array($legacyColumns[$column])) {
+ return $rewritten;
+ }
+
+ $column = key($legacyColumns[$column]);
+
+ $rewritten = $filter->setValue($column);
+ }
+
+ if ($parent !== null) {
+ foreach ($parent as $child) {
+ if ($child instanceof Filter\Condition && $child->getColumn() === 'dir') {
+ $dir = $child->getValue();
+
+ $rewritten = $filter->setValue("{$column} {$dir}");
+
+ $parent->remove($child);
+ }
+ }
+ }
+ } elseif ($column === 'dir') {
+ if ($parent !== null) {
+ foreach ($parent as $child) {
+ if ($child instanceof Filter\Condition && $child->getColumn() === 'sort') {
+ return null;
+ }
+ }
+ }
+
+ return false;
+ } elseif (preg_match('/^_(host|service)_([\w.]+)/i', $column, $groups)) {
+ $rewritten = $filter->setColumn($groups[1] . '.vars.' . $groups[2]);
+ }
+ } else {
+ /** @var Filter\Chain $filter */
+ foreach ($filter as $child) {
+ $retVal = $this->rewrite(
+ $child instanceof Filter\Condition ? clone $child : $child,
+ $legacyColumns,
+ $filter
+ );
+ if ($retVal === false) {
+ $filter->remove($child);
+ } elseif ($retVal instanceof Filter\Rule) {
+ $filter->replace($child, $retVal);
+ }
+ }
+ }
+
+ return $rewritten;
+ }
+
+ protected static function commonColumns(): array
+ {
+ return [
+
+ // Filter columns
+ 'host' => [
+ 'host.name_ci' => self::USE_EXPR
+ ],
+ 'host_display_name' => [
+ 'host.display_name' => self::USE_EXPR
+ ],
+ 'host_alias' => self::DROP,
+ 'hostgroup' => [
+ 'hostgroup.name_ci' => self::USE_EXPR
+ ],
+ 'hostgroup_alias' => [
+ 'hostgroup.display_name' => self::USE_EXPR
+ ],
+ 'service' => [
+ 'service.name_ci' => self::USE_EXPR
+ ],
+ 'service_display_name' => [
+ 'service.display_name' => self::USE_EXPR
+ ],
+ 'servicegroup' => [
+ 'servicegroup.name_ci' => self::USE_EXPR
+ ],
+ 'servicegroup_alias' => [
+ 'servicegroup.display_name' => self::USE_EXPR
+ ],
+
+ // Restriction columns
+ 'instance_name' => self::DROP,
+ 'host_name' => [
+ 'host.name' => self::USE_EXPR
+ ],
+ 'hostgroup_name' => [
+ 'hostgroup.name' => self::USE_EXPR
+ ],
+ 'service_description' => [
+ 'service.name' => self::USE_EXPR
+ ],
+ 'servicegroup_name' => [
+ 'servicegroup.name' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function hostsColumns(): array
+ {
+ return [
+
+ // Extraordinary columns
+ 'addColumns' => function ($filter) {
+ /** @var Filter\Condition $filter */
+ $legacyColumns = array_filter(array_map('trim', explode(',', $filter->getValue())));
+
+ $columns = [
+ 'host.state.soft_state',
+ 'host.state.last_state_change',
+ 'host.icon_image.icon_image',
+ 'host.display_name',
+ 'host.state.output',
+ 'host.state.performance_data',
+ 'host.state.is_problem'
+ ];
+ foreach ($legacyColumns as $column) {
+ if (($c = self::transformFilter(Filter::equal($column, 'bogus'), 'hosts')) !== false) {
+ if ($c instanceof Filter\Condition) {
+ $columns[] = $c->getColumn();
+ }
+ }
+ }
+
+ if (empty($columns)) {
+ return false;
+ }
+
+ return Filter::equal('columns', implode(',', $columns));
+ },
+
+ // Query columns
+ 'host_acknowledged' => [
+ 'host.state.is_acknowledged' => self::NO_YES
+ ],
+ 'host_acknowledgement_type' => [
+ 'host.state.is_acknowledged' => array_merge(self::NO_YES, ['sticky'])
+ ],
+ 'host_action_url' => [
+ 'host.action_url.action_url' => self::USE_EXPR
+ ],
+ 'host_active_checks_enabled' => [
+ 'host.active_checks_enabled' => self::NO_YES
+ ],
+ 'host_active_checks_enabled_changed' => self::DROP,
+ 'host_address' => [
+ 'host.address' => self::USE_EXPR
+ ],
+ 'host_address6' => [
+ 'host.address6' => self::USE_EXPR
+ ],
+ 'host_alias' => self::DROP,
+ 'host_check_command' => [
+ 'host.checkcommand_name' => self::USE_EXPR
+ ],
+ 'host_check_execution_time' => [
+ 'host.state.execution_time' => self::USE_EXPR
+ ],
+ 'host_check_latency' => [
+ 'host.state.latency' => self::USE_EXPR
+ ],
+ 'host_check_source' => [
+ 'host.state.check_source' => self::USE_EXPR
+ ],
+ 'host_check_timeperiod' => [
+ 'host.check_timeperiod_name' => self::USE_EXPR
+ ],
+ 'host_current_check_attempt' => [
+ 'host.state.check_attempt' => self::USE_EXPR
+ ],
+ 'host_current_notification_number' => self::DROP,
+ 'host_display_name' => [
+ 'host.display_name' => self::USE_EXPR
+ ],
+ 'host_event_handler_enabled' => [
+ 'host.event_handler_enabled' => self::NO_YES
+ ],
+ 'host_event_handler_enabled_changed' => self::DROP,
+ 'host_flap_detection_enabled' => [
+ 'host.flapping_enabled' => self::NO_YES
+ ],
+ 'host_flap_detection_enabled_changed' => self::DROP,
+ 'host_handled' => [
+ 'host.state.is_handled' => self::NO_YES
+ ],
+ 'host_hard_state' => [
+ 'host.state.hard_state' => self::USE_EXPR
+ ],
+ 'host_in_downtime' => [
+ 'host.state.in_downtime' => self::NO_YES
+ ],
+ 'host_ipv4' => [
+ 'host.address_bin' => self::USE_EXPR
+ ],
+ 'host_is_flapping' => [
+ 'host.state.is_flapping' => self::NO_YES
+ ],
+ 'host_is_reachable' => [
+ 'host.state.is_reachable' => self::NO_YES
+ ],
+ 'host_last_check' => [
+ 'host.state.last_update' => self::USE_EXPR
+ ],
+ 'host_last_notification' => self::DROP,
+ 'host_last_state_change' => [
+ 'host.state.last_state_change' => self::USE_EXPR
+ ],
+ 'host_last_state_change_ts' => [
+ 'host.state.last_state_change' => self::USE_EXPR
+ ],
+ 'host_long_output' => [
+ 'host.state.long_output' => self::USE_EXPR
+ ],
+ 'host_max_check_attempts' => [
+ 'host.max_check_attempts' => self::USE_EXPR
+ ],
+ 'host_modified_host_attributes' => self::DROP,
+ 'host_name' => [
+ 'host.name' => self::USE_EXPR
+ ],
+ 'host_next_check' => [
+ 'host.state.next_check' => self::USE_EXPR
+ ],
+ 'host_notes_url' => [
+ 'host.notes_url.notes_url' => self::USE_EXPR
+ ],
+ 'host_notifications_enabled' => [
+ 'host.notifications_enabled' => self::NO_YES
+ ],
+ 'host_notifications_enabled_changed' => self::DROP,
+ 'host_obsessing' => self::DROP,
+ 'host_obsessing_changed' => self::DROP,
+ 'host_output' => [
+ 'host.state.output' => self::USE_EXPR
+ ],
+ 'host_passive_checks_enabled' => [
+ 'host.passive_checks_enabled' => self::NO_YES
+ ],
+ 'host_passive_checks_enabled_changed' => self::DROP,
+ 'host_percent_state_change' => self::DROP,
+ 'host_perfdata' => [
+ 'host.state.performance_data' => self::USE_EXPR
+ ],
+ 'host_problem' => [
+ 'host.state.is_problem' => self::NO_YES
+ ],
+ 'host_severity' => [
+ 'host.state.severity' => self::USE_EXPR
+ ],
+ 'host_state' => [
+ 'host.state.soft_state' => self::USE_EXPR
+ ],
+ 'host_state_type' => [
+ 'host.state.state_type' => ['soft', 'hard']
+ ],
+ 'host_unhandled' => [
+ 'host.state.is_handled' => array_reverse(self::NO_YES)
+ ],
+
+ // Filter columns
+ 'host_contact' => [
+ 'host.user.name' => self::USE_EXPR
+ ],
+ 'host_contactgroup' => [
+ 'host.usergroup.name' => self::USE_EXPR
+ ],
+
+ // Query columns the dataview doesn't include, added here because it's possible to filter for them anyway
+ 'host_check_interval' => self::DROP,
+ 'host_icon_image' => self::DROP,
+ 'host_icon_image_alt' => self::DROP,
+ 'host_notes' => self::DROP,
+ 'object_type' => self::DROP,
+ 'object_id' => self::DROP,
+ 'host_attempt' => self::DROP,
+ 'host_check_type' => self::DROP,
+ 'host_event_handler' => self::DROP,
+ 'host_failure_prediction_enabled' => self::DROP,
+ 'host_is_passive_checked' => self::DROP,
+ 'host_last_hard_state' => self::DROP,
+ 'host_last_hard_state_change' => self::DROP,
+ 'host_last_time_down' => self::DROP,
+ 'host_last_time_unreachable' => self::DROP,
+ 'host_last_time_up' => self::DROP,
+ 'host_next_notification' => self::DROP,
+ 'host_next_update' => function ($filter) {
+ /** @var Filter\Condition $filter */
+ if ($filter->getValue() !== 'now') {
+ return false;
+ }
+
+ // Doesn't get dropped because there's a default dashlet using it..
+ // Though since this dashlet uses it to check for overdue hosts we'll
+ // replace it as next_update is volatile (only in redis up2date)
+ return Filter::equal('host.state.is_overdue', $filter instanceof Filter\LessThan ? 'y' : 'n');
+ },
+ 'host_no_more_notifications' => self::DROP,
+ 'host_normal_check_interval' => self::DROP,
+ 'host_problem_has_been_acknowledged' => self::DROP,
+ 'host_process_performance_data' => self::DROP,
+ 'host_retry_check_interval' => self::DROP,
+ 'host_scheduled_downtime_depth' => self::DROP,
+ 'host_status_update_time' => self::DROP,
+ 'problems' => self::DROP
+ ];
+ }
+
+ protected static function multipleHostsColumns(): array
+ {
+ return array_merge(
+ static::hostsColumns(),
+ [
+ 'host' => [
+ 'host.name' => self::USE_EXPR
+ ]
+ ]
+ );
+ }
+
+ protected static function hostColumns(): array
+ {
+ return [
+ 'host' => [
+ 'name' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function servicesColumns(): array
+ {
+ return [
+
+ // Extraordinary columns
+ 'addColumns' => function ($filter) {
+ /** @var Filter\Condition $filter */
+ $legacyColumns = array_filter(array_map('trim', explode(',', $filter->getValue())));
+
+ $columns = [
+ 'service.state.soft_state',
+ 'service.state.last_state_change',
+ 'service.icon_image.icon_image',
+ 'service.display_name',
+ 'service.host.display_name',
+ 'service.state.output',
+ 'service.state.performance_data',
+ 'service.state.is_problem'
+ ];
+ foreach ($legacyColumns as $column) {
+ if (($c = self::transformFilter(Filter::equal($column, 'bogus'), 'services')) !== false) {
+ if ($c instanceof Filter\Condition) {
+ $columns[] = $c->getColumn();
+ }
+ }
+ }
+
+ if (empty($columns)) {
+ return false;
+ }
+
+ return Filter::equal('columns', implode(',', $columns));
+ },
+
+ // Query columns
+ 'host_acknowledged' => [
+ 'host.state.is_acknowledged' => self::NO_YES
+ ],
+ 'host_action_url' => [
+ 'host.action_url.action_url' => self::USE_EXPR
+ ],
+ 'host_active_checks_enabled' => [
+ 'host.active_checks_enabled' => self::NO_YES
+ ],
+ 'host_address' => [
+ 'host.address' => self::USE_EXPR
+ ],
+ 'host_address6' => [
+ 'host.address6' => self::USE_EXPR
+ ],
+ 'host_alias' => self::DROP,
+ 'host_check_source' => [
+ 'host.state.check_source' => self::USE_EXPR
+ ],
+ 'host_display_name' => [
+ 'host.display_name' => self::USE_EXPR
+ ],
+ 'host_handled' => [
+ 'host.state.is_handled' => self::NO_YES
+ ],
+ 'host_hard_state' => [
+ 'host.state.hard_state' => self::USE_EXPR
+ ],
+ 'host_in_downtime' => [
+ 'host.state.in_downtime' => self::NO_YES
+ ],
+ 'host_ipv4' => [
+ 'host.address_bin' => self::USE_EXPR
+ ],
+ 'host_is_flapping' => [
+ 'host.state.is_flapping' => self::NO_YES
+ ],
+ 'host_last_check' => [
+ 'host.state.last_update' => self::USE_EXPR
+ ],
+ 'host_last_hard_state' => [
+ 'host.state.previous_hard_state' => self::USE_EXPR
+ ],
+ 'host_last_hard_state_change' => self::DROP,
+ 'host_last_state_change' => [
+ 'host.state.last_state_change' => self::USE_EXPR
+ ],
+ 'host_last_time_down' => self::DROP,
+ 'host_last_time_unreachable' => self::DROP,
+ 'host_last_time_up' => self::DROP,
+ 'host_long_output' => [
+ 'host.state.long_output' => self::USE_EXPR
+ ],
+ 'host_modified_host_attributes' => self::DROP,
+ 'host_notes_url' => [
+ 'host.notes_url.notes_url' => self::USE_EXPR
+ ],
+ 'host_notifications_enabled' => [
+ 'host.notifications_enabled' => self::NO_YES
+ ],
+ 'host_output' => [
+ 'host.state.output' => self::USE_EXPR
+ ],
+ 'host_passive_checks_enabled' => [
+ 'host.passive_checks_enabled' => self::NO_YES
+ ],
+ 'host_perfdata' => [
+ 'host.state.performance_data' => self::USE_EXPR
+ ],
+ 'host_problem' => [
+ 'host.state.is_problem' => self::NO_YES
+ ],
+ 'host_severity' => [
+ 'host.state.severity' => self::USE_EXPR
+ ],
+ 'host_state' => [
+ 'host.state.soft_state' => self::USE_EXPR
+ ],
+ 'host_state_type' => [
+ 'host.state.state_type' => ['soft', 'hard']
+ ],
+ 'service_acknowledged' => [
+ 'service.state.is_acknowledged' => self::NO_YES
+ ],
+ 'service_acknowledgement_type' => [
+ 'service.state.is_acknowledged' => array_merge(self::NO_YES, ['sticky'])
+ ],
+ 'service_action_url' => [
+ 'service.action_url.action_url' => self::USE_EXPR
+ ],
+ 'service_active_checks_enabled' => [
+ 'service.active_checks_enabled' => self::NO_YES
+ ],
+ 'service_active_checks_enabled_changed' => self::DROP,
+ 'service_attempt' => [
+ 'service.state.check_attempt' => self::USE_EXPR
+ ],
+ 'service_check_command' => [
+ 'service.checkcommand_name' => self::USE_EXPR
+ ],
+ 'service_check_source' => [
+ 'service.state.check_source' => self::USE_EXPR
+ ],
+ 'service_check_timeperiod' => [
+ 'service.check_timeperiod_name' => self::USE_EXPR
+ ],
+ 'service_current_check_attempt' => [
+ 'service.state.check_attempt' => self::USE_EXPR
+ ],
+ 'service_current_notification_number' => self::DROP,
+ 'service_display_name' => [
+ 'service.display_name' => self::USE_EXPR
+ ],
+ 'service_event_handler_enabled' => [
+ 'service.event_handler_enabled' => self::NO_YES
+ ],
+ 'service_event_handler_enabled_changed' => self::DROP,
+ 'service_flap_detection_enabled' => [
+ 'service.flapping_enabled' => self::NO_YES
+ ],
+ 'service_flap_detection_enabled_changed' => self::DROP,
+ 'service_handled' => [
+ 'service.state.is_handled' => self::NO_YES
+ ],
+ 'service_hard_state' => [
+ 'service.state.hard_state' => self::USE_EXPR
+ ],
+ 'service_host_name' => [
+ 'host.name' => self::USE_EXPR
+ ],
+ 'service_in_downtime' => [
+ 'service.state.in_downtime' => self::NO_YES
+ ],
+ 'service_is_flapping' => [
+ 'service.state.is_flapping' => self::NO_YES
+ ],
+ 'service_is_reachable' => [
+ 'service.state.is_reachable' => self::NO_YES
+ ],
+ 'service_last_check' => [
+ 'service.state.last_update' => self::USE_EXPR
+ ],
+ 'service_last_hard_state' => [
+ 'service.state.previous_hard_state' => self::USE_EXPR
+ ],
+ 'service_last_hard_state_change' => self::DROP,
+ 'service_last_notification' => self::DROP,
+ 'service_last_state_change' => [
+ 'service.state.last_state_change' => self::USE_EXPR
+ ],
+ 'service_last_state_change_ts' => [
+ 'service.state.last_state_change' => self::USE_EXPR
+ ],
+ 'service_last_time_critical' => self::DROP,
+ 'service_last_time_ok' => self::DROP,
+ 'service_last_time_unknown' => self::DROP,
+ 'service_last_time_warning' => self::DROP,
+ 'service_long_output' => [
+ 'service.state.long_output' => self::USE_EXPR
+ ],
+ 'service_max_check_attempts' => [
+ 'service.max_check_attempts' => self::USE_EXPR
+ ],
+ 'service_modified_service_attributes' => self::DROP,
+ 'service_next_check' => [
+ 'service.state.next_check' => self::USE_EXPR
+ ],
+ 'service_notes' => [
+ 'service.notes' => self::USE_EXPR
+ ],
+ 'service_notes_url' => [
+ 'service.notes_url.notes_url' => self::USE_EXPR
+ ],
+ 'service_notifications_enabled' => [
+ 'service.notifications_enabled' => self::NO_YES
+ ],
+ 'service_notifications_enabled_changed' => self::DROP,
+ 'service_obsessing' => self::DROP,
+ 'service_obsessing_changed' => self::DROP,
+ 'service_output' => [
+ 'service.state.output' => self::USE_EXPR
+ ],
+ 'service_passive_checks_enabled' => [
+ 'service.passive_checks_enabled' => self::USE_EXPR
+ ],
+ 'service_passive_checks_enabled_changed' => self::DROP,
+ 'service_perfdata' => [
+ 'service.state.performance_data' => self::USE_EXPR
+ ],
+ 'service_problem' => [
+ 'service.state.is_problem' => self::NO_YES
+ ],
+ 'service_severity' => [
+ 'service.state.severity' => self::USE_EXPR
+ ],
+ 'service_state' => [
+ 'service.state.soft_state' => self::USE_EXPR
+ ],
+ 'service_state_type' => [
+ 'service.state.state_type' => ['soft', 'hard']
+ ],
+ 'service_unhandled' => [
+ 'service.state.is_handled' => array_reverse(self::NO_YES)
+ ],
+
+ // Filter columns
+ 'host_contact' => [
+ 'host.user.name' => self::USE_EXPR
+ ],
+ 'host_contactgroup' => [
+ 'host.usergroup.name' => self::USE_EXPR
+ ],
+ 'service_contact' => [
+ 'service.user.name' => self::USE_EXPR
+ ],
+ 'service_contactgroup' => [
+ 'service.usergroup.name' => self::USE_EXPR
+ ],
+ 'service_host' => [
+ 'host.name_ci' => self::USE_EXPR
+ ],
+
+ // Query columns the dataview doesn't include, added here because it's possible to filter for them anyway
+ 'host_icon_image' => self::DROP,
+ 'host_icon_image_alt' => self::DROP,
+ 'host_notes' => self::DROP,
+ 'host_acknowledgement_type' => self::DROP,
+ 'host_active_checks_enabled_changed' => self::DROP,
+ 'host_attempt' => self::DROP,
+ 'host_check_command' => self::DROP,
+ 'host_check_execution_time' => self::DROP,
+ 'host_check_latency' => self::DROP,
+ 'host_check_timeperiod_object_id' => self::DROP,
+ 'host_check_type' => self::DROP,
+ 'host_current_check_attempt' => self::DROP,
+ 'host_current_notification_number' => self::DROP,
+ 'host_event_handler' => self::DROP,
+ 'host_event_handler_enabled' => self::DROP,
+ 'host_event_handler_enabled_changed' => self::DROP,
+ 'host_failure_prediction_enabled' => self::DROP,
+ 'host_flap_detection_enabled' => self::DROP,
+ 'host_flap_detection_enabled_changed' => self::DROP,
+ 'host_is_reachable' => self::DROP,
+ 'host_last_notification' => self::DROP,
+ 'host_max_check_attempts' => self::DROP,
+ 'host_next_check' => self::DROP,
+ 'host_next_notification' => self::DROP,
+ 'host_no_more_notifications' => self::DROP,
+ 'host_normal_check_interval' => self::DROP,
+ 'host_notifications_enabled_changed' => self::DROP,
+ 'host_obsessing' => self::DROP,
+ 'host_obsessing_changed' => self::DROP,
+ 'host_passive_checks_enabled_changed' => self::DROP,
+ 'host_percent_state_change' => self::DROP,
+ 'host_problem_has_been_acknowledged' => self::DROP,
+ 'host_process_performance_data' => self::DROP,
+ 'host_retry_check_interval' => self::DROP,
+ 'host_scheduled_downtime_depth' => self::DROP,
+ 'host_status_update_time' => self::DROP,
+ 'host_unhandled' => self::DROP,
+ 'object_type' => self::DROP,
+ 'service_check_interval' => self::DROP,
+ 'service_icon_image' => self::DROP,
+ 'service_icon_image_alt' => self::DROP,
+ 'service_check_execution_time' => self::DROP,
+ 'service_check_latency' => self::DROP,
+ 'service_check_timeperiod_object_id' => self::DROP,
+ 'service_check_type' => self::DROP,
+ 'service_event_handler' => self::DROP,
+ 'service_failure_prediction_enabled' => self::DROP,
+ 'service_is_passive_checked' => self::DROP,
+ 'service_next_notification' => self::DROP,
+ 'service_next_update' => function ($filter) {
+ /** @var Filter\Condition $filter */
+ if ($filter->getValue() !== 'now') {
+ return false;
+ }
+
+ // Doesn't get dropped because there's a default dashlet using it..
+ // Though since this dashlet uses it to check for overdue services we'll
+ // replace it as next_update is volatile (only in redis up2date)
+ return Filter::equal('service.state.is_overdue', $filter instanceof Filter\LessThan ? 'y' : 'n');
+ },
+ 'service_no_more_notifications' => self::DROP,
+ 'service_normal_check_interval' => self::DROP,
+ 'service_percent_state_change' => self::DROP,
+ 'service_problem_has_been_acknowledged' => self::DROP,
+ 'service_process_performance_data' => self::DROP,
+ 'service_retry_check_interval' => self::DROP,
+ 'service_scheduled_downtime_depth' => self::DROP,
+ 'service_status_update_time' => self::DROP,
+ 'problems' => self::DROP,
+ ];
+ }
+
+ protected static function servicegridColumns(): array
+ {
+ return array_merge(
+ static::servicesColumns(),
+ [
+ 'problems' => [
+ 'problems' => self::USE_EXPR
+ ]
+ ]
+ );
+ }
+
+ protected static function multipleServicesColumns(): array
+ {
+ return array_merge(
+ static::servicesColumns(),
+ [
+ 'host' => [
+ 'host.name' => self::USE_EXPR
+ ],
+ 'service' => [
+ 'service.name' => self::USE_EXPR
+ ]
+ ]
+ );
+ }
+
+ protected static function serviceColumns(): array
+ {
+ return [
+ 'host' => [
+ 'host.name' => self::USE_EXPR
+ ],
+ 'service' => [
+ 'name' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function hostgroupsColumns(): array
+ {
+ return [
+
+ // Query columns
+ 'hostgroup_alias' => [
+ 'hostgroup.display_name' => self::USE_EXPR
+ ],
+ 'hosts_severity' => self::SORT_ONLY,
+ 'hosts_total' => self::SORT_ONLY,
+ 'services_total' => self::SORT_ONLY,
+
+ // Filter columns
+ 'host_contact' => [
+ 'host.user.name' => self::USE_EXPR
+ ],
+ 'host_contactgroup' => [
+ 'host.usergroup.name' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function servicegroupsColumns(): array
+ {
+ return [
+
+ // Query columns
+ 'services_severity' => self::SORT_ONLY,
+ 'services_total' => self::SORT_ONLY,
+ 'servicegroup_alias' => [
+ 'servicegroup.display_name' => self::USE_EXPR
+ ],
+
+ // Filter columns
+ 'host_contact' => [
+ 'host.user.name' => self::USE_EXPR
+ ],
+ 'host_contactgroup' => [
+ 'host.usergroup.name' => self::USE_EXPR
+ ],
+ 'service_contact' => [
+ 'service.user.name' => self::USE_EXPR
+ ],
+ 'service_contactgroup' => [
+ 'service.usergroup.name' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function contactgroupsColumns(): array
+ {
+ return [
+
+ // Query columns
+ 'contactgroup_name' => [
+ 'usergroup.name' => self::USE_EXPR
+ ],
+ 'contactgroup_alias' => [
+ 'usergroup.display_name' => self::USE_EXPR
+ ],
+ 'contact_count' => self::DROP,
+
+ // Filter columns
+ 'contactgroup' => [
+ 'usergroup.name_ci' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function contactsColumns(): array
+ {
+ $receivesStateNotifications = function ($state, $type = null) {
+ return function ($filter) use ($state, $type) {
+ /** @var Filter\Condition $filter */
+ $negate = $filter instanceof Filter\Unequal || $filter instanceof Filter\Unlike;
+ switch ($filter->getValue()) {
+ case '0':
+ $filter = Filter::any(
+ Filter::equal('user.notifications_enabled', 'n'),
+ Filter::unequal('user.states', $state)
+ );
+ if ($type !== null) {
+ $filter->add(Filter::unequal('user.types', $type));
+ }
+
+ break;
+ case '1':
+ $filter = Filter::all(
+ Filter::equal('user.notifications_enabled', 'y'),
+ Filter::equal('user.states', $state)
+ );
+ if ($type !== null) {
+ $filter->add(Filter::equal('user.types', $type));
+ }
+
+ break;
+ default:
+ return null;
+ }
+
+ if ($negate) {
+ $filter = Filter::none($filter);
+ }
+
+ return $filter;
+ };
+ };
+
+ return [
+
+ // Query columns
+ 'contact_object_id' => self::DROP,
+ 'contact_id' => [
+ 'user.id' => self::USE_EXPR
+ ],
+ 'contact_name' => [
+ 'user.name' => self::USE_EXPR
+ ],
+ 'contact_alias' => [
+ 'user.display_name' => self::USE_EXPR
+ ],
+ 'contact_email' => [
+ 'user.email' => self::USE_EXPR
+ ],
+ 'contact_pager' => [
+ 'user.pager' => self::USE_EXPR
+ ],
+ 'contact_has_host_notfications' => $receivesStateNotifications(['up', 'down']),
+ 'contact_has_service_notfications' => $receivesStateNotifications(['ok', 'warning', 'critical', 'unknown']),
+ 'contact_can_submit_commands' => self::DROP,
+ 'contact_notify_service_recovery' => $receivesStateNotifications(
+ ['ok', 'warning', 'critical', 'unknown'],
+ 'recovery'
+ ),
+ 'contact_notify_service_warning' => $receivesStateNotifications('warning'),
+ 'contact_notify_service_critical' => $receivesStateNotifications('critical'),
+ 'contact_notify_service_unknown' => $receivesStateNotifications('unknown'),
+ 'contact_notify_service_flapping' => $receivesStateNotifications(
+ ['ok', 'warning', 'critical', 'unknown'],
+ ['flapping_start', 'flapping_end']
+ ),
+ 'contact_notify_service_downtime' => $receivesStateNotifications(
+ ['ok', 'warning', 'critical', 'unknown'],
+ ['downtime_start', 'downtime_end', 'downtime_removed']
+ ),
+ 'contact_notify_host_recovery' => $receivesStateNotifications(['up', 'down'], 'recovery'),
+ 'contact_notify_host_down' => $receivesStateNotifications('down'),
+ 'contact_notify_host_unreachable' => self::DROP,
+ 'contact_notify_host_flapping' => $receivesStateNotifications(
+ ['up', 'down'],
+ ['flapping_start', 'flapping_end']
+ ),
+ 'contact_notify_host_downtime' => $receivesStateNotifications(
+ ['up', 'down'],
+ ['downtime_start', 'downtime_end', 'downtime_removed']
+ ),
+ 'contact_notify_host_timeperiod' => function ($filter) {
+ /** @var Filter\Condition $filter */
+ $filter->setColumn('user.timeperiod.name_ci');
+ return Filter::all(
+ $filter,
+ Filter::equal('user.states', ['up', 'down'])
+ );
+ },
+ 'contact_notify_service_timeperiod' => function ($filter) {
+ /** @var Filter\Condition $filter */
+ $filter->setColumn('user.timeperiod.name_ci');
+ return Filter::all(
+ $filter,
+ Filter::equal('user.states', ['ok', 'warning', 'critical', 'unknown'])
+ );
+ },
+
+ // Filter columns
+ 'contact' => [
+ 'user.name_ci' => self::USE_EXPR
+ ],
+ 'contactgroup' => [
+ 'usergroup.name_ci' => self::USE_EXPR
+ ],
+ 'contactgroup_name' => [
+ 'usergroup.name' => self::USE_EXPR
+ ],
+ 'contactgroup_alias' => [
+ 'usergroup.display_name' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function commentsColumns(): array
+ {
+ return [
+
+ // Query columns
+ 'comment_author_name' => [
+ 'comment.author' => self::USE_EXPR
+ ],
+ 'comment_data' => [
+ 'comment.text' => self::USE_EXPR
+ ],
+ 'comment_expiration' => [
+ 'comment.expire_time' => self::USE_EXPR
+ ],
+ 'comment_internal_id' => self::DROP,
+ 'comment_is_persistent' => [
+ 'comment.is_persistent' => self::NO_YES
+ ],
+ 'comment_name' => [
+ 'comment.name' => self::USE_EXPR
+ ],
+ 'comment_timestamp' => [
+ 'comment.entry_time' => self::USE_EXPR
+ ],
+ 'comment_type' => [
+ 'comment.entry_type' => self::LOWER_EXPR
+ ],
+ 'host_display_name' => [
+ 'host.display_name' => self::USE_EXPR
+ ],
+ 'object_type' => [
+ 'comment.object_type' => self::LOWER_EXPR
+ ],
+ 'service_display_name' => [
+ 'service.display_name' => self::USE_EXPR
+ ],
+ 'service_host_name' => [
+ 'host.name' => self::USE_EXPR
+ ],
+
+ // Filter columns
+ 'comment_author' => [
+ 'comment.author' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function downtimesColumns(): array
+ {
+ return [
+
+ // Query columns
+ 'downtime_author_name' => [
+ 'downtime.author' => self::USE_EXPR
+ ],
+ 'downtime_comment' => [
+ 'downtime.comment' => self::USE_EXPR
+ ],
+ 'downtime_duration' => [
+ 'downtime.flexible_duration' => self::USE_EXPR
+ ],
+ 'downtime_end' => [
+ 'downtime.end_time' => self::USE_EXPR
+ ],
+ 'downtime_entry_time' => [
+ 'downtime.entry_time' => self::USE_EXPR
+ ],
+ 'downtime_internal_id' => self::DROP,
+ 'downtime_is_fixed' => [
+ 'downtime.is_flexible' => array_reverse(self::NO_YES)
+ ],
+ 'downtime_is_flexible' => [
+ 'downtime.is_flexible' => self::NO_YES
+ ],
+ 'downtime_is_in_effect' => [
+ 'downtime.is_in_effect' => self::NO_YES
+ ],
+ 'downtime_name' => [
+ 'downtime.name' => self::USE_EXPR
+ ],
+ 'downtime_scheduled_end' => [
+ 'downtime.scheduled_end_time' => self::USE_EXPR
+ ],
+ 'downtime_scheduled_start' => [
+ 'downtime.scheduled_start_time' => self::USE_EXPR
+ ],
+ 'downtime_start' => [
+ 'downtime.start_time' => self::USE_EXPR
+ ],
+ 'host_display_name' => [
+ 'host.display_name' => self::USE_EXPR
+ ],
+ 'host_state' => [
+ 'host.state.soft_state' => self::USE_EXPR
+ ],
+ 'object_type' => [
+ 'downtime.object_type' => self::LOWER_EXPR
+ ],
+ 'service_display_name' => [
+ 'service.display_name' => self::USE_EXPR
+ ],
+ 'service_host_name' => [
+ 'host.name' => self::USE_EXPR
+ ],
+ 'service_state' => [
+ 'service.state.soft_state' => self::USE_EXPR
+ ],
+
+ // Filter columns
+ 'downtime_author' => [
+ 'downtime.author' => self::USE_EXPR
+ ]
+ ];
+ }
+
+ protected static function historyColumns(): array
+ {
+ return [
+
+ // Query columns
+ 'id' => self::DROP,
+ 'object_type' => [
+ 'history.object_type' => self::LOWER_EXPR
+ ],
+ 'timestamp' => [
+ 'history.event_time' => self::USE_EXPR
+ ],
+ 'state' => [
+ 'history.state.soft_state' => self::USE_EXPR
+ ],
+ 'output' => [
+ 'history.state.output' => self::USE_EXPR
+ ],
+ 'type' => function ($filter) {
+ /** @var Filter\Condition $filter */
+ $expr = strtolower($filter->getValue());
+
+ switch (true) {
+ // NotificationhistoryQuery
+ case substr($expr, 0, 13) === 'notification_':
+ $filter->setColumn('history.notification.type');
+ $filter->setValue([
+ 'notification_ack' => 'acknowledgement',
+ 'notification_flapping' => 'flapping_start',
+ 'notification_flapping_end' => 'flapping_end',
+ 'notification_dt_start' => 'downtime_start',
+ 'notification_dt_end' => 'downtime_end',
+ 'notification_custom' => 'custom',
+ 'notification_state' => ['problem', 'recovery']
+ ][$expr]);
+ return Filter::all($filter, Filter::equal('history.event_type', 'notification'));
+ // StatehistoryQuery
+ case in_array($expr, ['soft_state', 'hard_state'], true):
+ $filter->setColumn('history.state.state_type');
+ $filter->setValue(substr($expr, 0, 4));
+ return Filter::all($filter, Filter::equal('history.event_type', 'state_change'));
+ // DowntimestarthistoryQuery and DowntimeendhistoryQuery
+ case in_array($expr, ['dt_start', 'dt_end'], true):
+ $filter->setColumn('history.event_type');
+ $filter->setValue('downtime_' . substr($expr, 3));
+ return $filter;
+ // CommenthistoryQuery
+ case in_array($expr, ['comment', 'ack'], true):
+ $filter->setColumn('history.comment.entry_type');
+ $filter->setValue($expr);
+ return Filter::all($filter, Filter::equal('history.event_type', 'comment_add'));
+ // CommentdeletionhistoryQuery
+ case in_array($expr, ['comment_deleted', 'ack_deleted'], true):
+ $filter->setColumn('history.comment.entry_type');
+ $filter->setValue($expr);
+ return Filter::all($filter, Filter::equal('history.event_type', 'comment_remove'));
+ // FlappingstarthistoryQuery and CommenthistoryQuery
+ case in_array($expr, ['flapping', 'flapping_deleted'], true):
+ $filter->setColumn('history.event_type');
+ return $filter->setValue($expr === 'flapping' ? 'flapping_start' : 'flapping_end');
+ }
+ }
+ ];
+ }
+
+ protected static function notificationHistoryColumns(): array
+ {
+ return [
+
+ // Query columns
+ 'notification_contact_name' => [
+ 'notification_history.user.name' => self::USE_EXPR
+ ],
+ 'notification_output' => [
+ 'notification_history.text' => self::USE_EXPR
+ ],
+ 'notification_reason' => [
+ 'notification_history.type' => [
+ 0 => ['problem', 'recovery'],
+ 1 => 'acknowledgement',
+ 2 => 'flapping_start',
+ 3 => 'flapping_end',
+ 5 => 'downtime_start',
+ 6 => 'downtime_end',
+ 7 => 'downtime_removed',
+ 8 => 'custom' // ido schema doc says it's `99`, icinga2 though uses `8`
+ ]
+ ],
+ 'notification_state' => [
+ 'notification_history.state' => self::USE_EXPR
+ ],
+ 'notification_timestamp' => [
+ 'notification_history.send_time' => self::USE_EXPR
+ ],
+ 'object_type' => [
+ 'notification_history.object_type' => self::LOWER_EXPR
+ ],
+ 'service_host_name' => [
+ 'host.name' => self::USE_EXPR
+ ]
+ ];
+ }
+}