├── doc ├── img │ ├── cube_move-up.png │ ├── cube_simple.png │ ├── cube_slice.png │ ├── cube_director.png │ └── cube_action-links.png ├── 02-Installation.md.d │ └── From-Source.md ├── 02-Installation.md └── 01-About.md ├── configuration.php ├── phpstan-baseline-8x.neon ├── phpstan-baseline-by-php-version.php ├── library └── Cube │ ├── ProvidedHook │ ├── Icingadb │ │ └── IcingadbSupport.php │ └── Cube │ │ ├── IcingaDbActions.php │ │ └── MonitoringActions.php │ ├── Ido │ ├── DataView │ │ └── Hoststatus.php │ ├── Query │ │ └── HoststatusQuery.php │ ├── ZfSelectWrapper.php │ ├── IdoHostStatusCube.php │ ├── IdoServiceStatusCube.php │ ├── CustomVarDimension.php │ ├── IdoCube.php │ └── DbCube.php │ ├── Dimension.php │ ├── DimensionParams.php │ ├── IcingaDb │ ├── IcingaDbHostStatusCube.php │ ├── IcingaDbServiceStatusCube.php │ ├── CustomVariableDimension.php │ └── IcingaDbCube.php │ ├── Web │ ├── ActionLink.php │ ├── ActionLinks.php │ ├── IdoController.php │ └── Controller.php │ ├── Hook │ ├── ActionsHook.php │ └── IcingaDbActionsHook.php │ ├── CubeRenderer │ ├── HostStatusCubeRenderer.php │ └── ServiceStatusCubeRenderer.php │ ├── Cube.php │ └── CubeRenderer.php ├── application ├── views │ └── scripts │ │ ├── cube-details.phtml │ │ └── cube-index.phtml ├── controllers │ ├── IndexController.php │ ├── IdoHostsController.php │ ├── IdoServicesController.php │ ├── HostsController.php │ └── ServicesController.php └── forms │ └── DimensionsForm.php ├── module.info ├── AUTHORS ├── .mailmap ├── phpstan.neon ├── phpstan-baseline-7x.neon ├── run.php ├── README.md ├── public └── css │ └── module.less ├── LICENSE └── phpstan-baseline-standard.neon /doc/img/cube_move-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icinga/icingaweb2-module-cube/HEAD/doc/img/cube_move-up.png -------------------------------------------------------------------------------- /doc/img/cube_simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icinga/icingaweb2-module-cube/HEAD/doc/img/cube_simple.png -------------------------------------------------------------------------------- /doc/img/cube_slice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icinga/icingaweb2-module-cube/HEAD/doc/img/cube_slice.png -------------------------------------------------------------------------------- /doc/img/cube_director.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icinga/icingaweb2-module-cube/HEAD/doc/img/cube_director.png -------------------------------------------------------------------------------- /doc/img/cube_action-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Icinga/icingaweb2-module-cube/HEAD/doc/img/cube_action-links.png -------------------------------------------------------------------------------- /configuration.php: -------------------------------------------------------------------------------- 1 | menuSection(N_('Reporting'), ['icon' => 'fa-chart-simple', 'priority' => 100]) 6 | ->add($this->translate('Cube'))->setUrl('cube/hosts')->setPriority(10); 7 | -------------------------------------------------------------------------------- /phpstan-baseline-8x.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: "#^Parameter \\#1 \\$callback of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{Zend_Db_Select, string\\} given\\.$#" 5 | count: 1 6 | path: library/Cube/Ido/ZfSelectWrapper.php 7 | -------------------------------------------------------------------------------- /phpstan-baseline-by-php-version.php: -------------------------------------------------------------------------------- 1 | $includes 12 | ]; 13 | -------------------------------------------------------------------------------- /library/Cube/ProvidedHook/Icingadb/IcingadbSupport.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | tabs ?> 4 | 5 |

escape($this->title) ?>

6 | 7 | 8 |
9 | 12 |
13 | -------------------------------------------------------------------------------- /application/controllers/IndexController.php: -------------------------------------------------------------------------------- 1 | redirectNow('cube/hosts' . ($this->params->toString() === '' ? '' : '?' . $this->params->toString())); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /module.info: -------------------------------------------------------------------------------- 1 | Name: Cube 2 | Version: 1.3.3 3 | Requires: 4 | Libraries: icinga-php-library (>=0.13.0) 5 | Modules: monitoring (>= 2.9.0), icingadb (>= 1.0.0) 6 | Description: Cube for Icinga Web 7 | The Cube allows you to analyze data across multiple dimensions in your 8 | Icinga Web frontend. Currently it shows host statistics (total count, 9 | health) grouped by various custom variables in multiple dimensions. It 10 | integrates well with other modules and provides extensible hooks 11 | -------------------------------------------------------------------------------- /library/Cube/Ido/DataView/Hoststatus.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 15 | $this->query = new HoststatusQuery($connection->getResource(), $columns); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Alexander A. Klimov 2 | Eric Lippmann 3 | Feu Mourek 4 | Florian Strohmaier 5 | Johannes Meyer 6 | Ken Jungclaus 7 | Michael Friedrich 8 | Nicolai Buchwitz 9 | Sukhwinder Dhillon 10 | Thomas Gelf 11 | Timm Ortloff 12 | Yonas Habteab 13 | raviks789 14 | sant-swedge 15 | -------------------------------------------------------------------------------- /application/controllers/IdoHostsController.php: -------------------------------------------------------------------------------- 1 | createTabs()->activate('cube/hosts'); 16 | 17 | $this->renderCube(); 18 | } 19 | 20 | protected function getCube(): IdoCube 21 | { 22 | return new IdoHostStatusCube(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /application/controllers/IdoServicesController.php: -------------------------------------------------------------------------------- 1 | createTabs()->activate('cube/services'); 16 | 17 | $this->renderCube(); 18 | } 19 | 20 | protected function getCube(): IdoCube 21 | { 22 | return new IdoServiceStatusCube(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Alexander A. Klimov 2 | Feu Mourek 3 | Feu Mourek 4 | Florian Strohmaier 5 | Michael Friedrich 6 | Sukhwinder Dhillon 7 | Thomas Gelf 8 | Yonas Habteab <57616252+Yonas-net@users.noreply.github.com> 9 | raviks789 <33730024+raviks789@users.noreply.github.com> 10 | -------------------------------------------------------------------------------- /doc/02-Installation.md.d/From-Source.md: -------------------------------------------------------------------------------- 1 | # Installing Icinga Cube from Source 2 | 3 | Please see the Icinga Web documentation on 4 | [how to install modules](https://icinga.com/docs/icinga-web-2/latest/doc/08-Modules/#installation) from source. 5 | Make sure you use `cube` as the module name. The following requirements must also be met. 6 | 7 | ## Requirements 8 | 9 | * PHP (≥7.2) 10 | * [Icinga Web](https://github.com/Icinga/icingaweb2) (≥2.9) 11 | * [Icinga DB Web](https://github.com/Icinga/icingadb-web) (≥1.0) 12 | * [Icinga PHP Library (ipl)](https://github.com/Icinga/icinga-php-library) (≥0.13.0) 13 | 14 | If you are using PostgreSQL, you need at least version 9.5 which provides the `ROLLUP` feature. 15 | 16 | -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline-standard.neon 3 | - phpstan-baseline-by-php-version.php 4 | 5 | parameters: 6 | level: max 7 | 8 | checkFunctionNameCase: true 9 | checkInternalClassCaseSensitivity: true 10 | treatPhpDocTypesAsCertain: false 11 | 12 | paths: 13 | - application 14 | - library 15 | 16 | scanDirectories: 17 | - /icingaweb2 18 | - /usr/share/icinga-php 19 | - /usr/share/icingaweb2-modules 20 | 21 | ignoreErrors: 22 | - 23 | messages: 24 | - '#Unsafe usage of new static\(\)#' 25 | - '#. but return statement is missing#' 26 | reportUnmatched: false 27 | 28 | universalObjectCratesClasses: 29 | - Icinga\Web\View 30 | - ipl\Orm\Model 31 | -------------------------------------------------------------------------------- /phpstan-baseline-7x.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoHostStatusCube\\:\\:listAvailableDimensions\\(\\) should return array but returns array\\\\|false\\.$#" 5 | count: 1 6 | path: library/Cube/Ido/IdoHostStatusCube.php 7 | 8 | - 9 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoServiceStatusCube\\:\\:listAvailableDimensions\\(\\) should return array but returns array\\\\|false\\.$#" 10 | count: 1 11 | path: library/Cube/Ido/IdoServiceStatusCube.php 12 | 13 | - 14 | message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{Zend_Db_Select, string\\} given\\.$#" 15 | count: 1 16 | path: library/Cube/Ido/ZfSelectWrapper.php 17 | -------------------------------------------------------------------------------- /application/views/scripts/cube-index.phtml: -------------------------------------------------------------------------------- 1 |
2 | compact): ?> 3 | 4 | tabs ?> 5 | 6 |

escape($this->title) ?>

7 | form && ! $this->compact): ?> 8 | qlink('Hide settings', $this->url->without('showSettings'), null, ['icon' => 'wrench']) ?> 9 | 10 | qlink('Show settings', $this->url->with('showSettings', true), null, ['icon' => 'wrench']) ?> 11 | 12 | 13 |
14 | 15 |
16 | form && ! $this->compact): ?> 17 | form ?> 18 | 19 | cube): ?> 20 | cube->render($this) ?> 21 | 22 |
23 | -------------------------------------------------------------------------------- /doc/02-Installation.md: -------------------------------------------------------------------------------- 1 | 2 | # Installing Icinga Cube 3 | 4 | The recommended way to install Icinga Cube is to use prebuilt packages for 5 | all supported platforms from our official release repository. 6 | Please note that [Icinga Web](https://icinga.com/docs/icinga-web) is required to run Icinga Cube 7 | and if it is not already set up, it is best to do this first. 8 | 9 | The following steps will guide you through installing and setting up Icinga Cube. 10 | 11 | 12 | 13 | ## Installing the Package 14 | 15 | If the [repository](https://packages.icinga.com) is not configured yet, please add it first. 16 | Then use your distribution's package manager to install the `icinga-cube` package 17 | or install [from source](02-Installation.md.d/From-Source.md). 18 | 19 | 20 | ## Configuring Icinga Cube 21 | 22 | No additional steps are required to set up Icinga Cube and it is ready to use right after installation. 23 | 24 | -------------------------------------------------------------------------------- /application/controllers/HostsController.php: -------------------------------------------------------------------------------- 1 | createTabs()->activate('cube/hosts'); 18 | 19 | $this->renderCube(); 20 | } 21 | 22 | protected function getCube(): IcingaDbCube 23 | { 24 | return new IcingaDbHostStatusCube(); 25 | } 26 | 27 | public function completeAction(): void 28 | { 29 | $suggestions = new ObjectSuggestions(); 30 | $suggestions->setModel(Host::class); 31 | $suggestions->forRequest($this->getServerRequest()); 32 | $this->getDocument()->add($suggestions); 33 | } 34 | 35 | public function searchEditorAction(): void 36 | { 37 | $editor = $this->createSearchEditor( 38 | Host::on($this->getDb()), 39 | $this->preserveParams 40 | ); 41 | 42 | $this->getDocument()->add($editor); 43 | $this->setTitle($this->translate('Adjust Filter')); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /application/controllers/ServicesController.php: -------------------------------------------------------------------------------- 1 | createTabs()->activate('cube/services'); 18 | 19 | $this->renderCube(); 20 | } 21 | 22 | protected function getCube(): IcingaDbCube 23 | { 24 | return new IcingaDbServiceStatusCube(); 25 | } 26 | 27 | public function completeAction(): void 28 | { 29 | $suggestions = new ObjectSuggestions(); 30 | $suggestions->setModel(Service::class); 31 | $suggestions->forRequest($this->getServerRequest()); 32 | $this->getDocument()->add($suggestions); 33 | } 34 | 35 | public function searchEditorAction(): void 36 | { 37 | $editor = $this->createSearchEditor( 38 | Service::on($this->getDb()), 39 | $this->preserveParams 40 | ); 41 | 42 | $this->getDocument()->add($editor); 43 | $this->setTitle($this->translate('Adjust Filter')); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /run.php: -------------------------------------------------------------------------------- 1 | provideHook('cube/Actions', 'Cube/MonitoringActions'); 8 | $this->provideHook('cube/IcingaDbActions', 'Cube/IcingaDbActions'); 9 | 10 | $this->provideHook('icingadb/icingadbSupport'); 11 | 12 | if (! Cube::isUsingIcingaDb()) { 13 | $this->addRoute('cube/hosts', new Zend_Controller_Router_Route_Static( 14 | 'cube/hosts', 15 | [ 16 | 'controller' => 'ido-hosts', 17 | 'action' => 'index', 18 | 'module' => 'cube' 19 | ] 20 | )); 21 | 22 | $this->addRoute('cube/hosts/details', new Zend_Controller_Router_Route_Static( 23 | 'cube/hosts/details', 24 | [ 25 | 'controller' => 'ido-hosts', 26 | 'action' => 'details', 27 | 'module' => 'cube' 28 | ] 29 | )); 30 | 31 | $this->addRoute('cube/services', new Zend_Controller_Router_Route_Static( 32 | 'cube/services', 33 | [ 34 | 'controller' => 'ido-services', 35 | 'action' => 'index', 36 | 'module' => 'cube' 37 | ] 38 | )); 39 | 40 | $this->addRoute('cube/services/details', new Zend_Controller_Router_Route_Static( 41 | 'cube/services/details', 42 | [ 43 | 'controller' => 'ido-services', 44 | 'action' => 'details', 45 | 'module' => 'cube' 46 | ] 47 | )); 48 | } 49 | -------------------------------------------------------------------------------- /library/Cube/ProvidedHook/Cube/IcingaDbActions.php: -------------------------------------------------------------------------------- 1 | hasBaseFilter()) { 22 | $filter->add($cube->getBaseFilter()); 23 | } 24 | 25 | foreach ($cube->getSlices() as $dimension => $slice) { 26 | $filter->add(Filter::equal($dimension, $slice)); 27 | } 28 | 29 | $url = Url::fromPath('icingadb/' . $type . 's'); 30 | $url->setFilter($filter); 31 | 32 | if ($type === 'host') { 33 | $this->addActionLink( 34 | $url, 35 | t('Show hosts status'), 36 | t('This shows all matching hosts and their current state in Icinga DB Web'), 37 | 'server' 38 | ); 39 | } else { 40 | $this->addActionLink( 41 | $url, 42 | t('Show services status'), 43 | t('This shows all matching hosts and their current state in Icinga DB Web'), 44 | 'cog' 45 | ); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /library/Cube/Dimension.php: -------------------------------------------------------------------------------- 1 | 'hostgroup', 17 | 'servicegroups' => 'servicegroup', 18 | 'services' => 'servicestatus' 19 | ); 20 | 21 | protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter) 22 | { 23 | if ($name === 'servicestatus') { 24 | return ['s.host_object_id', 'ho.object_id']; 25 | } 26 | 27 | return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter); 28 | } 29 | 30 | protected function createSubQueryFilter(FilterExpression $filter, $queryName) 31 | { 32 | try { 33 | return parent::createSubQueryFilter($filter, $queryName); 34 | } catch (Exception $e) { 35 | if (version_compare(Version::VERSION, '2.10.0', '>=')) { 36 | throw $e; 37 | } 38 | 39 | if ($e->getMessage() === 'Undefined array key 0' && basename($e->getFile()) === 'IdoQuery.php') { 40 | // Ensures compatibility with earlier Icinga Web 2 versions 41 | throw new NotImplementedError(''); 42 | } else { 43 | throw $e; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /library/Cube/ProvidedHook/Cube/MonitoringActions.php: -------------------------------------------------------------------------------- 1 | getSlices() as $key => $val) { 26 | $vars['_host_' . $key] = $val; 27 | } 28 | 29 | $url = 'monitoring/list/hosts'; 30 | 31 | $this->addActionLink( 32 | $this->makeUrl($url, $vars), 33 | $view->translate('Show hosts status'), 34 | $view->translate('This shows all matching hosts and their current state in the monitoring module'), 35 | 'host' 36 | ); 37 | } elseif ($cube instanceof IdoServiceStatusCube) { 38 | $vars = []; 39 | foreach ($cube->getSlices() as $key => $val) { 40 | $vars['_service_' . $key] = $val; 41 | } 42 | 43 | $url = 'monitoring/list/services'; 44 | 45 | $this->addActionLink( 46 | $this->makeUrl($url, $vars), 47 | $view->translate('Show services status'), 48 | $view->translate('This shows all matching services and their current state in the monitoring module'), 49 | 'host' 50 | ); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /library/Cube/DimensionParams.php: -------------------------------------------------------------------------------- 1 | getParam('dimensions')); 25 | } 26 | 27 | public static function fromArray(array $dimensions = []) 28 | { 29 | $self = new static(); 30 | 31 | $self->dimensions = array_filter($dimensions); 32 | 33 | return $self; 34 | } 35 | 36 | public static function fromString($dimensions) 37 | { 38 | return static::fromArray(Str::trimSplit($dimensions)); 39 | } 40 | 41 | /** 42 | * @param $dimension 43 | * 44 | * @return $this 45 | */ 46 | public function add($dimension) 47 | { 48 | if (! empty($dimension)) { 49 | $this->dimensions[] = $dimension; 50 | } 51 | 52 | return $this; 53 | } 54 | 55 | /** 56 | * Overwrite dimensions 57 | * 58 | * @param $dimensions 59 | * 60 | * @return DimensionParams 61 | */ 62 | public static function update($dimensions) 63 | { 64 | $self = new static(); 65 | $self->dimensions = $dimensions; 66 | 67 | return $self; 68 | } 69 | 70 | /** 71 | * @return string encoded dimensions separated by coma 72 | */ 73 | public function getParams() 74 | { 75 | return implode(',', $this->dimensions); 76 | } 77 | 78 | /** 79 | * @return array 80 | */ 81 | public function getDimensions() 82 | { 83 | return $this->dimensions; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /library/Cube/Ido/ZfSelectWrapper.php: -------------------------------------------------------------------------------- 1 | select = $select; 18 | } 19 | 20 | /** 21 | * Get the underlying Zend_Db_Select query 22 | * 23 | * @return \Zend_Db_Select 24 | */ 25 | public function unwrap() 26 | { 27 | return $this->select; 28 | } 29 | 30 | /** 31 | * {@see \Zend_Db_Select::reset()} 32 | */ 33 | public function reset($part = null) 34 | { 35 | $this->select->reset($part); 36 | 37 | return $this; 38 | } 39 | 40 | /** 41 | * {@see \Zend_Db_Select::columns()} 42 | */ 43 | public function columns($cols = '*', $correlationName = null) 44 | { 45 | if (is_array($cols)) { 46 | foreach ($cols as $alias => &$col) { 47 | if (substr($col, 0, 2) === 'o.') { 48 | $col = 'ho.' . substr($col, 2); 49 | } 50 | } 51 | } 52 | 53 | return $this->select->columns($cols, $correlationName); 54 | } 55 | 56 | /** 57 | * Proxy Zend_Db_Select method calls 58 | * 59 | * @param string $name The name of the method to call 60 | * @param array $arguments Arguments for the method to call 61 | * 62 | * @return mixed 63 | * 64 | * @throws \BadMethodCallException If the called method does not exist 65 | */ 66 | public function __call($name, array $arguments) 67 | { 68 | if (! method_exists($this->select, $name)) { 69 | $class = get_class($this); 70 | $message = "Call to undefined method $class::$name"; 71 | 72 | throw new \BadMethodCallException($message); 73 | } 74 | 75 | return call_user_func_array([$this->select, $name], $arguments); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /doc/01-About.md: -------------------------------------------------------------------------------- 1 | # Icinga Cube 2 | 3 | The Icinga Cube is a tiny but useful [Icinga Web](https://github.com/Icinga/icingaweb2) 4 | module. It currently shows host and service statistics (total count, health) grouped by 5 | various custom variables in multiple dimensions. 6 | 7 | ![Cube - Overview](img/cube_simple.png) 8 | 9 | It will be your new best friend in case you are running a large environment and 10 | want to get a quick answers to questions like: 11 | 12 | * Which project uses how many servers per environment at which location/site? 13 | * Who occupies most servers? 14 | * How many of those are used in production? 15 | * Which project has only development and test boxes? 16 | * Which operating system is used for which project and in which environment? 17 | * Do we still have Debian Lenny? 18 | * Which projects are to blame for this? 19 | * Do we have applications where the operating systems used differ in staging 20 | and production? 21 | * Which project uses which operating system version for which application? 22 | * Which projects have homogeneous environments? 23 | * Which projects are at a consistent patch level? 24 | * How many RHEL 6 variants (6.1, 6.2, 6.3...) do we use? 25 | * Who is running the oldest ones? In production? 26 | * Which projects are still using physical servers in which environment? 27 | 28 | For Businessmen - Drill and Slice 29 | --------------------------------- 30 | 31 | Get answers to your questions. Quick and fully autonomous, using the cube 32 | requires no technical skills. Choose amongst all available dimensions and rotate 33 | the Cube to fit your needs. 34 | 35 | ![Cube - Configure Dimensions](img/cube_move-up.png) 36 | 37 | Want to drill down? Choose a slice and get your answers: 38 | 39 | ![Cube - Configure Dimensions](img/cube_slice.png) 40 | 41 | All facts configured for systems monitored by [Icinga](https://www.icinga.com/) 42 | can be used for your research. 43 | 44 | For Icinga Director users 45 | ------------------------- 46 | 47 | In case you are using the [Icinga Director](https://github.com/Icinga/icingaweb2-module-director), 48 | in addition to the multi-selection/edit feature the cube provides a nice way to 49 | modify multiple hosts at once. 50 | 51 | ![Cube - Director multi-edit](img/cube_director.png) 52 | 53 | Installation 54 | ------------ 55 | 56 | To install Icinga Cube see [Installation](02-Installation.md). 57 | -------------------------------------------------------------------------------- /library/Cube/IcingaDb/IcingaDbHostStatusCube.php: -------------------------------------------------------------------------------- 1 | 'hosts_total', 24 | 'hosts_down' => 'hosts_down_handled + f.hosts_down_unhandled', 25 | 'hosts_unhandled_down' => 'hosts_down_unhandled', 26 | ]; 27 | } 28 | 29 | public function createDimension($name) 30 | { 31 | $this->registerAvailableDimensions(); 32 | 33 | if (isset($this->availableDimensions[$name])) { 34 | return clone $this->availableDimensions[$name]; 35 | } 36 | 37 | return new CustomVariableDimension($name); 38 | } 39 | 40 | public function listAvailableDimensions() 41 | { 42 | return $this->fetchHostVariableDimensions(); 43 | } 44 | 45 | public function prepareInnerQuery() 46 | { 47 | $query = HoststateSummary::on($this->getDb()); 48 | $query->columns(array_diff_key($query->getModel()->getColumns(), (new Host())->getColumns())); 49 | $query->disableDefaultSort(); 50 | $this->applyRestrictions($query); 51 | 52 | $this->innerQuery = $query; 53 | return $this->innerQuery; 54 | } 55 | 56 | /** 57 | * Return Filter for Hosts cube. 58 | * 59 | * @return Filter\Any|Filter\Chain 60 | */ 61 | public function getObjectsFilter() 62 | { 63 | if ($this->objectsFilter === null) { 64 | $this->finalizeInnerQuery(); 65 | 66 | $hosts = $this->innerQuery()->columns(['host' => 'host.name']); 67 | $hosts->getSelectBase()->resetGroupBy(); 68 | 69 | $filter = Filter::any(); 70 | 71 | foreach ($hosts as $object) { 72 | $filter->add(Filter::equal('host.name', $object->host)); 73 | } 74 | 75 | $this->objectsFilter = $filter; 76 | } 77 | 78 | return $this->objectsFilter; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /library/Cube/Web/ActionLink.php: -------------------------------------------------------------------------------- 1 | url = $url; 41 | $this->title = $title; 42 | $this->description = $description; 43 | $this->icon = $icon; 44 | } 45 | 46 | /** 47 | * @return Url 48 | */ 49 | public function getUrl() 50 | { 51 | return $this->url; 52 | } 53 | 54 | /** 55 | * @return string 56 | */ 57 | public function getTitle() 58 | { 59 | return $this->title; 60 | } 61 | 62 | /** 63 | * @return string 64 | */ 65 | public function getDescription() 66 | { 67 | return $this->description; 68 | } 69 | 70 | /** 71 | * @return string 72 | */ 73 | public function getIcon() 74 | { 75 | return $this->icon; 76 | } 77 | 78 | /** 79 | * Render our icon 80 | * 81 | * @param View $view 82 | * @return string 83 | */ 84 | protected function renderIcon(View $view) 85 | { 86 | return $view->icon($this->getIcon()); 87 | } 88 | 89 | /** 90 | * @param View $view 91 | * @return string 92 | */ 93 | public function render(View $view) 94 | { 95 | return sprintf( 96 | '%s%s

%s

', 97 | $this->getUrl(), 98 | $this->renderIcon($view), 99 | $view->escape($this->getTitle()), 100 | $view->escape($this->getDescription()) 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Icinga Cube 2 | 3 | [![PHP Support](https://img.shields.io/badge/php-%3E%3D%207.2-777BB4?logo=PHP)](https://php.net/) 4 | ![Build Status](https://github.com/icinga/icingaweb2-module-cube/workflows/PHP%20Tests/badge.svg?branch=main) 5 | [![Github Tag](https://img.shields.io/github/tag/Icinga/icingaweb2-module-cube.svg)](https://github.com/Icinga/icingaweb2-module-cube) 6 | 7 | ![Icinga Logo](https://icinga.com/wp-content/uploads/2014/06/icinga_logo.png) 8 | 9 | The Icinga Cube is a tiny but useful [Icinga Web](https://github.com/Icinga/icingaweb2) 10 | module. It currently shows host and service statistics (total count, health) grouped by 11 | various custom variables in multiple dimensions. 12 | 13 | ![Cube - Overview](doc/img/cube_simple.png) 14 | 15 | It will be your new best friend in case you are running a large environment and 16 | want to get a quick answers to questions like: 17 | 18 | * Which project uses how many servers per environment at which location/site? 19 | * Who occupies most servers? 20 | * How many of those are used in production? 21 | * Which project has only development and test boxes? 22 | * Which operating system is used for which project and in which environment? 23 | * Do we still have Debian Lenny? 24 | * Which projects are to blame for this? 25 | * Do we have applications where the operating systems used differ in staging 26 | and production? 27 | * Which project uses which operating system version for which application? 28 | * Which projects have homogeneous environments? 29 | * Which projects are at a consistent patch level? 30 | * How many RHEL 6 variants (6.1, 6.2, 6.3...) do we use? 31 | * Who is running the oldest ones? In production? 32 | * Which projects are still using physical servers in which environment? 33 | 34 | For Businessmen - Drill and Slice 35 | --------------------------------- 36 | 37 | Get answers to your questions. Quick and fully autonomous, using the cube 38 | requires no technical skills. Choose amongst all available dimensions and rotate 39 | the Cube to fit your needs. 40 | 41 | ![Cube - Configure Dimensions](doc/img/cube_move-up.png) 42 | 43 | Want to drill down? Choose a slice and get your answers: 44 | 45 | ![Cube - Configure Dimensions](doc/img/cube_slice.png) 46 | 47 | All facts configured for systems monitored by [Icinga](https://www.icinga.com/) 48 | can be used for your research. 49 | 50 | For Icinga Director users 51 | ------------------------- 52 | 53 | In case you are using the [Icinga Director](https://github.com/Icinga/icingaweb2-module-director), 54 | in addition to the multi-selection/edit feature the cube provides a nice way to 55 | modify multiple hosts at once. 56 | 57 | ![Cube - Director multi-edit](doc/img/cube_director.png) 58 | 59 | Installation 60 | ------------ 61 | 62 | To install Icinga Cube see [Installation](https://icinga.com/docs/icinga-cube/latest/doc/02-Installation/). 63 | -------------------------------------------------------------------------------- /library/Cube/Hook/ActionsHook.php: -------------------------------------------------------------------------------- 1 | 33 | * $this->addActionLink( 34 | * $this->makeUrl('mymodule/controller/action', array('some' => 'param')), 35 | * 'A shown title', 36 | * 'A longer description text, should fit into the available square field', 37 | * 'icon-name' 38 | * ); 39 | * 40 | * 41 | * For a list of available icon names please enable the Icinga Web 2 'doc' 42 | * module and go to "Documentation" -> "Developer - Style" -> "Icons" 43 | * 44 | * @param Cube $cube 45 | * @param View $view 46 | * 47 | * @return void 48 | */ 49 | abstract public function prepareActionLinks(Cube $cube, View $view); 50 | 51 | /** 52 | * Lazy access to an ActionLinks object 53 | * 54 | * @return ActionLinks 55 | */ 56 | public function getActionLinks() 57 | { 58 | if ($this->actionLinks === null) { 59 | $this->actionLinks = new ActionLinks(); 60 | } 61 | return $this->actionLinks; 62 | } 63 | 64 | /** 65 | * Helper method instantiating an ActionLink object 66 | * 67 | * @param Url $url 68 | * @param string $title 69 | * @param string $description 70 | * @param string $icon 71 | * 72 | * @return $this 73 | */ 74 | public function addActionLink(Url $url, $title, $description, $icon) 75 | { 76 | $this->getActionLinks()->add( 77 | new ActionLink($url, $title, $description, $icon) 78 | ); 79 | 80 | return $this; 81 | } 82 | 83 | /** 84 | * Helper method instantiating an Url object 85 | * 86 | * @param string $path 87 | * @param array $params 88 | * @return Url 89 | */ 90 | public function makeUrl($path, $params = null) 91 | { 92 | $url = Url::fromPath($path); 93 | if ($params !== null) { 94 | $url->getParams()->mergeValues($params); 95 | } 96 | 97 | return $url; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /library/Cube/Web/ActionLinks.php: -------------------------------------------------------------------------------- 1 | prepareActionLinks($cube, $view); 43 | } catch (Exception $e) { 44 | $html[] = self::renderErrorItem($e, $view); 45 | } 46 | 47 | foreach ($hook->getActionLinks()->getLinks() as $link) { 48 | $html[] = '
  • ' . $link->render($view) . '
  • '; 49 | } 50 | } 51 | 52 | if (empty($html)) { 53 | $html[] = self::renderErrorItem( 54 | $view->translate('No action links have been provided for this cube'), 55 | $view 56 | ); 57 | } 58 | 59 | return implode("\n", $html) . "\n"; 60 | } 61 | 62 | /** 63 | * @param Exception|string $error 64 | * @param View $view 65 | * @return string 66 | */ 67 | private static function renderErrorItem($error, View $view) 68 | { 69 | if ($error instanceof Exception) { 70 | $error = $error->getMessage(); 71 | } 72 | return '
  • ' . $view->escape($error) . '
  • '; 73 | } 74 | 75 | /** 76 | * Add an ActionLink to this set of actions 77 | * 78 | * @param ActionLink $link 79 | * @return $this 80 | */ 81 | public function add(ActionLink $link) 82 | { 83 | $this->links[] = $link; 84 | return $this; 85 | } 86 | 87 | /** 88 | * @return ActionLink[] 89 | */ 90 | public function getLinks() 91 | { 92 | return $this->links; 93 | } 94 | 95 | /** 96 | * @param View $view 97 | * 98 | * @return string 99 | */ 100 | public function render(View $view) 101 | { 102 | $links = $this->getLinks(); 103 | if (empty($links)) { 104 | return ''; 105 | } 106 | 107 | $html = ''; 112 | 113 | return $html; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /library/Cube/IcingaDb/IcingaDbServiceStatusCube.php: -------------------------------------------------------------------------------- 1 | registerAvailableDimensions(); 23 | 24 | if (isset($this->availableDimensions[$name])) { 25 | return clone $this->availableDimensions[$name]; 26 | } 27 | 28 | return new CustomVariableDimension($name); 29 | } 30 | 31 | public function getAvailableFactColumns() 32 | { 33 | return [ 34 | 'services_cnt' => 'services_total', 35 | 'services_critical' => 'services_critical_handled + f.services_critical_unhandled', 36 | 'services_unhandled_critical' => 'services_critical_unhandled', 37 | 'services_warning' => 'services_warning_handled + f.services_warning_unhandled', 38 | 'services_unhandled_warning' => 'services_warning_unhandled', 39 | 'services_unknown' => 'services_unknown_handled + f.services_unknown_unhandled', 40 | 'services_unhandled_unknown' => 'services_unknown_unhandled', 41 | ]; 42 | } 43 | 44 | public function listAvailableDimensions() 45 | { 46 | return array_merge( 47 | $this->fetchServiceVariableDimensions(), 48 | $this->fetchHostVariableDimensions() 49 | ); 50 | } 51 | 52 | public function prepareInnerQuery() 53 | { 54 | $query = ServicestateSummary::on($this->getDb()); 55 | $query->columns(array_diff_key($query->getModel()->getColumns(), (new Service())->getColumns())); 56 | $query->disableDefaultSort(); 57 | $this->applyRestrictions($query); 58 | 59 | return $query; 60 | } 61 | 62 | /** 63 | * Return Filter for Services cube. 64 | * 65 | * @return Filter\Any|Filter\Chain 66 | */ 67 | public function getObjectsFilter() 68 | { 69 | if ($this->objectsFilter === null) { 70 | $this->finalizeInnerQuery(); 71 | 72 | $services = $this->innerQuery()->columns([ 73 | 'host_name' => 'host.name', 74 | 'service_name' => 'service.name' 75 | ]); 76 | 77 | $services->getSelectBase()->resetGroupBy(); 78 | $filter = Filter::any(); 79 | 80 | foreach ($services as $service) { 81 | $filter->add( 82 | Filter::all( 83 | Filter::equal('service.name', $service->service_name), 84 | Filter::equal('host.name', $service->host_name) 85 | ) 86 | ); 87 | } 88 | 89 | $this->objectsFilter = $filter; 90 | } 91 | 92 | return $this->objectsFilter; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /library/Cube/Ido/IdoHostStatusCube.php: -------------------------------------------------------------------------------- 1 | 'SUM(CASE WHEN hs.has_been_checked = 1 THEN 1 ELSE 0 END)', 24 | 'hosts_down' => 'SUM(CASE WHEN hs.has_been_checked = 1 AND hs.current_state = 1' 25 | . ' THEN 1 ELSE 0 END)', 26 | 'hosts_unhandled_down' => 'SUM(CASE WHEN hs.has_been_checked = 1 AND hs.current_state = 1' 27 | . ' AND hs.problem_has_been_acknowledged = 0 AND hs.scheduled_downtime_depth = 0' 28 | . ' THEN 1 ELSE 0 END)', 29 | 'hosts_unreachable' => 'SUM(CASE WHEN hs.current_state = 2 THEN 1 ELSE 0 END)', 30 | 'hosts_unhandled_unreachable' => 'SUM(CASE WHEN hs.current_state = 2' 31 | . ' AND hs.problem_has_been_acknowledged = 0 AND hs.scheduled_downtime_depth = 0' 32 | . ' THEN 1 ELSE 0 END)', 33 | ); 34 | } 35 | 36 | public function createDimension($name) 37 | { 38 | $this->registerAvailableDimensions(); 39 | 40 | if (isset($this->availableDimensions[$name])) { 41 | return clone $this->availableDimensions[$name]; 42 | } 43 | 44 | return new CustomVarDimension($name, CustomVarDimension::TYPE_HOST); 45 | } 46 | 47 | /** 48 | * This returns a list of all available Dimensions 49 | * 50 | * @return array 51 | */ 52 | public function listAvailableDimensions() 53 | { 54 | $this->requireBackend(); 55 | 56 | $view = $this->backend->select()->from('hoststatus'); 57 | 58 | $view->applyFilter($this->getMonitoringRestriction()); 59 | 60 | $select = $view->getQuery()->clearOrder()->getSelectQuery(); 61 | 62 | $select 63 | ->columns('cv.varname') 64 | ->join( 65 | ['cv' => $this->tableName('icinga_customvariablestatus')], 66 | 'cv.object_id = ho.object_id', 67 | [] 68 | ) 69 | ->group('cv.varname'); 70 | 71 | if (version_compare($this->getIdoVersion(), '1.12.0', '>=')) { 72 | $select->where('cv.is_json = 0'); 73 | } 74 | 75 | $select->order('cv.varname'); 76 | 77 | $dimensions = $this->filterProtectedCustomvars($this->db()->fetchCol($select)); 78 | $keys = array_map('strtolower', $dimensions); 79 | 80 | return array_combine($keys, $dimensions); 81 | } 82 | 83 | public function prepareInnerQuery() 84 | { 85 | $this->requireBackend(); 86 | 87 | $view = new Hoststatus($this->backend); 88 | 89 | $view->getQuery()->requireColumn('host_state'); 90 | 91 | $view->applyFilter($this->getMonitoringRestriction()); 92 | 93 | $select = $view->getQuery()->clearOrder()->getSelectQuery(); 94 | 95 | return $select; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /library/Cube/Ido/IdoServiceStatusCube.php: -------------------------------------------------------------------------------- 1 | 'SUM(CASE WHEN ss.has_been_checked = 1 THEN 1 ELSE 0 END)', 20 | 'services_critical' => 'SUM(CASE WHEN ss.has_been_checked = 1 AND ss.current_state = 2' 21 | . ' THEN 1 ELSE 0 END)', 22 | 'services_unhandled_critical' => 'SUM(CASE WHEN ss.has_been_checked = 1 AND ss.current_state = 2' 23 | . ' AND ss.problem_has_been_acknowledged = 0 AND ss.scheduled_downtime_depth = 0' 24 | . ' THEN 1 ELSE 0 END)', 25 | 'services_warning' => 'SUM(CASE WHEN ss.current_state = 1 THEN 1 ELSE 0 END)', 26 | 'services_unhandled_warning' => 'SUM(CASE WHEN ss.current_state = 1' 27 | . ' AND ss.problem_has_been_acknowledged = 0 AND ss.scheduled_downtime_depth = 0' 28 | . ' THEN 1 ELSE 0 END)', 29 | 'services_unknown' => 'SUM(CASE WHEN ss.current_state = 3 THEN 1 ELSE 0 END)', 30 | 'services_unhandled_unknown' => 'SUM(CASE WHEN ss.current_state = 3' 31 | . ' AND ss.problem_has_been_acknowledged = 0 AND ss.scheduled_downtime_depth = 0' 32 | . ' THEN 1 ELSE 0 END)', 33 | ]; 34 | } 35 | 36 | /** 37 | * This returns a list of all available Dimensions 38 | * 39 | * @return array 40 | */ 41 | public function listAvailableDimensions() 42 | { 43 | $this->requireBackend(); 44 | 45 | $view = $this->backend->select()->from('servicestatus'); 46 | 47 | $view->applyFilter($this->getMonitoringRestriction()); 48 | 49 | $select = $view->getQuery()->clearOrder()->getSelectQuery(); 50 | 51 | $select 52 | ->columns('cv.varname') 53 | ->join( 54 | ['cv' => $this->tableName('icinga_customvariablestatus')], 55 | 'cv.object_id = so.object_id', 56 | [] 57 | ) 58 | ->group('cv.varname'); 59 | 60 | if (version_compare($this->getIdoVersion(), '1.12.0', '>=')) { 61 | $select->where('cv.is_json = 0'); 62 | } 63 | 64 | $select->order('cv.varname'); 65 | 66 | $dimensions = $this->filterProtectedCustomvars($this->db()->fetchCol($select)); 67 | $keys = array_map('strtolower', $dimensions); 68 | 69 | return array_combine($keys, $dimensions); 70 | } 71 | 72 | public function prepareInnerQuery() 73 | { 74 | $this->requireBackend(); 75 | 76 | $view = $this->backend->select()->from('servicestatus'); 77 | 78 | $view->getQuery()->requireColumn('service_state'); 79 | 80 | $view->applyFilter($this->getMonitoringRestriction()); 81 | 82 | $select = $view->getQuery()->clearOrder()->getSelectQuery(); 83 | 84 | return $select; 85 | } 86 | 87 | public function createDimension($name) 88 | { 89 | $this->registerAvailableDimensions(); 90 | 91 | if (isset($this->availableDimensions[$name])) { 92 | return clone $this->availableDimensions[$name]; 93 | } 94 | 95 | return new CustomVarDimension($name, CustomVarDimension::TYPE_SERVICE); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /library/Cube/Hook/IcingaDbActionsHook.php: -------------------------------------------------------------------------------- 1 | actionLinks; 46 | } 47 | 48 | /** 49 | * Helper method to populate action links array 50 | * 51 | * @param Url $url 52 | * @param string $title 53 | * @param string $description 54 | * @param string $icon 55 | * 56 | * @return $this 57 | */ 58 | final protected function addActionLink(Url $url, string $title, string $description, string $icon): self 59 | { 60 | $linkContent = (new HtmlDocument()); 61 | $linkContent->addHtml(new Icon($icon)); 62 | $linkContent->addHtml(HtmlElement::create('span', ['class' => 'title'], $title)); 63 | $linkContent->addHtml(HtmlElement::create('p', null, $description)); 64 | 65 | $this->actionLinks[] = new Link($linkContent, $url); 66 | 67 | return $this; 68 | } 69 | 70 | /** 71 | * Helper method instantiating an Url object 72 | * 73 | * @param string $path 74 | * @param array $params 75 | * @return Url 76 | */ 77 | final protected function makeUrl(string $path, array $params = null): Url 78 | { 79 | $url = Url::fromPath($path); 80 | if ($params !== null) { 81 | $url->getParams()->mergeValues($params); 82 | } 83 | 84 | return $url; 85 | } 86 | 87 | /** 88 | * Render all links for all Hook implementations 89 | * 90 | * This is what the Cube calls when rendering details 91 | * 92 | * @param IcingaDbCube $cube 93 | * 94 | * @return string 95 | */ 96 | public static function renderAll(Cube $cube) 97 | { 98 | $html = new HtmlDocument(); 99 | 100 | /** @var IcingaDbActionsHook $hook */ 101 | foreach (Hook::all('Cube/IcingaDbActions') as $hook) { 102 | try { 103 | $hook->createActionLinks($cube); 104 | } catch (Exception $e) { 105 | $html->addHtml(HtmlElement::create('li', ['class' => 'error'], $e->getMessage())); 106 | } 107 | 108 | foreach ($hook->getActionLinks() as $link) { 109 | $html->addHtml(HtmlElement::create('li', null, $link)); 110 | } 111 | } 112 | 113 | if ($html->isEmpty()) { 114 | $html->addHtml( 115 | HtmlElement::create( 116 | 'li', 117 | ['class' => 'error'], 118 | t('No action links have been provided for this cube') 119 | ) 120 | ); 121 | } 122 | 123 | return $html->render(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /library/Cube/Ido/CustomVarDimension.php: -------------------------------------------------------------------------------- 1 | varName = $varName; 52 | $this->type = $type; 53 | } 54 | 55 | /** 56 | * Define whether null values should be shown 57 | * 58 | * @param bool $wantNull 59 | * @return $this 60 | */ 61 | public function wantNull($wantNull = true) 62 | { 63 | $this->wantNull = $wantNull; 64 | return $this; 65 | } 66 | 67 | /** 68 | * @return string 69 | */ 70 | public function getName() 71 | { 72 | return $this->varName; 73 | } 74 | 75 | /** 76 | * @return string 77 | */ 78 | public function getLabel() 79 | { 80 | return $this->varLabel ?: $this->getName(); 81 | } 82 | 83 | public function setLabel($label) 84 | { 85 | $this->varLabel = $label; 86 | 87 | return $this; 88 | } 89 | 90 | public function addLabel($label) 91 | { 92 | if ($this->varLabel === null) { 93 | $this->setLabel($label); 94 | } else { 95 | $this->varLabel .= ' & ' . $label; 96 | } 97 | 98 | return $this; 99 | } 100 | 101 | public function getColumnExpression(Cube $cube) 102 | { 103 | /** @var IdoCube $cube */ 104 | if ($this->wantNull) { 105 | return 'COALESCE(' . $cube->db()->quoteIdentifier(['c_' . $this->varName, 'varvalue']) . ", '-')"; 106 | } else { 107 | return $cube->db()->quoteIdentifier(['c_' . $this->varName, 'varvalue']); 108 | } 109 | } 110 | 111 | protected function safeVarname($name) 112 | { 113 | return $name; 114 | } 115 | 116 | public function addToCube(Cube $cube) 117 | { 118 | switch ($this->type) { 119 | case self::TYPE_HOST: 120 | $objectId = 'ho.object_id'; 121 | break; 122 | case self::TYPE_SERVICE: 123 | $objectId = 'so.object_id'; 124 | break; 125 | default: 126 | $objectId = 'o.object_id'; 127 | } 128 | $name = $this->safeVarname($this->varName); 129 | /** @var IdoCube $cube */ 130 | $alias = $cube->db()->quoteIdentifier(['c_' . $name]); 131 | 132 | if ($cube->isPgsql()) { 133 | $on = "LOWER($alias.varname) = ?"; 134 | $name = strtolower($name); 135 | } else { 136 | $on = $alias . '.varname = ? COLLATE latin1_general_ci'; 137 | } 138 | 139 | $cube->innerQuery()->joinLeft( 140 | array($alias => $cube->tableName('icinga_customvariablestatus')), 141 | $cube->db()->quoteInto($on, $name) 142 | . ' AND ' . $alias . '.object_id = ' . $objectId, 143 | array() 144 | )->group($alias . '.varvalue'); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /library/Cube/CubeRenderer/HostStatusCubeRenderer.php: -------------------------------------------------------------------------------- 1 | cube->getDimensionAfter($name)) && isset($this->summaries->{$next->getName()})) { 17 | $htm .= ' (' . $this->summaries->{$next->getName()}->hosts_cnt . ')'; 18 | } 19 | 20 | return $htm; 21 | } 22 | 23 | protected function getDimensionClasses($name, $row) 24 | { 25 | $classes = parent::getDimensionClasses($name, $row); 26 | $sums = $row; 27 | 28 | $next = $this->cube->getDimensionAfter($name); 29 | if ($next && isset($this->summaries->{$next->getName()})) { 30 | $sums = $this->summaries->{$next->getName()}; 31 | } 32 | 33 | $severityClass = []; 34 | if ($sums->hosts_unhandled_down > 0) { 35 | $severityClass[] = 'critical'; 36 | } elseif (isset($sums->hosts_unhandled_unreachable) && $sums->hosts_unhandled_unreachable > 0) { 37 | $severityClass[] = 'unreachable'; 38 | } 39 | 40 | if (empty($severityClass)) { 41 | if ($sums->hosts_down > 0) { 42 | $severityClass = ['critical', 'handled']; 43 | } elseif (isset($sums->hosts_unreachable) && $sums->hosts_unreachable > 0) { 44 | $severityClass = ['unreachable', 'handled']; 45 | } else { 46 | $severityClass[] = 'ok'; 47 | } 48 | } 49 | 50 | return array_merge($classes, $severityClass); 51 | } 52 | 53 | public function renderFacts($facts) 54 | { 55 | $indent = str_repeat(' ', 3); 56 | $parts = array(); 57 | 58 | if ($facts->hosts_unhandled_down > 0) { 59 | $parts['critical'] = $facts->hosts_unhandled_down; 60 | } 61 | 62 | if (isset($facts->hosts_unhandled_unreachable) && $facts->hosts_unhandled_unreachable > 0) { 63 | $parts['unreachable'] = $facts->hosts_unhandled_unreachable; 64 | } 65 | 66 | if ($facts->hosts_down > 0 && $facts->hosts_down > $facts->hosts_unhandled_down) { 67 | $parts['critical handled'] = $facts->hosts_down - $facts->hosts_unhandled_down; 68 | } 69 | 70 | if ( 71 | isset($facts->hosts_unreachable, $facts->hosts_unhandled_unreachable) 72 | && $facts->hosts_unreachable > 0 73 | && $facts->hosts_unreachable > 74 | $facts->hosts_unhandled_unreachable 75 | ) { 76 | $parts['unreachable handled'] = $facts->hosts_unreachable - $facts->hosts_unhandled_unreachable; 77 | } 78 | 79 | if ( 80 | $facts->hosts_cnt > $facts->hosts_down 81 | && (! isset($facts->hosts_unreachable) || $facts->hosts_cnt > $facts->hosts_unreachable) 82 | ) { 83 | $ok = $facts->hosts_cnt - $facts->hosts_down; 84 | if (isset($facts->hosts_unreachable)) { 85 | $ok -= $facts->hosts_unreachable; 86 | } 87 | 88 | $parts['ok'] = $ok; 89 | } 90 | 91 | $main = ''; 92 | $sub = ''; 93 | foreach ($parts as $class => $count) { 94 | if ($count === 0) { 95 | continue; 96 | } 97 | 98 | if ($main === '') { 99 | $main = $this->makeBadgeHtml($class, $count); 100 | } else { 101 | $sub .= $this->makeBadgeHtml($class, $count); 102 | } 103 | } 104 | if ($sub !== '') { 105 | $sub = $indent 106 | . '' 107 | . "\n " 108 | . $sub 109 | . $indent 110 | . "\n"; 111 | } 112 | 113 | return $main . $sub; 114 | } 115 | 116 | protected function makeBadgeHtml($class, $count) 117 | { 118 | $indent = str_repeat(' ', 3); 119 | return sprintf( 120 | '%s%s', 121 | $indent, 122 | $class, 123 | $count 124 | ) . "\n"; 125 | } 126 | 127 | protected function getDetailsBaseUrl() 128 | { 129 | return 'cube/hosts/details'; 130 | } 131 | 132 | protected function getSeveritySortColumns(): Generator 133 | { 134 | yield from ['hosts_unhandled_down', 'hosts_down']; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /library/Cube/IcingaDb/CustomVariableDimension.php: -------------------------------------------------------------------------------- 1 | sourceName = $matches[1]; 42 | $this->varName = $matches[2]; 43 | } 44 | 45 | $this->name = $name; 46 | } 47 | 48 | /** 49 | * Get the variable name without prefix 50 | * 51 | * @return string 52 | */ 53 | public function getVarName(): string 54 | { 55 | return $this->varName ?? $this->getName(); 56 | } 57 | 58 | /** 59 | * Get the variable source name 60 | * 61 | * @return ?string 62 | */ 63 | public function getSourceName(): ?string 64 | { 65 | return $this->sourceName; 66 | } 67 | 68 | public function getName() 69 | { 70 | return $this->name; 71 | } 72 | 73 | public function getLabel() 74 | { 75 | return $this->label ?: $this->getName(); 76 | } 77 | 78 | public function setLabel($label) 79 | { 80 | $this->label = $label; 81 | 82 | return $this; 83 | } 84 | 85 | public function addLabel($label) 86 | { 87 | if ($this->label === null) { 88 | $this->setLabel($label); 89 | } else { 90 | $this->label .= ' & ' . $label; 91 | } 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Define whether null values should be shown 98 | * 99 | * @param bool $wantNull 100 | * @return $this 101 | */ 102 | public function wantNull($wantNull = true) 103 | { 104 | $this->wantNull = $wantNull; 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * @param IcingaDbCube $cube 111 | * @return Expression|string 112 | */ 113 | public function getColumnExpression(Cube $cube) 114 | { 115 | $expression = $cube->getDb()->quoteIdentifier([$this->createCustomVarAlias(), 'flatvalue']); 116 | 117 | if ($this->wantNull) { 118 | return new Expression("COALESCE($expression, '-')"); 119 | } 120 | 121 | return $expression; 122 | } 123 | 124 | public function addToCube(Cube $cube) 125 | { 126 | /** @var IcingaDbCube $cube */ 127 | $innerQuery = $cube->innerQuery(); 128 | $sourceTable = $this->getSourceName() ?? $innerQuery->getModel()->getTableName(); 129 | 130 | $subQuery = $innerQuery->createSubQuery(new CustomvarFlat(), $sourceTable . '.vars'); 131 | $subQuery->getSelectBase()->resetWhere(); // The link to the outer query is the ON condition 132 | $subQuery->columns(['flatvalue', 'object_id' => $sourceTable . '.id']); 133 | $subQuery->filter(Filter::like('flatname', $this->getVarName())); 134 | 135 | // Values might not be unique (wildcard dimensions) 136 | $subQueryModelAlias = $subQuery->getResolver()->getAlias($subQuery->getModel()); 137 | $subQuery->getSelectBase()->groupBy([ 138 | $subQueryModelAlias . '.flatname', // Required by postgres, if there are any custom variable protections 139 | $subQueryModelAlias . '.flatvalue', 140 | 'object_id' 141 | ]); 142 | 143 | $this->applyRestrictions($subQuery); 144 | 145 | $subQueryAlias = $cube->getDb()->quoteIdentifier([$this->createCustomVarAlias()]); 146 | $innerQuery->getSelectBase()->groupBy($subQueryAlias . '.flatvalue'); 147 | 148 | $sourceIdPath = '.id'; 149 | if ($innerQuery->getModel() instanceof Service && $sourceTable === 'host') { 150 | $sourceIdPath = '.host_id'; 151 | } 152 | 153 | $innerQuery->getSelectBase()->join( 154 | [$subQueryAlias => $subQuery->assembleSelect()], 155 | [ 156 | $subQueryAlias . '.object_id = ' 157 | . $innerQuery->getResolver()->getAlias($innerQuery->getModel()) . $sourceIdPath 158 | ] 159 | ); 160 | } 161 | 162 | protected function createCustomVarAlias(): string 163 | { 164 | return implode('_', ['c', $this->getSourceName(), $this->getVarName()]); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php: -------------------------------------------------------------------------------- 1 | services_unhandled_critical > 0) { 18 | $parts['critical'] = $facts->services_unhandled_critical; 19 | } 20 | 21 | if ($facts->services_unhandled_unknown > 0) { 22 | $parts['unknown'] = $facts->services_unhandled_unknown; 23 | } 24 | 25 | if ($facts->services_unhandled_warning > 0) { 26 | $parts['warning'] = $facts->services_unhandled_warning; 27 | } 28 | 29 | if ($facts->services_critical > 0 && $facts->services_critical > $facts->services_unhandled_critical) { 30 | $parts['critical handled'] = $facts->services_critical - $facts->services_unhandled_critical; 31 | } 32 | 33 | if ($facts->services_unknown > 0 && $facts->services_unknown > $facts->services_unhandled_unknown) { 34 | $parts['unknown handled'] = $facts->services_unknown - $facts->services_unhandled_unknown; 35 | } 36 | 37 | if ($facts->services_warning > 0 && $facts->services_warning > $facts->services_unhandled_warning) { 38 | $parts['warning handled'] = $facts->services_warning - $facts->services_unhandled_warning; 39 | } 40 | 41 | if ( 42 | $facts->services_cnt > $facts->services_critical && $facts->services_cnt > $facts->services_warning 43 | && $facts->services_cnt > $facts->services_unknown 44 | ) { 45 | $parts['ok'] = $facts->services_cnt - $facts->services_critical - $facts->services_warning - 46 | $facts->services_unknown; 47 | } 48 | 49 | $main = ''; 50 | $sub = ''; 51 | foreach ($parts as $class => $count) { 52 | if ($count === 0) { 53 | continue; 54 | } 55 | 56 | if ($main === '') { 57 | $main = $this->makeBadgeHtml($class, $count); 58 | } else { 59 | $sub .= $this->makeBadgeHtml($class, $count); 60 | } 61 | } 62 | if ($sub !== '') { 63 | $sub = $indent 64 | . '' 65 | . "\n " 66 | . $sub 67 | . $indent 68 | . "\n"; 69 | } 70 | 71 | return $main . $sub; 72 | } 73 | 74 | /** 75 | * @inheritdoc 76 | */ 77 | protected function renderDimensionLabel($name, $row) 78 | { 79 | $htm = parent::renderDimensionLabel($name, $row); 80 | 81 | if (($next = $this->cube->getDimensionAfter($name)) && isset($this->summaries->{$next->getName()})) { 82 | $htm .= ' (' . $this->summaries->{$next->getName()}->services_cnt . ')'; 83 | } 84 | 85 | return $htm; 86 | } 87 | 88 | protected function getDimensionClasses($name, $row) 89 | { 90 | $classes = parent::getDimensionClasses($name, $row); 91 | $sums = $row; 92 | 93 | $next = $this->cube->getDimensionAfter($name); 94 | if ($next && isset($this->summaries->{$next->getName()})) { 95 | $sums = $this->summaries->{$next->getName()}; 96 | } 97 | 98 | if ($sums->services_unhandled_critical > 0) { 99 | $severityClass[] = 'critical'; 100 | } elseif ($sums->services_unhandled_unknown > 0) { 101 | $severityClass[] = 'unknown'; 102 | } elseif ($sums->services_unhandled_warning > 0) { 103 | $severityClass[] = 'warning'; 104 | } 105 | 106 | if (empty($severityClass)) { 107 | if ($sums->services_critical > 0) { 108 | $severityClass = ['critical', 'handled']; 109 | } elseif ($sums->services_unknown > 0) { 110 | $severityClass = ['unknown', 'handled']; 111 | } elseif ($sums->services_warning > 0) { 112 | $severityClass = ['warning', 'handled']; 113 | } else { 114 | $severityClass[] = 'ok'; 115 | } 116 | } 117 | 118 | return array_merge($classes, $severityClass); 119 | } 120 | 121 | protected function makeBadgeHtml($class, $count) 122 | { 123 | $indent = str_repeat(' ', 3); 124 | 125 | return sprintf( 126 | '%s%s', 127 | $indent, 128 | $class, 129 | $count 130 | ) . "\n"; 131 | } 132 | 133 | protected function getDetailsBaseUrl() 134 | { 135 | return 'cube/services/details'; 136 | } 137 | 138 | protected function getSeveritySortColumns(): Generator 139 | { 140 | $columns = ['critical', 'unknown', 'warning']; 141 | foreach ($columns as $column) { 142 | yield "services_unhandled_$column"; 143 | } 144 | 145 | foreach ($columns as $column) { 146 | yield "services_$column"; 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /library/Cube/Web/IdoController.php: -------------------------------------------------------------------------------- 1 | prepareCube(); 30 | 31 | $this->getTabs()->add('details', [ 32 | 'label' => $this->translate('Cube details'), 33 | 'url' => $this->getRequest()->getUrl() 34 | ])->activate('details'); 35 | 36 | $this->view->title = $cube->getSlicesLabel(); 37 | 38 | $this->view->links = ActionLinks::renderAll($cube, $this->view); 39 | 40 | $this->render('cube-details', null, true); 41 | } 42 | 43 | protected function renderCube(): void 44 | { 45 | $this->params->shift('format'); 46 | $showSettings = $this->params->shift('showSettings'); 47 | 48 | $cube = $this->prepareCube(); 49 | 50 | $this->view->title = sprintf( 51 | $this->translate('Cube: %s'), 52 | $cube->getPathLabel() 53 | ); 54 | 55 | if (count($cube->listDimensions()) > 0) { 56 | $this->view->cube = $cube; 57 | } else { 58 | $showSettings = true; 59 | } 60 | 61 | $this->view->url = Url::fromRequest(); 62 | if ($showSettings) { 63 | $form = (new DimensionsForm()) 64 | ->setUrl($this->view->url) 65 | ->setCube($cube) 66 | ->setUrl(Url::fromRequest()) 67 | ->on(DimensionsForm::ON_SUCCESS, function ($form) { 68 | $this->redirectNow($form->getRedirectUrl()); 69 | }) 70 | ->handleRequest($this->getServerRequest()); 71 | 72 | $this->view->form = $form; 73 | } else { 74 | $this->setAutorefreshInterval(15); 75 | } 76 | 77 | $this->render('cube-index', null, true); 78 | } 79 | 80 | private function prepareCube(): IdoCube 81 | { 82 | $cube = $this->getCube(); 83 | $cube->chooseFacts(array_keys($cube->getAvailableFactColumns())); 84 | 85 | $vars = DimensionParams::fromString($this->params->shift('dimensions', ''))->getDimensions(); 86 | 87 | $resolved = $this->params->shift('resolved', false); 88 | 89 | if ( 90 | ! $resolved 91 | && Module::exists('icingadb') 92 | && $this->hasIcingadbDimensionParams($vars) 93 | ) { 94 | $this->transformIcingadbDimensionParamsAndRedirect($vars); 95 | } elseif ($resolved) { 96 | $this->redirectNow(Url::fromRequest()->without('resolved')); 97 | } 98 | 99 | $wantNull = $this->params->shift('wantNull'); 100 | 101 | foreach ($vars as $var) { 102 | $cube->addDimensionByName($var); 103 | if ($wantNull) { 104 | $cube->getDimension($var)->wantNull(); 105 | } 106 | } 107 | 108 | foreach ($this->params->toArray() as $param) { 109 | $cube->slice(rawurldecode($param[0]), rawurldecode($param[1])); 110 | } 111 | 112 | return $cube; 113 | } 114 | 115 | /** 116 | * Get whether the dimensions contain icingadb dimension 117 | * 118 | * @param array $dimensions 119 | * 120 | * @return bool 121 | */ 122 | private function hasIcingadbDimensionParams(array $dimensions): bool 123 | { 124 | foreach ($dimensions as $dimension) { 125 | if ( 126 | Str::startsWith($dimension, CustomVariableDimension::HOST_PREFIX) 127 | || Str::startsWith($dimension, CustomVariableDimension::SERVICE_PREFIX) 128 | ) { 129 | return true; 130 | } 131 | } 132 | 133 | return false; 134 | } 135 | 136 | /** 137 | * Transform icingadb dimension and slice params and redirect 138 | * 139 | * This remove the new icingadb prefix from params and remove sort, problems-only, filter params 140 | * 141 | * @param array $icingadbDimensions 142 | */ 143 | private function transformIcingadbDimensionParamsAndRedirect(array $icingadbDimensions): void 144 | { 145 | $dimensions = []; 146 | $slices = []; 147 | $toRemoveSlices = []; 148 | 149 | $prefix = CustomVariableDimension::HOST_PREFIX; 150 | if ($this->getRequest()->getControllerName() === 'ido-services') { 151 | $prefix = CustomVariableDimension::SERVICE_PREFIX; 152 | } 153 | 154 | foreach ($icingadbDimensions as $param) { 155 | $newParam = $param; 156 | if (strpos($param, $prefix) !== false) { 157 | $newParam = substr($param, strlen($prefix)); 158 | } 159 | 160 | $slice = $this->params->shift(IcingaDbCube::SLICE_PREFIX . $param); 161 | if ($slice) { 162 | $slices[$newParam] = $slice; 163 | $toRemoveSlices[] = IcingaDbCube::SLICE_PREFIX . $param; 164 | } 165 | 166 | $dimensions[] = $newParam; 167 | } 168 | 169 | $icingadbParams = array_merge( 170 | $icingadbDimensions, 171 | $toRemoveSlices, 172 | array_keys($this->params->toArray(false)) 173 | ); 174 | 175 | $this->redirectNow( 176 | Url::fromRequest() 177 | ->setParam('dimensions', DimensionParams::fromArray($dimensions)->getParams()) 178 | ->addParams($slices) 179 | ->addParams(['resolved' => true]) 180 | ->without($icingadbParams) 181 | ); 182 | } 183 | 184 | public function createTabs(): Tabs 185 | { 186 | $params = Url::fromRequest()->getParams()->toString(); 187 | 188 | return $this->getTabs() 189 | ->add('cube/hosts', [ 190 | 'label' => $this->translate('Hosts'), 191 | 'url' => 'cube/hosts' . ($params === '' ? '' : '?' . $params) 192 | ]) 193 | ->add('cube/services', [ 194 | 'label' => $this->translate('Services'), 195 | 'url' => 'cube/services' . ($params === '' ? '' : '?' . $params) 196 | ]); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /library/Cube/Ido/IdoCube.php: -------------------------------------------------------------------------------- 1 | filterProtectedCustomvars([$name])) === 1) { 58 | $this->addDimension($this->createDimension($name)); 59 | } 60 | 61 | return $this; 62 | } 63 | 64 | /** 65 | * We can steal the DB connection directly from a Monitoring backend 66 | * 67 | * @param MonitoringBackend $backend 68 | * @return $this 69 | */ 70 | public function setBackend(MonitoringBackend $backend) 71 | { 72 | $this->backend = $backend; 73 | 74 | $resource = $backend->getResource(); 75 | $resource->getDbAdapter() 76 | ->getConnection() 77 | ->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL); 78 | 79 | $this->setConnection($resource); 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Provice access to our DB resource 86 | * 87 | * This lazy-loads the default monitoring backend in case no DB has been 88 | * given 89 | * 90 | * @return \Zend_Db_Adapter_Abstract 91 | */ 92 | public function db() 93 | { 94 | $this->requireBackend(); 95 | return parent::db(); 96 | } 97 | 98 | /** 99 | * Returns the Icinga IDO version 100 | * 101 | * @return string 102 | */ 103 | protected function getIdoVersion() 104 | { 105 | if ($this->idoVersion === null) { 106 | $db = $this->db(); 107 | $this->idoVersion = $db->fetchOne( 108 | $db->select()->from('icinga_dbversion', 'version') 109 | ); 110 | } 111 | 112 | return $this->idoVersion; 113 | } 114 | 115 | /** 116 | * Steal the default monitoring DB resource... 117 | * 118 | * ...in case none has been defined otherwise 119 | * 120 | * @return void 121 | */ 122 | protected function requireBackend() 123 | { 124 | if ($this->db === null) { 125 | $this->setBackend(MonitoringBackend::instance()); 126 | } 127 | } 128 | 129 | protected function getMonitoringRestriction() 130 | { 131 | $restriction = Filter::matchAny(); 132 | $restriction->setAllowedFilterColumns(array( 133 | 'host_name', 134 | 'hostgroup_name', 135 | 'instance_name', 136 | 'service_description', 137 | 'servicegroup_name', 138 | function ($c) { 139 | return preg_match('/^_(?:host|service)_/i', $c); 140 | } 141 | )); 142 | 143 | $filters = Auth::getInstance()->getUser()->getRestrictions('monitoring/filter/objects'); 144 | 145 | foreach ($filters as $filter) { 146 | if ($filter === '*') { 147 | return Filter::matchAny(); 148 | } 149 | try { 150 | $restriction->addFilter(Filter::fromQueryString($filter)); 151 | } catch (QueryException $e) { 152 | throw new ConfigurationError( 153 | 'Cannot apply restriction %s using the filter %s. You can only use the following columns: %s', 154 | 'monitoring/filter/objects', 155 | $filter, 156 | implode(', ', array( 157 | 'instance_name', 158 | 'host_name', 159 | 'hostgroup_name', 160 | 'service_description', 161 | 'servicegroup_name', 162 | '_(host|service)_' 163 | )), 164 | $e 165 | ); 166 | } 167 | } 168 | 169 | return $restriction; 170 | } 171 | 172 | /** 173 | * Return the given array without values matching the custom variables protected by the monitoring module 174 | * 175 | * @param string[] $customvars 176 | * 177 | * @return string[] 178 | */ 179 | protected function filterProtectedCustomvars(array $customvars) 180 | { 181 | if ($this->blacklistedProperties === null) { 182 | $this->blacklistedProperties = new GlobFilter( 183 | Auth::getInstance()->getRestrictions('monitoring/blacklist/properties') 184 | ); 185 | } 186 | 187 | if ($this instanceof IdoServiceStatusCube) { 188 | $type = 'service'; 189 | } else { 190 | $type = 'host'; 191 | } 192 | 193 | $customvars = $this->blacklistedProperties->removeMatching( 194 | [$type => ['vars' => array_flip($customvars)]] 195 | ); 196 | 197 | $customvars = isset($customvars[$type]['vars']) ? array_flip($customvars[$type]['vars']) : []; 198 | 199 | if ($this->protectedCustomvars === null) { 200 | $config = Config::module('monitoring')->get('security', 'protected_customvars'); 201 | $protectedCustomvars = array(); 202 | 203 | foreach (preg_split('~,~', $config, -1, PREG_SPLIT_NO_EMPTY) as $pattern) { 204 | $regex = array(); 205 | foreach (explode('*', $pattern) as $literal) { 206 | $regex[] = preg_quote($literal, '/'); 207 | } 208 | 209 | $protectedCustomvars[] = implode('.*', $regex); 210 | } 211 | 212 | $this->protectedCustomvars = empty($protectedCustomvars) 213 | ? '/^$/' 214 | : '/^(?:' . implode('|', $protectedCustomvars) . ')$/'; 215 | } 216 | 217 | return preg_grep($this->protectedCustomvars, $customvars, PREG_GREP_INVERT); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /application/forms/DimensionsForm.php: -------------------------------------------------------------------------------- 1 | 'icinga-controls', 25 | 'name' => 'dimensions-form' 26 | ]; 27 | 28 | /** 29 | * @var Cube 30 | */ 31 | private $cube; 32 | 33 | /** 34 | * @var Url 35 | */ 36 | private $url; 37 | 38 | /** 39 | * Get the url 40 | * 41 | * @return Url 42 | */ 43 | public function getUrl() 44 | { 45 | return $this->url; 46 | } 47 | 48 | /** 49 | * Set the url 50 | * 51 | * @param mixed $url 52 | */ 53 | public function setUrl($url): self 54 | { 55 | $this->url = $url; 56 | 57 | return $this; 58 | } 59 | 60 | public function setCube(Cube $cube) 61 | { 62 | $this->cube = $cube; 63 | return $this; 64 | } 65 | 66 | public function hasBeenSubmitted(): bool 67 | { 68 | // required to submit dimension controls and the selected dropdown option 69 | return $this->hasBeenSent() && 70 | ($this->getPressedSubmitElement() !== null || $this->getPopulatedValue('addDimension')); 71 | } 72 | 73 | public function assemble() 74 | { 75 | $dimensions = $this->cube->listDimensions(); 76 | $cnt = count($dimensions); 77 | 78 | if ($cnt < 3) { 79 | $allDimensions = $this->cube->listAdditionalDimensions(); 80 | 81 | $this->addElement('select', 'addDimension', [ 82 | 'options' => [null => $this->translate('+ Add a dimension')] + $allDimensions, 83 | 'class' => 'autosubmit' 84 | ]); 85 | } 86 | 87 | $pos = 0; 88 | foreach ($dimensions as $dimension) { 89 | $this->addDimensionButtons($dimension, $pos++, $cnt); 90 | } 91 | 92 | foreach ($this->cube->getSlices() as $key => $value) { 93 | $this->addSlice($this->cube->getDimension($key), $value); 94 | } 95 | 96 | $this->addElement($this->createUidElement()); 97 | } 98 | 99 | protected function addSlice(Dimension $dimension, $value) 100 | { 101 | $sliceId = sha1($this->cube::SLICE_PREFIX . $dimension->getName()); 102 | 103 | $sliceFieldset = Html::tag('fieldset', ['class' => 'dimensions']); 104 | 105 | $btn = $this->createElement('submitButton', 'removeSlice_' . $sliceId, [ 106 | 'label' => new Icon('trash'), 107 | 'class' => 'dimension-control' 108 | ]); 109 | 110 | $this->registerElement($btn); 111 | $sliceFieldset->addHtml($btn); 112 | 113 | $sliceFieldset->addHtml(Html::tag( 114 | 'span', 115 | ['class' => 'dimension-name'], 116 | sprintf('%s: %s = %s', $this->translate('Slice/Filter'), $dimension->getLabel(), $value) 117 | )); 118 | 119 | $this->addHtml($sliceFieldset); 120 | } 121 | 122 | protected function addDimensionButtons(Dimension $dimension, $pos, $total) 123 | { 124 | $dimensionId = sha1($dimension->getName()); 125 | 126 | $dimensionFieldset = Html::tag('fieldset', ['class' => 'dimensions']); 127 | 128 | $btn = $this->createElement('submitButton', 'removeDimension_' . $dimensionId, [ 129 | 'label' => new Icon('trash'), 130 | 'title' => sprintf($this->translate('Remove dimension "%s"'), $dimension->getLabel()), 131 | 'class' => 'dimension-control' 132 | ]); 133 | 134 | $this->registerElement($btn); 135 | $dimensionFieldset->addHtml($btn); 136 | 137 | if ($pos > 0) { 138 | $btn = $this->createElement('submitButton', 'moveDimensionUp_' . $dimensionId, [ 139 | 'label' => new Icon('angle-double-up'), 140 | 'title' => sprintf($this->translate('Move dimension "%s" up'), $dimension->getLabel()), 141 | 'class' => 'dimension-control', 142 | ]); 143 | 144 | $this->registerElement($btn); 145 | $dimensionFieldset->addHtml($btn); 146 | } 147 | 148 | if ($pos + 1 !== $total) { 149 | $btn = $this->createElement('submitButton', 'moveDimensionDown_' . $dimensionId, [ 150 | 'label' => new Icon('angle-double-down'), 151 | 'title' => sprintf($this->translate('Move dimension "%s" down'), $dimension->getLabel()), 152 | 'class' => 'dimension-control' 153 | ]); 154 | 155 | $this->registerElement($btn); 156 | $dimensionFieldset->addHtml($btn); 157 | } 158 | 159 | $dimensionFieldset->addHtml(Html::tag('span', ['class' => 'dimension-name'], $dimension->getLabel())); 160 | 161 | $this->addHtml($dimensionFieldset); 162 | } 163 | 164 | public function onSuccess() 165 | { 166 | $url = $this->getUrl(); 167 | 168 | if ($dimension = $this->getValue('addDimension')) { 169 | $url->setParam('dimensions', DimensionParams::fromUrl($url)->add($dimension)->getParams()); 170 | Notification::success($this->translate('New dimension has been added')); 171 | } else { 172 | $updateDimensions = false; 173 | $pressedButtonName = $this->getPressedSubmitElement()->getName(); 174 | 175 | foreach ($this->cube->listDimensions() as $name => $_) { 176 | $dimensionId = sha1($name); 177 | 178 | switch (true) { 179 | case ($pressedButtonName === 'removeDimension_' . $dimensionId): 180 | $this->cube->removeDimension($name); 181 | $updateDimensions = true; 182 | break 2; 183 | case ($pressedButtonName === 'moveDimensionUp_' . $dimensionId): 184 | $this->cube->moveDimensionUp($name); 185 | $updateDimensions = true; 186 | break 2; 187 | case ($pressedButtonName === 'moveDimensionDown_' . $dimensionId): 188 | $this->cube->moveDimensionDown($name); 189 | $updateDimensions = true; 190 | break 2; 191 | } 192 | } 193 | 194 | if ($updateDimensions) { 195 | $dimensions = array_merge(array_keys($this->cube->listDimensions()), $this->cube->listSlices()); 196 | $url->setParam('dimensions', DimensionParams::update($dimensions)->getParams()); 197 | } else { 198 | foreach ($this->cube->listSlices() as $slice) { 199 | $slice = $this->cube::SLICE_PREFIX . $slice; 200 | $sliceId = sha1($slice); 201 | 202 | if ($pressedButtonName === 'removeSlice_' . $sliceId) { 203 | $url->getParams()->remove(rawurlencode($slice)); 204 | } 205 | } 206 | } 207 | } 208 | 209 | $this->setRedirectUrl($url); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /public/css/module.less: -------------------------------------------------------------------------------- 1 | // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2 2 | 3 | .controls > a { 4 | color: @icinga-blue; 5 | 6 | &:hover::before { 7 | text-decoration: none; 8 | } 9 | } 10 | 11 | div.cube { 12 | div.cube-dimension0 > .header { 13 | font-size: 1.2em; 14 | font-weight: bold; 15 | border-bottom: 1px solid @text-color; 16 | } 17 | 18 | div.cube-dimension1 { 19 | clear: both; 20 | border-left: 0.5em solid transparent; 21 | } 22 | 23 | div.cube-dimension1 > .header, 24 | div.cube-dimension2 { 25 | display: inline-block; 26 | width: 10em; 27 | height: 10em; 28 | margin: 0; 29 | padding: 0.2em; 30 | margin-top: 0.3em; 31 | overflow: hidden; 32 | } 33 | 34 | div.cube-dimension1 > .header { 35 | display: flex; 36 | float: left; 37 | font-weight: bold; 38 | text-align: center; 39 | padding-left: 0; 40 | 41 | > a:first-child { 42 | flex: 1; 43 | max-width: 8em; 44 | word-wrap: break-word; 45 | } 46 | } 47 | 48 | div.cube-dimension2 { 49 | > .header { 50 | display: flex; 51 | text-align: center; 52 | color: @text-color-on-icinga-blue; 53 | margin-bottom: 1em; 54 | 55 | > a:first-child { 56 | flex: 1; 57 | max-width: 8em; 58 | overflow: hidden; 59 | text-overflow: ellipsis; 60 | white-space: nowrap; 61 | } 62 | } 63 | 64 | background: @color-ok; 65 | 66 | &.critical { 67 | background: @color-critical; 68 | } 69 | 70 | &.warning { 71 | background: @color-warning; 72 | } 73 | 74 | &.unknown { 75 | background: @color-unknown; 76 | } 77 | 78 | &.unreachable { 79 | background: @color-unreachable; 80 | } 81 | 82 | &.ok { 83 | background: @color-ok; 84 | } 85 | 86 | > .body { 87 | text-align: center; 88 | } 89 | } 90 | 91 | span.critical, span.warning, span.unknown, span.unreachable, span.ok { 92 | display: inline; 93 | border-radius: 0.1em; 94 | padding: 0.05em 0.2em; 95 | color: @text-color-on-icinga-blue; 96 | font-size: 3em; 97 | } 98 | 99 | span.ok { 100 | background: @color-ok; 101 | } 102 | 103 | .others { 104 | display: flex; 105 | justify-content: center; 106 | span { 107 | margin-left: 0.2em; 108 | margin-top: 0.8em; 109 | font-size: 1em; 110 | } 111 | } 112 | 113 | span.critical { 114 | background: @color-critical; 115 | } 116 | 117 | span.critical.handled { 118 | background: @color-critical-handled; 119 | } 120 | 121 | div.cube-dimension0.critical > .header { 122 | color: @color-critical; 123 | border-color: @color-critical; 124 | } 125 | 126 | div.cube-dimension1.critical { 127 | border-left-color: @color-critical; 128 | 129 | &.handled { 130 | border-left-color: @color-critical-handled; 131 | } 132 | } 133 | 134 | div.cube-dimension2.critical { 135 | span.sum { 136 | font-size: 1em; 137 | } 138 | } 139 | 140 | span.warning { 141 | background: @color-warning; 142 | } 143 | 144 | span.warning.handled { 145 | background: @color-warning-handled; 146 | } 147 | 148 | div.cube-dimension0.warning > .header { 149 | color: @color-warning; 150 | border-color: @color-warning; 151 | } 152 | 153 | div.cube-dimension1.warning { 154 | border-left-color: @color-warning; 155 | 156 | &.handled { 157 | border-left-color: @color-warning-handled; 158 | } 159 | } 160 | 161 | div.cube-dimension2.warning { 162 | span.sum { 163 | font-size: 1em; 164 | } 165 | } 166 | 167 | span.unknown{ 168 | background: @color-unknown; 169 | } 170 | 171 | span.unknown.handled { 172 | background: @color-unknown-handled; 173 | } 174 | 175 | div.cube-dimension0.unknown > .header { 176 | color: @color-unknown; 177 | border-color: @color-unknown; 178 | } 179 | 180 | div.cube-dimension1.unknown { 181 | border-left-color: @color-unknown; 182 | 183 | &.handled { 184 | border-left-color: @color-unknown-handled; 185 | } 186 | } 187 | 188 | div.cube-dimension2.unknown { 189 | span.sum { 190 | font-size: 1em; 191 | } 192 | } 193 | } 194 | 195 | span.unreachable{ 196 | background: @color-unreachable; 197 | } 198 | 199 | span.unreachable.handled { 200 | background: @color-unreachable-handled; 201 | } 202 | 203 | div.cube-dimension0.unreachable > .header { 204 | color: @color-unreachable; 205 | border-color: @color-unreachable; 206 | } 207 | 208 | div.cube-dimension1.unreachable { 209 | border-left-color: @color-unreachable; 210 | 211 | &.handled { 212 | border-left-color: @color-unreachable-handled; 213 | } 214 | } 215 | 216 | div.cube-dimension2.unreachable { 217 | span.sum { 218 | font-size: 1em; 219 | } 220 | } 221 | 222 | fieldset.dimensions { 223 | margin: 0; 224 | padding: 0; 225 | 226 | span { 227 | min-width: 18em; 228 | display: inline-block; 229 | } 230 | 231 | button[type=submit] { 232 | border: none; 233 | color: @text-color; 234 | background: none; 235 | 236 | padding-left: .75em; 237 | padding-right: .75em; 238 | 239 | i:before { 240 | margin-right: 0; 241 | } 242 | 243 | &:not([disabled]):hover, &:focus { 244 | .rounded-corners(); 245 | background-color: @icinga-blue; 246 | color: @text-color-on-icinga-blue; 247 | } 248 | 249 | &:focus { 250 | outline: 3px solid fade(@icinga-blue, 50%); 251 | outline-offset: 1px; 252 | } 253 | } 254 | 255 | button[type=submit][disabled] { 256 | color: @disabled-gray; 257 | background-color: @body-bg-color; 258 | cursor: auto; 259 | } 260 | 261 | &:first-of-type button[type=submit]:nth-of-type(2) { 262 | margin-left: 2.50em; 263 | } 264 | 265 | &:not(:first-of-type) { 266 | button[type=submit]:nth-of-type(2):last-of-type { 267 | margin-right: 2.50em; 268 | } 269 | } 270 | 271 | &:last-of-type { 272 | margin-bottom: 0.5em; 273 | } 274 | } 275 | 276 | .dimension-name { 277 | font-weight: bold; 278 | margin-left: 1em; 279 | } 280 | 281 | ul.action-links { 282 | list-style-type: none; 283 | margin: 0; 284 | padding: 0; 285 | } 286 | 287 | ul.action-links { 288 | li { 289 | margin: 0; 290 | padding: 0; 291 | float: left; 292 | display: inline-block; 293 | width: 18em; 294 | min-height: 10em; 295 | overflow: hidden; 296 | } 297 | 298 | li.error { 299 | background-color: @color-critical; 300 | color: @text-color-on-icinga-blue; 301 | padding: 1em; 302 | font-weight: bold; 303 | text-align: center; 304 | } 305 | 306 | a { 307 | width: 100%; 308 | height: 100%; 309 | padding: 0.5em 1em; 310 | display: block; 311 | text-decoration: none; 312 | color: @gray; 313 | 314 | .title { 315 | display: inline-block; 316 | margin-left: 5em; 317 | font-weight: bold; 318 | color: @link-color; 319 | } 320 | 321 | p { 322 | margin-left: 5em; 323 | } 324 | 325 | i { 326 | position: absolute; 327 | font-size: 3em; 328 | display: inline-block; 329 | } 330 | &:hover { 331 | background: @tr-hover-color; 332 | text-decoration: none; 333 | } 334 | } 335 | } 336 | 337 | /** Form **/ 338 | 339 | .content form { 340 | margin-bottom: 2em; 341 | 342 | button[type=submit] { 343 | margin-top: 0.5em; 344 | } 345 | 346 | select { 347 | width: 100%; 348 | } 349 | } 350 | 351 | .controls .icinga-form .toggle-switch { 352 | margin-top: .25em; 353 | margin-bottom: .25em; 354 | } 355 | -------------------------------------------------------------------------------- /library/Cube/Ido/DbCube.php: -------------------------------------------------------------------------------- 1 | connection = $connection; 57 | $this->db = $connection->getDbAdapter(); 58 | return $this; 59 | } 60 | 61 | /** 62 | * Prepare the query and fetch all data 63 | * 64 | * @return array 65 | */ 66 | public function fetchAll() 67 | { 68 | $query = $this->fullQuery(); 69 | return $this->db()->fetchAll($query); 70 | } 71 | 72 | /** 73 | * Choose a one or more facts 74 | * 75 | * This also initializes a fact column lookup array 76 | * 77 | * @param array $facts 78 | * @return $this 79 | */ 80 | public function chooseFacts(array $facts) 81 | { 82 | parent::chooseFacts($facts); 83 | 84 | $this->factColumns = array(); 85 | $columns = $this->getAvailableFactColumns(); 86 | foreach ($this->chosenFacts as $name) { 87 | $this->factColumns[$name] = $columns[$name]; 88 | } 89 | 90 | return $this; 91 | } 92 | 93 | /** 94 | * @param $name 95 | * @return $this 96 | */ 97 | public function setDbName($name) 98 | { 99 | $this->dbName = $name; 100 | return $this; 101 | } 102 | 103 | /** 104 | * Gives back the table name, eventually prefixed with a defined DB name 105 | * 106 | * @param string $name 107 | * @return string 108 | */ 109 | public function tableName($name) 110 | { 111 | if ($this->dbName === null) { 112 | return $name; 113 | } else { 114 | return $this->dbName . '.' . $name; 115 | } 116 | } 117 | 118 | /** 119 | * Returns an eventually defined DB name 120 | * 121 | * @return string|null 122 | */ 123 | public function getDbName() 124 | { 125 | return $this->dbName; 126 | } 127 | 128 | /** 129 | * Get our inner query 130 | * 131 | * Hint: mostly used to get rid of NULL values 132 | * 133 | * @return ZfSelectWrapper 134 | */ 135 | public function innerQuery() 136 | { 137 | if ($this->innerQuery === null) { 138 | $this->innerQuery = new ZfSelectWrapper($this->prepareInnerQuery()); 139 | } 140 | 141 | return $this->innerQuery; 142 | } 143 | 144 | /** 145 | * We first prepare the queries and to finalize it later on 146 | * 147 | * This way dimensions can be added one by one, they will be allowed to 148 | * optionally join additional tables or apply other modifications late 149 | * in the process 150 | * 151 | * @return void 152 | */ 153 | public function finalizeInnerQuery() 154 | { 155 | $query = $this->innerQuery()->unwrap(); 156 | $columns = array(); 157 | foreach ($this->dimensions as $name => $dimension) { 158 | $dimension->addToCube($this); 159 | if ($this->hasSlice($name)) { 160 | $query->where( 161 | $dimension->getColumnExpression($this) . ' = ?', 162 | $this->slices[$name] 163 | ); 164 | } else { 165 | $columns[$name] = $dimension->getColumnExpression($this); 166 | } 167 | } 168 | 169 | $c = []; 170 | 171 | foreach ($columns + $this->factColumns as $k => $v) { 172 | $c[$this->db()->quoteIdentifier([$k])] = $v; 173 | } 174 | 175 | $query->columns($c); 176 | } 177 | 178 | /** 179 | * Lazy-load our full query 180 | * 181 | * @return \Zend_Db_Select 182 | */ 183 | protected function fullQuery() 184 | { 185 | if ($this->fullQuery === null) { 186 | $this->fullQuery = $this->prepareFullQuery(); 187 | } 188 | 189 | return $this->fullQuery; 190 | } 191 | 192 | /** 193 | * Lazy-load our full query 194 | * 195 | * @return \Zend_Db_Select 196 | */ 197 | protected function rollupQuery() 198 | { 199 | if ($this->rollupQuery === null) { 200 | $this->rollupQuery = $this->prepareRollupQuery(); 201 | } 202 | 203 | return $this->rollupQuery; 204 | } 205 | 206 | /** 207 | * The full query wraps the rollup query in a sub-query to work around 208 | * MySQL limitations. This is required to not get into trouble when ordering, 209 | * especially combined with the need to keep control over (eventually desired) 210 | * NULL value fact columns 211 | * 212 | * @return \Zend_Db_Select 213 | */ 214 | protected function prepareFullQuery() 215 | { 216 | $alias = 'rollup'; 217 | $cols = $this->listColumns(); 218 | 219 | $columns = array(); 220 | 221 | foreach ($cols as $col) { 222 | $columns[$this->db()->quoteIdentifier([$col])] = $alias . '.' . $this->db()->quoteIdentifier([$col]); 223 | } 224 | 225 | $select = $this->db()->select()->from( 226 | array($alias => $this->rollupQuery()), 227 | $columns 228 | ); 229 | 230 | foreach ($columns as $col) { 231 | $select->order('(' . $col . ' IS NOT NULL)'); 232 | $select->order($col); 233 | } 234 | 235 | return $select; 236 | } 237 | 238 | /** 239 | * Provide access to our DB 240 | * 241 | * @return \Zend_Db_Adapter_Abstract 242 | */ 243 | public function db() 244 | { 245 | return $this->db; 246 | } 247 | 248 | /** 249 | * Whether our connection is PostgreSQL 250 | * 251 | * @return bool 252 | */ 253 | public function isPgsql() 254 | { 255 | return $this->connection->getDbType() === 'pgsql'; 256 | } 257 | 258 | 259 | /** 260 | * This prepares the rollup query 261 | * 262 | * Inner query is wrapped in a subquery, summaries for all facts are 263 | * fetched. Rollup considers all defined dimensions and expects them 264 | * to exist as columns in the innerQuery 265 | * 266 | * @return \Zend_Db_Select 267 | */ 268 | protected function prepareRollupQuery() 269 | { 270 | $alias = 'sub'; 271 | 272 | $dimensions = array_map(function ($val) { 273 | return $this->db()->quoteIdentifier([$val]); 274 | }, array_keys($this->listDimensions())); 275 | $this->finalizeInnerQuery(); 276 | $columns = array(); 277 | foreach ($dimensions as $dimension) { 278 | $columns[$dimension] = $alias . '.' . $dimension; 279 | } 280 | 281 | foreach ($this->listFacts() as $fact) { 282 | $columns[$fact] = 'SUM(' . $fact . ')'; 283 | } 284 | 285 | $select = $this->db()->select()->from( 286 | array($alias => $this->innerQuery()->unwrap()), 287 | $columns 288 | ); 289 | 290 | if ($this->isPgsql()) { 291 | $select->group('ROLLUP (' . implode(', ', $dimensions) . ')'); 292 | } else { 293 | $select->group('(' . implode('), (', $dimensions) . ') WITH ROLLUP'); 294 | } 295 | 296 | return $select; 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /library/Cube/Cube.php: -------------------------------------------------------------------------------- 1 | Available dimensions */ 21 | protected $availableDimensions; 22 | 23 | /** @var array Fact names */ 24 | protected $chosenFacts; 25 | 26 | /** @var Dimension[] */ 27 | protected $dimensions = array(); 28 | 29 | protected $slices = array(); 30 | 31 | protected $renderer; 32 | 33 | abstract public function fetchAll(); 34 | 35 | /** 36 | * Get whether the icingadb backend is in use 37 | * 38 | * @return bool 39 | */ 40 | public static function isUsingIcingaDb(): bool 41 | { 42 | return static::IS_USING_ICINGADB 43 | ?? (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()); 44 | } 45 | 46 | public function removeDimension($name) 47 | { 48 | unset($this->dimensions[$name]); 49 | unset($this->slices[$name]); 50 | return $this; 51 | } 52 | 53 | /** 54 | * @return CubeRenderer 55 | * @throws IcingaException 56 | */ 57 | public function getRenderer() 58 | { 59 | throw new IcingaException('Got no cube renderer'); 60 | } 61 | 62 | public function getPathLabel() 63 | { 64 | $dimensions = $this->getDimensionsLabel(); 65 | $slices = $this->getSlicesLabel(); 66 | $parts = array(); 67 | if ($dimensions !== null) { 68 | $parts[] = $dimensions; 69 | } 70 | 71 | if ($slices !== null) { 72 | $parts[] = $slices; 73 | } 74 | 75 | return implode(', ', $parts); 76 | } 77 | 78 | public function getDimensionsLabel() 79 | { 80 | $dimensions = $this->listDimensions(); 81 | if (empty($dimensions)) { 82 | return null; 83 | } 84 | 85 | return implode(' -> ', array_map(function ($d) { 86 | return $d->getLabel(); 87 | }, $dimensions)); 88 | } 89 | 90 | public function getSlicesLabel() 91 | { 92 | $parts = array(); 93 | 94 | $slices = $this->getSlices(); 95 | if (empty($slices)) { 96 | return null; 97 | } 98 | foreach ($slices as $key => $value) { 99 | $parts[] = sprintf('%s = %s', $this->getDimension($key)->getLabel(), $value); 100 | } 101 | 102 | return implode(', ', $parts); 103 | } 104 | 105 | /** 106 | * Create a new dimension 107 | * 108 | * @param string $name 109 | * @return Dimension 110 | */ 111 | abstract public function createDimension($name); 112 | 113 | protected function registerAvailableDimensions() 114 | { 115 | if ($this->availableDimensions !== null) { 116 | return; 117 | } 118 | 119 | $this->availableDimensions = []; 120 | foreach ($this->listAvailableDimensions() as $name => $label) { 121 | if (! isset($this->availableDimensions[$name])) { 122 | $this->availableDimensions[$name] = $this->createDimension($name)->setLabel($label); 123 | } else { 124 | $this->availableDimensions[$name]->addLabel($label); 125 | } 126 | } 127 | } 128 | 129 | public function listAdditionalDimensions() 130 | { 131 | $this->registerAvailableDimensions(); 132 | 133 | $list = []; 134 | foreach ($this->availableDimensions as $name => $dimension) { 135 | if (! $this->hasDimension($name)) { 136 | $list[$name] = $dimension->getLabel(); 137 | } 138 | } 139 | 140 | return $list; 141 | } 142 | 143 | abstract public function listAvailableDimensions(); 144 | 145 | public function getDimensionAfter($name) 146 | { 147 | $found = false; 148 | $after = null; 149 | 150 | foreach ($this->listDimensions() as $k => $d) { 151 | if ($found) { 152 | $after = $d; 153 | break; 154 | } 155 | 156 | if ($k === $name) { 157 | $found = true; 158 | } 159 | } 160 | 161 | return $after; 162 | } 163 | 164 | public function listDimensionsUpTo($name) 165 | { 166 | $res = array(); 167 | foreach ($this->listDimensions() as $d => $_) { 168 | $res[] = $d; 169 | if ($d === $name) { 170 | break; 171 | } 172 | } 173 | 174 | return $res; 175 | } 176 | 177 | public function moveDimensionUp($name) 178 | { 179 | $last = $found = null; 180 | $positions = array_keys($this->dimensions); 181 | 182 | foreach ($positions as $k => $v) { 183 | if ($v === $name) { 184 | $found = $k; 185 | break; 186 | } 187 | 188 | $last = $k; 189 | } 190 | 191 | if ($found !== null) { 192 | $this->flipPositions($positions, $last, $found); 193 | } 194 | 195 | $this->reOrderDimensions($positions); 196 | return $this; 197 | } 198 | 199 | public function moveDimensionDown($name) 200 | { 201 | $next = $found = null; 202 | $positions = array_keys($this->dimensions); 203 | 204 | foreach ($positions as $k => $v) { 205 | if ($found !== null) { 206 | $next = $k; 207 | break; 208 | } 209 | 210 | if ($v === $name) { 211 | $found = $k; 212 | } 213 | } 214 | 215 | if ($next !== null) { 216 | $this->flipPositions($positions, $next, $found); 217 | } 218 | 219 | $this->reOrderDimensions($positions); 220 | return $this; 221 | } 222 | 223 | protected function flipPositions(&$array, $pos1, $pos2) 224 | { 225 | list( 226 | $array[$pos1], 227 | $array[$pos2] 228 | ) = array( 229 | $array[$pos2], 230 | $array[$pos1] 231 | ); 232 | } 233 | 234 | protected function reOrderDimensions($positions) 235 | { 236 | $dimensions = array(); 237 | foreach ($positions as $pos => $key) { 238 | $dimensions[$key] = $this->dimensions[$key]; 239 | } 240 | 241 | $this->dimensions = $dimensions; 242 | } 243 | 244 | public function addDimension(Dimension $dimension) 245 | { 246 | $name = $dimension->getName(); 247 | if ($this->hasDimension($name)) { 248 | throw new IcingaException('Cannot add dimension "%s" twice', $name); 249 | } 250 | 251 | $this->dimensions[$name] = $dimension; 252 | return $this; 253 | } 254 | 255 | public function slice($key, $value) 256 | { 257 | if ($this->hasDimension($key)) { 258 | $this->slices[$key] = $value; 259 | } else { 260 | throw new IcingaException('Got no such dimension: "%s"', $key); 261 | } 262 | 263 | return $this; 264 | } 265 | 266 | public function hasDimension($name) 267 | { 268 | return array_key_exists($name, $this->dimensions); 269 | } 270 | 271 | public function hasSlice($name) 272 | { 273 | return array_key_exists($name, $this->slices); 274 | } 275 | 276 | public function listSlices() 277 | { 278 | return array_keys($this->slices); 279 | } 280 | 281 | public function getSlices() 282 | { 283 | return $this->slices; 284 | } 285 | 286 | public function hasFact($name) 287 | { 288 | return array_key_exists($name, $this->chosenFacts); 289 | } 290 | 291 | public function getDimension($name) 292 | { 293 | return $this->dimensions[$name]; 294 | } 295 | 296 | /** 297 | * Return a list of chosen facts 298 | * 299 | * @return array 300 | */ 301 | public function listFacts() 302 | { 303 | return $this->chosenFacts; 304 | } 305 | 306 | /** 307 | * Choose a list of facts 308 | * 309 | * @param array $facts 310 | * @return $this 311 | */ 312 | public function chooseFacts(array $facts) 313 | { 314 | $this->chosenFacts = $facts; 315 | return $this; 316 | } 317 | 318 | public function listDimensions() 319 | { 320 | return array_diff_key($this->dimensions, $this->slices); 321 | } 322 | 323 | public function listColumns() 324 | { 325 | return array_merge(array_keys($this->listDimensions()), $this->listFacts()); 326 | } 327 | 328 | /** 329 | * @param View $view 330 | * @param CubeRenderer $renderer 331 | * @return string 332 | */ 333 | public function render(View $view, CubeRenderer $renderer = null) 334 | { 335 | if ($renderer === null) { 336 | $renderer = $this->getRenderer(); 337 | } 338 | 339 | return $renderer->render($view); 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /library/Cube/IcingaDb/IcingaDbCube.php: -------------------------------------------------------------------------------- 1 | innerQuery === null) { 77 | $this->innerQuery = $this->prepareInnerQuery(); 78 | } 79 | 80 | return $this->innerQuery; 81 | } 82 | 83 | /** 84 | * Get our rollup query 85 | * 86 | * @return Select 87 | */ 88 | protected function rollupQuery() 89 | { 90 | if ($this->rollupQuery === null) { 91 | $this->rollupQuery = $this->prepareRollupQuery(); 92 | } 93 | 94 | return $this->rollupQuery; 95 | } 96 | 97 | /** 98 | * Add a specific named dimension 99 | * 100 | * @param string $name 101 | * @return $this 102 | */ 103 | public function addDimensionByName($name) 104 | { 105 | $this->addDimension($this->createDimension($name)); 106 | 107 | return $this; 108 | } 109 | 110 | /** 111 | * Set whether to show problems only 112 | * 113 | * @param bool $problemOnly 114 | * 115 | * @return $this 116 | */ 117 | public function problemsOnly(bool $problemOnly = true): self 118 | { 119 | $this->problemsOnly = $problemOnly; 120 | 121 | return $this; 122 | } 123 | 124 | 125 | /** 126 | * Get whether to show problems only 127 | * 128 | * @return bool 129 | */ 130 | public function isProblemsOnly(): bool 131 | { 132 | return $this->problemsOnly; 133 | } 134 | 135 | /** 136 | * Fetch the host variable dimensions 137 | * 138 | * @return array 139 | */ 140 | public function fetchHostVariableDimensions(): array 141 | { 142 | $query = Host::on($this->getDb()) 143 | ->with('customvar_flat') 144 | ->columns('customvar_flat.flatname') 145 | ->orderBy('customvar_flat.flatname'); 146 | 147 | $this->applyRestrictions($query); 148 | 149 | $query->getSelectBase()->groupBy('flatname'); 150 | 151 | $dimensions = []; 152 | foreach ($query as $row) { 153 | // Replaces array index notations with [*] to get results for arbitrary indexes 154 | $name = preg_replace('/\\[\d+](?=\\.|$)/', '[*]', $row->customvar_flat->flatname); 155 | $name = strtolower($name); 156 | $dimensions[CustomVariableDimension::HOST_PREFIX . $name] = 'Host ' . $name; 157 | } 158 | 159 | return $dimensions; 160 | } 161 | 162 | /** 163 | * Fetch the service variable dimensions 164 | * 165 | * @return array 166 | */ 167 | public function fetchServiceVariableDimensions(): array 168 | { 169 | $query = Service::on($this->getDb()) 170 | ->with('customvar_flat') 171 | ->columns('customvar_flat.flatname') 172 | ->orderBy('customvar_flat.flatname'); 173 | 174 | $this->applyRestrictions($query); 175 | 176 | $query->getSelectBase()->groupBy('flatname'); 177 | 178 | $dimensions = []; 179 | foreach ($query as $row) { 180 | // Replaces array index notations with [*] to get results for arbitrary indexes 181 | $name = preg_replace('/\\[\d+](?=\\.|$)/', '[*]', $row->customvar_flat->flatname); 182 | $name = strtolower($name); 183 | $dimensions[CustomVariableDimension::SERVICE_PREFIX . $name] = 'Service ' . $name; 184 | } 185 | 186 | return $dimensions; 187 | } 188 | 189 | /** 190 | * Set sort by columns 191 | * 192 | * @param ?string $sortBy 193 | * 194 | * @return $this 195 | */ 196 | public function sortBy(?string $sortBy): self 197 | { 198 | if (empty($sortBy)) { 199 | return $this; 200 | } 201 | 202 | $this->sortBy = SortUtil::createOrderBy($sortBy)[0]; 203 | 204 | return $this; 205 | } 206 | 207 | /** 208 | * Get sort by columns 209 | * 210 | * @return ?array Column as key and direction as value 211 | */ 212 | public function getSortBy(): ?array 213 | { 214 | return $this->sortBy; 215 | } 216 | 217 | /** 218 | * We first prepare the queries and to finalize it later on 219 | * 220 | * This way dimensions can be added one by one, they will be allowed to 221 | * optionally join additional tables or apply other modifications late 222 | * in the process 223 | * 224 | * @return void 225 | */ 226 | protected function finalizeInnerQuery() 227 | { 228 | $query = $this->innerQuery(); 229 | $select = $query->getSelectBase(); 230 | 231 | $columns = []; 232 | foreach ($this->dimensions as $name => $dimension) { 233 | $quotedDimension = $this->getDb()->quoteIdentifier([$name]); 234 | $dimension->addToCube($this); 235 | $columns[$quotedDimension] = $dimension->getColumnExpression($this); 236 | 237 | if ($this->hasSlice($name)) { 238 | $select->where( 239 | $dimension->getColumnExpression($this) . ' = ?', 240 | $this->slices[$name] 241 | ); 242 | } else { 243 | $columns[$quotedDimension] = $dimension->getColumnExpression($this); 244 | } 245 | } 246 | 247 | $select->columns($columns); 248 | 249 | $this->applyRestrictions($query); 250 | if ($this->hasBaseFilter()) { 251 | $query->filter($this->getBaseFilter()); 252 | } 253 | } 254 | 255 | protected function prepareRollupQuery() 256 | { 257 | $dimensions = $this->listDimensions(); 258 | $this->finalizeInnerQuery(); 259 | 260 | $columns = []; 261 | $groupBy = []; 262 | foreach ($dimensions as $name => $dimension) { 263 | $quotedDimension = $this->getDb()->quoteIdentifier([$name]); 264 | 265 | $columns[$quotedDimension] = 'f.' . $quotedDimension; 266 | $groupBy[] = $quotedDimension; 267 | } 268 | 269 | $availableFacts = $this->getAvailableFactColumns(); 270 | 271 | foreach ($this->chosenFacts as $alias) { 272 | $columns[$alias] = new Expression('SUM(f.' . $availableFacts[$alias] . ')'); 273 | } 274 | 275 | if (! empty($groupBy)) { 276 | if ($this->getDb()->getAdapter() instanceof Pgsql) { 277 | $groupBy = 'ROLLUP(' . implode(', ', $groupBy) . ')'; 278 | } else { 279 | $groupBy[count($groupBy) - 1] .= ' WITH ROLLUP'; 280 | } 281 | } 282 | 283 | $rollupQuery = new Select(); 284 | $rollupQuery->from(['f' => $this->innerQuery()->assembleSelect()]) 285 | ->columns($columns) 286 | ->groupBy($groupBy); 287 | 288 | return $rollupQuery; 289 | } 290 | 291 | protected function prepareFullQuery() 292 | { 293 | $rollupQuery = $this->rollupQuery(); 294 | $columns = []; 295 | $orderBy = []; 296 | $sortBy = $this->getSortBy(); 297 | foreach ($this->listColumns() as $column) { 298 | $quotedColumn = $this->getDb()->quoteIdentifier([$column]); 299 | $columns[$quotedColumn] = 'rollup.' . $quotedColumn; 300 | 301 | if ($this->hasDimension($column)) { 302 | $orderBy["($quotedColumn IS NOT NULL)"] = null; 303 | 304 | $sortDir = 'ASC'; 305 | if ($sortBy && self::DIMENSION_VALUE_SORT_PARAM === $sortBy[0]) { 306 | $sortDir = $sortBy[1] ?? 'ASC'; 307 | } 308 | 309 | $orderBy[$quotedColumn] = $sortDir; 310 | } 311 | } 312 | 313 | return (new Select()) 314 | ->from(['rollup' => $rollupQuery]) 315 | ->columns($columns) 316 | ->orderBy($orderBy); 317 | } 318 | 319 | /** 320 | * Lazy-load our full query 321 | * 322 | * @return Select 323 | */ 324 | protected function fullQuery() 325 | { 326 | if ($this->fullQuery === null) { 327 | $this->fullQuery = $this->prepareFullQuery(); 328 | } 329 | 330 | return $this->fullQuery; 331 | } 332 | 333 | public function fetchAll() 334 | { 335 | $query = $this->fullQuery(); 336 | return $this->getDb()->fetchAll($query); 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /library/Cube/Web/Controller.php: -------------------------------------------------------------------------------- 1 | filter === null) { 59 | $this->filter = QueryString::parse((string) $this->params); 60 | } 61 | 62 | return $this->filter; 63 | } 64 | 65 | public function detailsAction(): void 66 | { 67 | $cube = $this->prepareCube(); 68 | $this->getTabs()->add('details', [ 69 | 'label' => $this->translate('Cube details'), 70 | 'url' => $this->getRequest()->getUrl() 71 | ])->activate('details'); 72 | 73 | $cube->setBaseFilter($this->getFilter()); 74 | 75 | $this->setTitle($cube->getSlicesLabel()); 76 | $this->view->links = IcingaDbActionsHook::renderAll($cube); 77 | 78 | $this->addContent( 79 | HtmlString::create($this->view->render('/cube-details.phtml')) 80 | ); 81 | } 82 | 83 | protected function renderCube(): void 84 | { 85 | $cube = $this->prepareCube(); 86 | $this->setTitle(sprintf( 87 | $this->translate('Cube: %s'), 88 | $cube->getPathLabel() 89 | )); 90 | 91 | $this->params->shift('format'); 92 | $showSettings = $this->params->shift('showSettings'); 93 | 94 | $query = $cube->innerQuery(); 95 | $problemsOnly = (bool) $this->params->shift('problems', false); 96 | $problemToggle = (new ProblemToggle($problemsOnly ?: null)) 97 | ->setIdProtector([$this->getRequest(), 'protectId']) 98 | ->on(ProblemToggle::ON_SUCCESS, function (ProblemToggle $form) { 99 | /** @var CheckboxElement $problems */ 100 | $problems = $form->getElement('problems'); 101 | if (! $problems->isChecked()) { 102 | $this->redirectNow(Url::fromRequest()->remove('problems')); 103 | } else { 104 | $this->redirectNow(Url::fromRequest()->setParam('problems')); 105 | } 106 | })->handleRequest($this->getServerRequest()); 107 | 108 | $this->addControl($problemToggle); 109 | 110 | $sortControl = SortControl::create([ 111 | IcingaDbCube::DIMENSION_VALUE_SORT_PARAM => t('Value'), 112 | IcingaDbCube::DIMENSION_SEVERITY_SORT_PARAM . ' desc' => t('Severity'), 113 | ]); 114 | 115 | $this->params->shift($sortControl->getSortParam()); 116 | $sortControl->handleRequest($this->getServerRequest()); 117 | $cube->sortBy($sortControl->getSort()); 118 | $this->addControl($sortControl); 119 | 120 | $searchBar = $this->createSearchBar( 121 | $query, 122 | $this->preserveParams 123 | ); 124 | 125 | if ($searchBar->hasBeenSent() && ! $searchBar->isValid()) { 126 | if ($searchBar->hasBeenSubmitted()) { 127 | $filter = $this->getFilter(); 128 | } else { 129 | $this->addControl($searchBar); 130 | $this->sendMultipartUpdate(); 131 | return; 132 | } 133 | } else { 134 | $filter = $searchBar->getFilter(); 135 | } 136 | 137 | if ($problemsOnly) { 138 | $filter = Filter::all($filter, Filter::equal('state.is_problem', true)); 139 | } 140 | 141 | $cube->setBaseFilter($filter); 142 | $cube->problemsOnly($problemsOnly); 143 | 144 | $this->addControl($searchBar); 145 | 146 | if (count($cube->listDimensions()) > 0) { 147 | $this->view->cube = $cube; 148 | } else { 149 | $showSettings = true; 150 | } 151 | 152 | $this->view->url = Url::fromRequest() 153 | ->onlyWith($this->preserveParams) 154 | ->setFilter($searchBar->getFilter()); 155 | 156 | if ($showSettings) { 157 | $form = (new DimensionsForm()) 158 | ->setUrl($this->view->url) 159 | ->setCube($cube) 160 | ->on(DimensionsForm::ON_SUCCESS, function ($form) { 161 | $this->redirectNow($form->getRedirectUrl()); 162 | }) 163 | ->handleRequest($this->getServerRequest()); 164 | 165 | $this->view->form = $form; 166 | } else { 167 | $this->setAutorefreshInterval(15); 168 | } 169 | 170 | $this->addContent( 171 | HtmlString::create($this->view->render('/cube-index.phtml')) 172 | ); 173 | 174 | if (! $searchBar->hasBeenSubmitted() && $searchBar->hasBeenSent()) { 175 | $this->sendMultipartUpdate(); 176 | } 177 | } 178 | 179 | private function prepareCube(): IcingaDbCube 180 | { 181 | $cube = $this->getCube(); 182 | $cube->chooseFacts(array_keys($cube->getAvailableFactColumns())); 183 | 184 | $dimensions = DimensionParams::fromString( 185 | $this->params->shift('dimensions', '') 186 | )->getDimensions(); 187 | 188 | if ($this->hasLegacyDimensionParams($dimensions)) { 189 | $this->transformLegacyDimensionParamsAndRedirect($dimensions); 190 | } 191 | 192 | $wantNull = $this->params->shift('wantNull'); 193 | foreach ($dimensions as $dimension) { 194 | $cube->addDimensionByName($dimension); 195 | if ($wantNull) { 196 | $cube->getDimension($dimension)->wantNull(); 197 | } 198 | 199 | $sliceParamWithPrefix = rawurlencode($cube::SLICE_PREFIX . $dimension); 200 | 201 | if ($this->params->has($sliceParamWithPrefix)) { 202 | $this->preserveParams[] = $sliceParamWithPrefix; 203 | $cube->slice($dimension, $this->params->shift($sliceParamWithPrefix)); 204 | } 205 | } 206 | 207 | return $cube; 208 | } 209 | 210 | /** 211 | * Get whether the given dimension param is legacy dimension param 212 | * 213 | * @param string $dimensionParam 214 | * 215 | * @return bool 216 | */ 217 | private function isLegacyDimensionParam(string $dimensionParam): bool 218 | { 219 | return ! Str::startsWith($dimensionParam, CustomVariableDimension::HOST_PREFIX) 220 | && ! Str::startsWith($dimensionParam, CustomVariableDimension::SERVICE_PREFIX); 221 | } 222 | 223 | /** 224 | * Get whether the dimensions contain legacy dimension 225 | * 226 | * @param array $dimensions 227 | * 228 | * @return bool 229 | */ 230 | private function hasLegacyDimensionParams(array $dimensions): bool 231 | { 232 | foreach ($dimensions as $dimension) { 233 | if ($this->isLegacyDimensionParam($dimension)) { 234 | return true; 235 | } 236 | } 237 | 238 | return false; 239 | } 240 | 241 | /** 242 | * Transform legacy dimension and slice params and redirect 243 | * 244 | * This adds the new prefix to params and then redirects so that the new URL contains the prefixed params 245 | * Slices are prefixed to differ filter and slice params 246 | * 247 | * @param array $legacyDimensions 248 | */ 249 | private function transformLegacyDimensionParamsAndRedirect(array $legacyDimensions): void 250 | { 251 | $dimensions = []; 252 | $slices = []; 253 | 254 | $dimensionPrefix = CustomVariableDimension::HOST_PREFIX; 255 | if ($this->getRequest()->getControllerName() === 'services') { 256 | $dimensionPrefix = CustomVariableDimension::SERVICE_PREFIX; 257 | } 258 | 259 | foreach ($legacyDimensions as $param) { 260 | $newParam = $param; 261 | if ($this->isLegacyDimensionParam($param)) { 262 | $newParam = $dimensionPrefix . $param; 263 | } 264 | 265 | $slice = $this->params->shift($param); 266 | if ($slice) { 267 | $slices[IcingaDbCube::SLICE_PREFIX . $newParam] = $slice; 268 | } 269 | 270 | $dimensions[] = $newParam; 271 | } 272 | 273 | $this->redirectNow( 274 | Url::fromRequest() 275 | ->setParam('dimensions', DimensionParams::fromArray($dimensions)->getParams()) 276 | ->addParams($slices) 277 | ->without($legacyDimensions) 278 | ); 279 | } 280 | 281 | public function createTabs(): Tabs 282 | { 283 | $params = Url::fromRequest() 284 | ->onlyWith($this->preserveParams) 285 | ->getParams() 286 | ->toString(); 287 | 288 | return $this->getTabs() 289 | ->add('cube/hosts', [ 290 | 'label' => $this->translate('Hosts'), 291 | 'url' => 'cube/hosts' . ($params === '' ? '' : '?' . $params) 292 | ]) 293 | ->add('cube/services', [ 294 | 'label' => $this->translate('Services'), 295 | 'url' => 'cube/services' . ($params === '' ? '' : '?' . $params) 296 | ]); 297 | } 298 | } 299 | -------------------------------------------------------------------------------- /library/Cube/CubeRenderer.php: -------------------------------------------------------------------------------- 1 | cube = $cube; 70 | } 71 | 72 | /** 73 | * Render the given facts 74 | * 75 | * @param $facts 76 | * @return string 77 | */ 78 | abstract public function renderFacts($facts); 79 | 80 | /** 81 | * Returns the base url for the details action 82 | * 83 | * @return string 84 | */ 85 | abstract protected function getDetailsBaseUrl(); 86 | 87 | /** 88 | * Get the severity sort columns 89 | * 90 | * @return Generator 91 | */ 92 | abstract protected function getSeveritySortColumns(): Generator; 93 | 94 | /** 95 | * Initialize all we need 96 | */ 97 | protected function initialize() 98 | { 99 | $this->started = false; 100 | $this->initializeDimensions() 101 | ->initializeFacts() 102 | ->initializeLastRow() 103 | ->initializeSummaries(); 104 | } 105 | 106 | /** 107 | * @return $this 108 | */ 109 | protected function initializeLastRow() 110 | { 111 | $object = (object) array(); 112 | foreach ($this->dimensions as $dimension) { 113 | $object->{$dimension->getName()} = null; 114 | } 115 | 116 | $this->lastRow = $object; 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * @return $this 123 | */ 124 | protected function initializeDimensions() 125 | { 126 | $this->dimensions = $this->cube->listDimensions(); 127 | 128 | $min = 3; 129 | $cnt = count($this->dimensions); 130 | if ($cnt < $min) { 131 | $pos = 0; 132 | $diff = $min - $cnt; 133 | $this->dimensionOrder = []; 134 | foreach ($this->dimensions as $name => $_) { 135 | $this->dimensionOrder[$pos++ + $diff] = $name; 136 | } 137 | } else { 138 | $this->dimensionOrder = array_keys($this->dimensions); 139 | } 140 | 141 | $this->reversedDimensions = array_reverse($this->dimensionOrder); 142 | $this->dimensionLevels = array_flip($this->dimensionOrder); 143 | return $this; 144 | } 145 | 146 | /** 147 | * @return $this 148 | */ 149 | protected function initializeFacts() 150 | { 151 | $this->facts = $this->cube->listFacts(); 152 | return $this; 153 | } 154 | 155 | /** 156 | * @return $this 157 | */ 158 | protected function initializeSummaries() 159 | { 160 | $this->summaries = (object) array(); 161 | return $this; 162 | } 163 | 164 | /** 165 | * @param object $row 166 | * @return bool 167 | */ 168 | protected function startsDimension($row) 169 | { 170 | foreach ($this->dimensionOrder as $name) { 171 | if ($row->$name === null) { 172 | $this->summaries->$name = $this->extractFacts($row); 173 | return true; 174 | } 175 | } 176 | 177 | return false; 178 | } 179 | 180 | /** 181 | * @param $row 182 | * @return object 183 | */ 184 | protected function extractFacts($row) 185 | { 186 | $res = (object) array(); 187 | 188 | foreach ($this->facts as $fact) { 189 | $res->$fact = $row->$fact; 190 | } 191 | 192 | return $res; 193 | } 194 | 195 | public function render(View $view) 196 | { 197 | $this->view = $view; 198 | $this->initialize(); 199 | $htm = $this->beginContainer(); 200 | 201 | $results = $this->cube->fetchAll(); 202 | 203 | if (! empty($results) && $this->cube::isUsingIcingaDb()) { 204 | $sortBy = $this->cube->getSortBy(); 205 | if ($sortBy && $sortBy[0] === $this->cube::DIMENSION_SEVERITY_SORT_PARAM) { 206 | $isSortDirDesc = isset($sortBy[1]) && $sortBy[1] !== 'asc'; 207 | $results = $this->sortBySeverity($results, $isSortDirDesc); 208 | } 209 | } 210 | 211 | foreach ($results as $row) { 212 | $htm .= $this->renderRow($row); 213 | } 214 | 215 | return $htm . $this->closeDimensions() . $this->endContainer(); 216 | } 217 | 218 | 219 | /** 220 | * Sort the results by severity 221 | * 222 | * @param $results array The fetched results 223 | * @param $isSortDirDesc bool Whether the sort direction is descending 224 | * 225 | * @return Generator 226 | */ 227 | private function sortBySeverity(array $results, bool $isSortDirDesc): Generator 228 | { 229 | $perspective = end($this->dimensionOrder); 230 | $resultsCount = count($results); 231 | $tree = [new TreeNode()]; 232 | 233 | $prepareHeaders = function (array $tree, object $row): TreeNode { 234 | $node = (new TreeNode()) 235 | ->setValue($row); 236 | $parent = end($tree); 237 | $parent->appendChild($node); 238 | 239 | return $node; 240 | }; 241 | 242 | $i = 0; 243 | do { 244 | $row = $results[$i]; 245 | while ($row->$perspective === null) { 246 | $tree[] = $prepareHeaders($tree, $row); 247 | 248 | if (! isset($results[++$i])) { 249 | break; 250 | } 251 | 252 | $row = $results[$i]; 253 | } 254 | 255 | for (; $i < $resultsCount; $i++) { 256 | $row = $results[$i]; 257 | 258 | $anyNull = false; 259 | foreach ($this->dimensionOrder as $dimension) { 260 | if ($row->$dimension === null) { 261 | $anyNull = true; 262 | array_pop($tree); 263 | } 264 | } 265 | 266 | if ($anyNull) { 267 | break; 268 | } 269 | 270 | $prepareHeaders($tree, $row); 271 | } 272 | } while ($i < $resultsCount); 273 | 274 | $nodes = function (TreeNode $node) use (&$nodes, $isSortDirDesc): Generator { 275 | yield $node->getValue(); 276 | $children = $node->getChildren(); 277 | 278 | uasort($children, function (TreeNode $a, TreeNode $b) use ($isSortDirDesc): int { 279 | foreach ($this->getSeveritySortColumns() as $column) { 280 | $comparison = $a->getValue()->$column <=> $b->getValue()->$column; 281 | if ($comparison !== 0) { 282 | return $comparison * ($isSortDirDesc ? -1 : 1); 283 | } 284 | } 285 | 286 | // $a and $b are equal in terms of $priorities. 287 | return 0; 288 | }); 289 | 290 | foreach ($children as $node) { 291 | yield from $nodes($node); 292 | } 293 | }; 294 | 295 | return $nodes($tree[1]); 296 | } 297 | 298 | protected function renderRow($row) 299 | { 300 | $htm = ''; 301 | if ($dimension = $this->startsDimension($row)) { 302 | return $htm; 303 | } 304 | 305 | $htm .= $this->closeDimensionsForRow($row); 306 | $htm .= $this->beginDimensionsForRow($row); 307 | $htm .= $this->renderFacts($row); 308 | $this->lastRow = $row; 309 | return $htm; 310 | } 311 | 312 | protected function beginDimensionsForRow($row) 313 | { 314 | $last = $this->lastRow; 315 | foreach ($this->dimensionOrder as $name) { 316 | if ($last->$name !== $row->$name) { 317 | return $this->beginDimensionsUpFrom($name, $row); 318 | } 319 | } 320 | 321 | return ''; 322 | } 323 | 324 | protected function beginDimensionsUpFrom($dimension, $row) 325 | { 326 | $htm = ''; 327 | $found = false; 328 | 329 | foreach ($this->dimensionOrder as $name) { 330 | if ($name === $dimension) { 331 | $found = true; 332 | } 333 | 334 | if ($found) { 335 | $htm .= $this->beginDimension($name, $row); 336 | } 337 | } 338 | 339 | return $htm; 340 | } 341 | 342 | protected function closeDimensionsForRow($row) 343 | { 344 | $last = $this->lastRow; 345 | foreach ($this->dimensionOrder as $name) { 346 | if ($last->$name !== $row->$name) { 347 | return $this->closeDimensionsDownTo($name); 348 | } 349 | } 350 | 351 | return ''; 352 | } 353 | 354 | protected function closeDimensionsDownTo($name) 355 | { 356 | $htm = ''; 357 | 358 | foreach ($this->reversedDimensions as $dimension) { 359 | $htm .= $this->closeDimension($dimension); 360 | 361 | if ($name === $dimension) { 362 | break; 363 | } 364 | } 365 | 366 | return $htm; 367 | } 368 | 369 | protected function closeDimensions() 370 | { 371 | $htm = ''; 372 | foreach ($this->reversedDimensions as $name) { 373 | $htm .= $this->closeDimension($name); 374 | } 375 | 376 | return $htm; 377 | } 378 | 379 | protected function closeDimension($name) 380 | { 381 | if (! $this->started) { 382 | return ''; 383 | } 384 | 385 | $indent = $this->getIndent($name); 386 | return $indent . ' ' . "\n" . $indent . "\n"; 387 | } 388 | 389 | protected function getIndent($name) 390 | { 391 | return str_repeat(' ', $this->getLevel($name)); 392 | } 393 | 394 | protected function beginDimension($name, $row) 395 | { 396 | $indent = $this->getIndent($name); 397 | if (! $this->started) { 398 | $this->started = true; 399 | } 400 | $view = $this->view; 401 | $dimension = $this->cube->getDimension($name); 402 | 403 | return 404 | $indent . '
    ' . "\n" 407 | . $indent . ' ' . "\n" 415 | . $indent . '
    ' . "\n"; 416 | } 417 | 418 | /** 419 | * Render the label for a given dimension name 420 | * 421 | * To have some context available, also 422 | * 423 | * @param $name 424 | * @param $row 425 | * @return string 426 | */ 427 | protected function renderDimensionLabel($name, $row) 428 | { 429 | $caption = $row->$name; 430 | if (empty($caption)) { 431 | $caption = '_'; 432 | } 433 | 434 | return $this->view->escape($caption); 435 | } 436 | 437 | protected function getDetailsUrl($name, $row) 438 | { 439 | $url = Url::fromPath($this->getDetailsBaseUrl()); 440 | 441 | if ($this->cube instanceof IcingaDbCube && $this->cube->hasBaseFilter()) { 442 | /** @var Filter\Rule $baseFilter */ 443 | $baseFilter = $this->cube->getBaseFilter(); 444 | $url->setFilter($baseFilter); 445 | } 446 | 447 | $urlParams = $url->getParams(); 448 | 449 | $dimensions = array_merge(array_keys($this->cube->listDimensions()), $this->cube->listSlices()); 450 | $urlParams->add('dimensions', DimensionParams::update($dimensions)->getParams()); 451 | 452 | foreach ($this->cube->listDimensionsUpTo($name) as $dimensionName) { 453 | $urlParams->add($this->cube::SLICE_PREFIX . $dimensionName, $row->$dimensionName); 454 | } 455 | 456 | foreach ($this->cube->getSlices() as $key => $val) { 457 | $urlParams->add($this->cube::SLICE_PREFIX . $key, $val); 458 | } 459 | 460 | return $url; 461 | } 462 | 463 | protected function getSliceUrl($name, $row) 464 | { 465 | return $this->view->url() 466 | ->setParam($this->cube::SLICE_PREFIX . $name, $row->$name); 467 | } 468 | 469 | protected function isOuterDimension($name) 470 | { 471 | return $this->reversedDimensions[0] !== $name; 472 | } 473 | 474 | protected function getDimensionClassString($name, $row) 475 | { 476 | return implode(' ', $this->getDimensionClasses($name, $row)); 477 | } 478 | 479 | protected function getDimensionClasses($name, $row) 480 | { 481 | return array('cube-dimension' . $this->getLevel($name)); 482 | } 483 | 484 | protected function getLevel($name) 485 | { 486 | return $this->dimensionLevels[$name]; 487 | } 488 | 489 | /** 490 | * @return string 491 | */ 492 | protected function beginContainer() 493 | { 494 | return '
    ' . "\n"; 495 | } 496 | 497 | /** 498 | * @return string 499 | */ 500 | protected function endContainer() 501 | { 502 | return '
    ' . "\n"; 503 | } 504 | 505 | /** 506 | * Well... just to be on the safe side 507 | */ 508 | public function __destruct() 509 | { 510 | unset($this->cube); 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /phpstan-baseline-standard.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | ignoreErrors: 3 | - 4 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Controllers\\\\IndexController\\:\\:indexAction\\(\\) has no return type specified\\.$#" 5 | count: 1 6 | path: application/controllers/IndexController.php 7 | 8 | - 9 | message: "#^Cannot call method getName\\(\\) on ipl\\\\Html\\\\Contract\\\\FormSubmitElement\\|null\\.$#" 10 | count: 1 11 | path: application/forms/DimensionsForm.php 12 | 13 | - 14 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:addDimensionButtons\\(\\) has no return type specified\\.$#" 15 | count: 1 16 | path: application/forms/DimensionsForm.php 17 | 18 | - 19 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:addDimensionButtons\\(\\) has parameter \\$pos with no type specified\\.$#" 20 | count: 1 21 | path: application/forms/DimensionsForm.php 22 | 23 | - 24 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:addDimensionButtons\\(\\) has parameter \\$total with no type specified\\.$#" 25 | count: 1 26 | path: application/forms/DimensionsForm.php 27 | 28 | - 29 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:addSlice\\(\\) has no return type specified\\.$#" 30 | count: 1 31 | path: application/forms/DimensionsForm.php 32 | 33 | - 34 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:addSlice\\(\\) has parameter \\$value with no type specified\\.$#" 35 | count: 1 36 | path: application/forms/DimensionsForm.php 37 | 38 | - 39 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:assemble\\(\\) has no return type specified\\.$#" 40 | count: 1 41 | path: application/forms/DimensionsForm.php 42 | 43 | - 44 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:onSuccess\\(\\) has no return type specified\\.$#" 45 | count: 1 46 | path: application/forms/DimensionsForm.php 47 | 48 | - 49 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:setCube\\(\\) has no return type specified\\.$#" 50 | count: 1 51 | path: application/forms/DimensionsForm.php 52 | 53 | - 54 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Forms\\\\DimensionsForm\\:\\:\\$url \\(ipl\\\\Web\\\\Url\\) does not accept mixed\\.$#" 55 | count: 1 56 | path: application/forms/DimensionsForm.php 57 | 58 | - 59 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:addDimension\\(\\) has no return type specified\\.$#" 60 | count: 1 61 | path: library/Cube/Cube.php 62 | 63 | - 64 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:chooseFacts\\(\\) has parameter \\$facts with no value type specified in iterable type array\\.$#" 65 | count: 1 66 | path: library/Cube/Cube.php 67 | 68 | - 69 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:fetchAll\\(\\) has no return type specified\\.$#" 70 | count: 1 71 | path: library/Cube/Cube.php 72 | 73 | - 74 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:flipPositions\\(\\) has no return type specified\\.$#" 75 | count: 1 76 | path: library/Cube/Cube.php 77 | 78 | - 79 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:flipPositions\\(\\) has parameter \\$array with no type specified\\.$#" 80 | count: 1 81 | path: library/Cube/Cube.php 82 | 83 | - 84 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:flipPositions\\(\\) has parameter \\$pos1 with no type specified\\.$#" 85 | count: 1 86 | path: library/Cube/Cube.php 87 | 88 | - 89 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:flipPositions\\(\\) has parameter \\$pos2 with no type specified\\.$#" 90 | count: 1 91 | path: library/Cube/Cube.php 92 | 93 | - 94 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getDimension\\(\\) has no return type specified\\.$#" 95 | count: 1 96 | path: library/Cube/Cube.php 97 | 98 | - 99 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getDimension\\(\\) has parameter \\$name with no type specified\\.$#" 100 | count: 1 101 | path: library/Cube/Cube.php 102 | 103 | - 104 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getDimensionAfter\\(\\) has no return type specified\\.$#" 105 | count: 1 106 | path: library/Cube/Cube.php 107 | 108 | - 109 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getDimensionAfter\\(\\) has parameter \\$name with no type specified\\.$#" 110 | count: 1 111 | path: library/Cube/Cube.php 112 | 113 | - 114 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getDimensionsLabel\\(\\) has no return type specified\\.$#" 115 | count: 1 116 | path: library/Cube/Cube.php 117 | 118 | - 119 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getPathLabel\\(\\) has no return type specified\\.$#" 120 | count: 1 121 | path: library/Cube/Cube.php 122 | 123 | - 124 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getSlices\\(\\) has no return type specified\\.$#" 125 | count: 1 126 | path: library/Cube/Cube.php 127 | 128 | - 129 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getSlicesLabel\\(\\) has no return type specified\\.$#" 130 | count: 1 131 | path: library/Cube/Cube.php 132 | 133 | - 134 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:hasDimension\\(\\) has no return type specified\\.$#" 135 | count: 1 136 | path: library/Cube/Cube.php 137 | 138 | - 139 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:hasDimension\\(\\) has parameter \\$name with no type specified\\.$#" 140 | count: 1 141 | path: library/Cube/Cube.php 142 | 143 | - 144 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:hasFact\\(\\) has no return type specified\\.$#" 145 | count: 1 146 | path: library/Cube/Cube.php 147 | 148 | - 149 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:hasFact\\(\\) has parameter \\$name with no type specified\\.$#" 150 | count: 1 151 | path: library/Cube/Cube.php 152 | 153 | - 154 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:hasSlice\\(\\) has no return type specified\\.$#" 155 | count: 1 156 | path: library/Cube/Cube.php 157 | 158 | - 159 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:hasSlice\\(\\) has parameter \\$name with no type specified\\.$#" 160 | count: 1 161 | path: library/Cube/Cube.php 162 | 163 | - 164 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listAdditionalDimensions\\(\\) has no return type specified\\.$#" 165 | count: 1 166 | path: library/Cube/Cube.php 167 | 168 | - 169 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listAvailableDimensions\\(\\) has no return type specified\\.$#" 170 | count: 1 171 | path: library/Cube/Cube.php 172 | 173 | - 174 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listColumns\\(\\) has no return type specified\\.$#" 175 | count: 1 176 | path: library/Cube/Cube.php 177 | 178 | - 179 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listDimensions\\(\\) has no return type specified\\.$#" 180 | count: 1 181 | path: library/Cube/Cube.php 182 | 183 | - 184 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listDimensionsUpTo\\(\\) has no return type specified\\.$#" 185 | count: 1 186 | path: library/Cube/Cube.php 187 | 188 | - 189 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listDimensionsUpTo\\(\\) has parameter \\$name with no type specified\\.$#" 190 | count: 1 191 | path: library/Cube/Cube.php 192 | 193 | - 194 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listFacts\\(\\) return type has no value type specified in iterable type array\\.$#" 195 | count: 1 196 | path: library/Cube/Cube.php 197 | 198 | - 199 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:listSlices\\(\\) has no return type specified\\.$#" 200 | count: 1 201 | path: library/Cube/Cube.php 202 | 203 | - 204 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:moveDimensionDown\\(\\) has no return type specified\\.$#" 205 | count: 1 206 | path: library/Cube/Cube.php 207 | 208 | - 209 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:moveDimensionDown\\(\\) has parameter \\$name with no type specified\\.$#" 210 | count: 1 211 | path: library/Cube/Cube.php 212 | 213 | - 214 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:moveDimensionUp\\(\\) has no return type specified\\.$#" 215 | count: 1 216 | path: library/Cube/Cube.php 217 | 218 | - 219 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:moveDimensionUp\\(\\) has parameter \\$name with no type specified\\.$#" 220 | count: 1 221 | path: library/Cube/Cube.php 222 | 223 | - 224 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:reOrderDimensions\\(\\) has no return type specified\\.$#" 225 | count: 1 226 | path: library/Cube/Cube.php 227 | 228 | - 229 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:reOrderDimensions\\(\\) has parameter \\$positions with no type specified\\.$#" 230 | count: 1 231 | path: library/Cube/Cube.php 232 | 233 | - 234 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:registerAvailableDimensions\\(\\) has no return type specified\\.$#" 235 | count: 1 236 | path: library/Cube/Cube.php 237 | 238 | - 239 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:removeDimension\\(\\) has no return type specified\\.$#" 240 | count: 1 241 | path: library/Cube/Cube.php 242 | 243 | - 244 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:removeDimension\\(\\) has parameter \\$name with no type specified\\.$#" 245 | count: 1 246 | path: library/Cube/Cube.php 247 | 248 | - 249 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:slice\\(\\) has no return type specified\\.$#" 250 | count: 1 251 | path: library/Cube/Cube.php 252 | 253 | - 254 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:slice\\(\\) has parameter \\$key with no type specified\\.$#" 255 | count: 1 256 | path: library/Cube/Cube.php 257 | 258 | - 259 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:slice\\(\\) has parameter \\$value with no type specified\\.$#" 260 | count: 1 261 | path: library/Cube/Cube.php 262 | 263 | - 264 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:\\$availableDimensions \\(array\\\\) does not accept array\\\\.$#" 265 | count: 1 266 | path: library/Cube/Cube.php 267 | 268 | - 269 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:\\$chosenFacts type has no value type specified in iterable type array\\.$#" 270 | count: 1 271 | path: library/Cube/Cube.php 272 | 273 | - 274 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:\\$renderer has no type specified\\.$#" 275 | count: 1 276 | path: library/Cube/Cube.php 277 | 278 | - 279 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:\\$slices has no type specified\\.$#" 280 | count: 1 281 | path: library/Cube/Cube.php 282 | 283 | - 284 | message: "#^Access to undefined constant Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:DIMENSION_SEVERITY_SORT_PARAM\\.$#" 285 | count: 1 286 | path: library/Cube/CubeRenderer.php 287 | 288 | - 289 | message: "#^Call to an undefined method Icinga\\\\Module\\\\Cube\\\\Cube\\:\\:getSortBy\\(\\)\\.$#" 290 | count: 1 291 | path: library/Cube/CubeRenderer.php 292 | 293 | - 294 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimension\\(\\) has no return type specified\\.$#" 295 | count: 1 296 | path: library/Cube/CubeRenderer.php 297 | 298 | - 299 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimension\\(\\) has parameter \\$name with no type specified\\.$#" 300 | count: 1 301 | path: library/Cube/CubeRenderer.php 302 | 303 | - 304 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimension\\(\\) has parameter \\$row with no type specified\\.$#" 305 | count: 1 306 | path: library/Cube/CubeRenderer.php 307 | 308 | - 309 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimensionsForRow\\(\\) has no return type specified\\.$#" 310 | count: 1 311 | path: library/Cube/CubeRenderer.php 312 | 313 | - 314 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimensionsForRow\\(\\) has parameter \\$row with no type specified\\.$#" 315 | count: 1 316 | path: library/Cube/CubeRenderer.php 317 | 318 | - 319 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimensionsUpFrom\\(\\) has no return type specified\\.$#" 320 | count: 1 321 | path: library/Cube/CubeRenderer.php 322 | 323 | - 324 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimensionsUpFrom\\(\\) has parameter \\$dimension with no type specified\\.$#" 325 | count: 1 326 | path: library/Cube/CubeRenderer.php 327 | 328 | - 329 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:beginDimensionsUpFrom\\(\\) has parameter \\$row with no type specified\\.$#" 330 | count: 1 331 | path: library/Cube/CubeRenderer.php 332 | 333 | - 334 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:closeDimension\\(\\) has no return type specified\\.$#" 335 | count: 1 336 | path: library/Cube/CubeRenderer.php 337 | 338 | - 339 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:closeDimension\\(\\) has parameter \\$name with no type specified\\.$#" 340 | count: 1 341 | path: library/Cube/CubeRenderer.php 342 | 343 | - 344 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:closeDimensions\\(\\) has no return type specified\\.$#" 345 | count: 1 346 | path: library/Cube/CubeRenderer.php 347 | 348 | - 349 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:closeDimensionsDownTo\\(\\) has no return type specified\\.$#" 350 | count: 1 351 | path: library/Cube/CubeRenderer.php 352 | 353 | - 354 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:closeDimensionsDownTo\\(\\) has parameter \\$name with no type specified\\.$#" 355 | count: 1 356 | path: library/Cube/CubeRenderer.php 357 | 358 | - 359 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:closeDimensionsForRow\\(\\) has no return type specified\\.$#" 360 | count: 1 361 | path: library/Cube/CubeRenderer.php 362 | 363 | - 364 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:closeDimensionsForRow\\(\\) has parameter \\$row with no type specified\\.$#" 365 | count: 1 366 | path: library/Cube/CubeRenderer.php 367 | 368 | - 369 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:extractFacts\\(\\) has parameter \\$row with no type specified\\.$#" 370 | count: 1 371 | path: library/Cube/CubeRenderer.php 372 | 373 | - 374 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDetailsUrl\\(\\) has no return type specified\\.$#" 375 | count: 1 376 | path: library/Cube/CubeRenderer.php 377 | 378 | - 379 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDetailsUrl\\(\\) has parameter \\$name with no type specified\\.$#" 380 | count: 1 381 | path: library/Cube/CubeRenderer.php 382 | 383 | - 384 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDetailsUrl\\(\\) has parameter \\$row with no type specified\\.$#" 385 | count: 1 386 | path: library/Cube/CubeRenderer.php 387 | 388 | - 389 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDimensionClassString\\(\\) has no return type specified\\.$#" 390 | count: 1 391 | path: library/Cube/CubeRenderer.php 392 | 393 | - 394 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDimensionClassString\\(\\) has parameter \\$name with no type specified\\.$#" 395 | count: 1 396 | path: library/Cube/CubeRenderer.php 397 | 398 | - 399 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDimensionClassString\\(\\) has parameter \\$row with no type specified\\.$#" 400 | count: 1 401 | path: library/Cube/CubeRenderer.php 402 | 403 | - 404 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDimensionClasses\\(\\) has no return type specified\\.$#" 405 | count: 1 406 | path: library/Cube/CubeRenderer.php 407 | 408 | - 409 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDimensionClasses\\(\\) has parameter \\$name with no type specified\\.$#" 410 | count: 1 411 | path: library/Cube/CubeRenderer.php 412 | 413 | - 414 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getDimensionClasses\\(\\) has parameter \\$row with no type specified\\.$#" 415 | count: 1 416 | path: library/Cube/CubeRenderer.php 417 | 418 | - 419 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getIndent\\(\\) has no return type specified\\.$#" 420 | count: 1 421 | path: library/Cube/CubeRenderer.php 422 | 423 | - 424 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getIndent\\(\\) has parameter \\$name with no type specified\\.$#" 425 | count: 1 426 | path: library/Cube/CubeRenderer.php 427 | 428 | - 429 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getLevel\\(\\) has no return type specified\\.$#" 430 | count: 1 431 | path: library/Cube/CubeRenderer.php 432 | 433 | - 434 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getLevel\\(\\) has parameter \\$name with no type specified\\.$#" 435 | count: 1 436 | path: library/Cube/CubeRenderer.php 437 | 438 | - 439 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getSliceUrl\\(\\) has no return type specified\\.$#" 440 | count: 1 441 | path: library/Cube/CubeRenderer.php 442 | 443 | - 444 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getSliceUrl\\(\\) has parameter \\$name with no type specified\\.$#" 445 | count: 1 446 | path: library/Cube/CubeRenderer.php 447 | 448 | - 449 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:getSliceUrl\\(\\) has parameter \\$row with no type specified\\.$#" 450 | count: 1 451 | path: library/Cube/CubeRenderer.php 452 | 453 | - 454 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:initialize\\(\\) has no return type specified\\.$#" 455 | count: 1 456 | path: library/Cube/CubeRenderer.php 457 | 458 | - 459 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:isOuterDimension\\(\\) has no return type specified\\.$#" 460 | count: 1 461 | path: library/Cube/CubeRenderer.php 462 | 463 | - 464 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:isOuterDimension\\(\\) has parameter \\$name with no type specified\\.$#" 465 | count: 1 466 | path: library/Cube/CubeRenderer.php 467 | 468 | - 469 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:render\\(\\) has no return type specified\\.$#" 470 | count: 1 471 | path: library/Cube/CubeRenderer.php 472 | 473 | - 474 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:renderDimensionLabel\\(\\) has parameter \\$name with no type specified\\.$#" 475 | count: 1 476 | path: library/Cube/CubeRenderer.php 477 | 478 | - 479 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:renderDimensionLabel\\(\\) has parameter \\$row with no type specified\\.$#" 480 | count: 1 481 | path: library/Cube/CubeRenderer.php 482 | 483 | - 484 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:renderFacts\\(\\) has parameter \\$facts with no type specified\\.$#" 485 | count: 1 486 | path: library/Cube/CubeRenderer.php 487 | 488 | - 489 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:renderRow\\(\\) has no return type specified\\.$#" 490 | count: 1 491 | path: library/Cube/CubeRenderer.php 492 | 493 | - 494 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:renderRow\\(\\) has parameter \\$row with no type specified\\.$#" 495 | count: 1 496 | path: library/Cube/CubeRenderer.php 497 | 498 | - 499 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:sortBySeverity\\(\\) has parameter \\$results with no value type specified in iterable type array\\.$#" 500 | count: 1 501 | path: library/Cube/CubeRenderer.php 502 | 503 | - 504 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:\\$dimensionLevels type has no value type specified in iterable type array\\.$#" 505 | count: 1 506 | path: library/Cube/CubeRenderer.php 507 | 508 | - 509 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:\\$dimensionOrder type has no value type specified in iterable type array\\.$#" 510 | count: 1 511 | path: library/Cube/CubeRenderer.php 512 | 513 | - 514 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:\\$dimensions type has no value type specified in iterable type array\\.$#" 515 | count: 1 516 | path: library/Cube/CubeRenderer.php 517 | 518 | - 519 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:\\$facts has no type specified\\.$#" 520 | count: 1 521 | path: library/Cube/CubeRenderer.php 522 | 523 | - 524 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:\\$reversedDimensions type has no value type specified in iterable type array\\.$#" 525 | count: 1 526 | path: library/Cube/CubeRenderer.php 527 | 528 | - 529 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\:\\:\\$started has no type specified\\.$#" 530 | count: 1 531 | path: library/Cube/CubeRenderer.php 532 | 533 | - 534 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:getDimensionClasses\\(\\) has no return type specified\\.$#" 535 | count: 1 536 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 537 | 538 | - 539 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:getDimensionClasses\\(\\) has parameter \\$name with no type specified\\.$#" 540 | count: 1 541 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 542 | 543 | - 544 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:getDimensionClasses\\(\\) has parameter \\$row with no type specified\\.$#" 545 | count: 1 546 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 547 | 548 | - 549 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:makeBadgeHtml\\(\\) has no return type specified\\.$#" 550 | count: 1 551 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 552 | 553 | - 554 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:makeBadgeHtml\\(\\) has parameter \\$class with no type specified\\.$#" 555 | count: 1 556 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 557 | 558 | - 559 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:makeBadgeHtml\\(\\) has parameter \\$count with no type specified\\.$#" 560 | count: 1 561 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 562 | 563 | - 564 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:renderDimensionLabel\\(\\) has parameter \\$name with no type specified\\.$#" 565 | count: 1 566 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 567 | 568 | - 569 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:renderDimensionLabel\\(\\) has parameter \\$row with no type specified\\.$#" 570 | count: 1 571 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 572 | 573 | - 574 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\HostStatusCubeRenderer\\:\\:renderFacts\\(\\) has parameter \\$facts with no type specified\\.$#" 575 | count: 1 576 | path: library/Cube/CubeRenderer/HostStatusCubeRenderer.php 577 | 578 | - 579 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:getDimensionClasses\\(\\) has no return type specified\\.$#" 580 | count: 1 581 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 582 | 583 | - 584 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:getDimensionClasses\\(\\) has parameter \\$name with no type specified\\.$#" 585 | count: 1 586 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 587 | 588 | - 589 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:getDimensionClasses\\(\\) has parameter \\$row with no type specified\\.$#" 590 | count: 1 591 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 592 | 593 | - 594 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:makeBadgeHtml\\(\\) has no return type specified\\.$#" 595 | count: 1 596 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 597 | 598 | - 599 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:makeBadgeHtml\\(\\) has parameter \\$class with no type specified\\.$#" 600 | count: 1 601 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 602 | 603 | - 604 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:makeBadgeHtml\\(\\) has parameter \\$count with no type specified\\.$#" 605 | count: 1 606 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 607 | 608 | - 609 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:renderDimensionLabel\\(\\) has parameter \\$name with no type specified\\.$#" 610 | count: 1 611 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 612 | 613 | - 614 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:renderDimensionLabel\\(\\) has parameter \\$row with no type specified\\.$#" 615 | count: 1 616 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 617 | 618 | - 619 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\CubeRenderer\\\\ServiceStatusCubeRenderer\\:\\:renderFacts\\(\\) has parameter \\$facts with no type specified\\.$#" 620 | count: 1 621 | path: library/Cube/CubeRenderer/ServiceStatusCubeRenderer.php 622 | 623 | - 624 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:add\\(\\) has parameter \\$dimension with no type specified\\.$#" 625 | count: 1 626 | path: library/Cube/DimensionParams.php 627 | 628 | - 629 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:fromArray\\(\\) has no return type specified\\.$#" 630 | count: 1 631 | path: library/Cube/DimensionParams.php 632 | 633 | - 634 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:fromArray\\(\\) has parameter \\$dimensions with no value type specified in iterable type array\\.$#" 635 | count: 1 636 | path: library/Cube/DimensionParams.php 637 | 638 | - 639 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:fromString\\(\\) has no return type specified\\.$#" 640 | count: 1 641 | path: library/Cube/DimensionParams.php 642 | 643 | - 644 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:fromString\\(\\) has parameter \\$dimensions with no type specified\\.$#" 645 | count: 1 646 | path: library/Cube/DimensionParams.php 647 | 648 | - 649 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:fromUrl\\(\\) has no return type specified\\.$#" 650 | count: 1 651 | path: library/Cube/DimensionParams.php 652 | 653 | - 654 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:getDimensions\\(\\) return type has no value type specified in iterable type array\\.$#" 655 | count: 1 656 | path: library/Cube/DimensionParams.php 657 | 658 | - 659 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:update\\(\\) has parameter \\$dimensions with no type specified\\.$#" 660 | count: 1 661 | path: library/Cube/DimensionParams.php 662 | 663 | - 664 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\DimensionParams\\:\\:\\$dimensions type has no value type specified in iterable type array\\.$#" 665 | count: 1 666 | path: library/Cube/DimensionParams.php 667 | 668 | - 669 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Hook\\\\ActionsHook\\:\\:makeUrl\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" 670 | count: 1 671 | path: library/Cube/Hook/ActionsHook.php 672 | 673 | - 674 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Hook\\\\IcingaDbActionsHook\\:\\:makeUrl\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" 675 | count: 1 676 | path: library/Cube/Hook/IcingaDbActionsHook.php 677 | 678 | - 679 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\CustomVariableDimension\\:\\:__construct\\(\\) has parameter \\$name with no type specified\\.$#" 680 | count: 1 681 | path: library/Cube/IcingaDb/CustomVariableDimension.php 682 | 683 | - 684 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\CustomVariableDimension\\:\\:\\$label has no type specified\\.$#" 685 | count: 1 686 | path: library/Cube/IcingaDb/CustomVariableDimension.php 687 | 688 | - 689 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\CustomVariableDimension\\:\\:\\$wantNull has no type specified\\.$#" 690 | count: 1 691 | path: library/Cube/IcingaDb/CustomVariableDimension.php 692 | 693 | - 694 | message: "#^Cannot access property \\$customvar_flat on mixed\\.$#" 695 | count: 2 696 | path: library/Cube/IcingaDb/IcingaDbCube.php 697 | 698 | - 699 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:fetchAll\\(\\) has no return type specified\\.$#" 700 | count: 1 701 | path: library/Cube/IcingaDb/IcingaDbCube.php 702 | 703 | - 704 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:fetchHostVariableDimensions\\(\\) return type has no value type specified in iterable type array\\.$#" 705 | count: 1 706 | path: library/Cube/IcingaDb/IcingaDbCube.php 707 | 708 | - 709 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:fetchServiceVariableDimensions\\(\\) return type has no value type specified in iterable type array\\.$#" 710 | count: 1 711 | path: library/Cube/IcingaDb/IcingaDbCube.php 712 | 713 | - 714 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:getAvailableFactColumns\\(\\) return type has no value type specified in iterable type array\\.$#" 715 | count: 1 716 | path: library/Cube/IcingaDb/IcingaDbCube.php 717 | 718 | - 719 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:getObjectsFilter\\(\\) has no return type specified\\.$#" 720 | count: 1 721 | path: library/Cube/IcingaDb/IcingaDbCube.php 722 | 723 | - 724 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:getSortBy\\(\\) return type has no value type specified in iterable type array\\.$#" 725 | count: 1 726 | path: library/Cube/IcingaDb/IcingaDbCube.php 727 | 728 | - 729 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:prepareFullQuery\\(\\) has no return type specified\\.$#" 730 | count: 1 731 | path: library/Cube/IcingaDb/IcingaDbCube.php 732 | 733 | - 734 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:prepareRollupQuery\\(\\) has no return type specified\\.$#" 735 | count: 1 736 | path: library/Cube/IcingaDb/IcingaDbCube.php 737 | 738 | - 739 | message: "#^Parameter \\#1 \\$filter of method ipl\\\\Orm\\\\Query\\:\\:filter\\(\\) expects ipl\\\\Stdlib\\\\Filter\\\\Rule, ipl\\\\Stdlib\\\\Filter\\\\Rule\\|null given\\.$#" 740 | count: 1 741 | path: library/Cube/IcingaDb/IcingaDbCube.php 742 | 743 | - 744 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:\\$objectsFilter has no type specified\\.$#" 745 | count: 1 746 | path: library/Cube/IcingaDb/IcingaDbCube.php 747 | 748 | - 749 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:\\$sortBy \\(array\\) does not accept mixed\\.$#" 750 | count: 1 751 | path: library/Cube/IcingaDb/IcingaDbCube.php 752 | 753 | - 754 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbCube\\:\\:\\$sortBy type has no value type specified in iterable type array\\.$#" 755 | count: 1 756 | path: library/Cube/IcingaDb/IcingaDbCube.php 757 | 758 | - 759 | message: "#^Cannot access property \\$host on mixed\\.$#" 760 | count: 1 761 | path: library/Cube/IcingaDb/IcingaDbHostStatusCube.php 762 | 763 | - 764 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbHostStatusCube\\:\\:getAvailableFactColumns\\(\\) return type has no value type specified in iterable type array\\.$#" 765 | count: 1 766 | path: library/Cube/IcingaDb/IcingaDbHostStatusCube.php 767 | 768 | - 769 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbHostStatusCube\\:\\:listAvailableDimensions\\(\\) has no return type specified\\.$#" 770 | count: 1 771 | path: library/Cube/IcingaDb/IcingaDbHostStatusCube.php 772 | 773 | - 774 | message: "#^Cannot access property \\$host_name on mixed\\.$#" 775 | count: 1 776 | path: library/Cube/IcingaDb/IcingaDbServiceStatusCube.php 777 | 778 | - 779 | message: "#^Cannot access property \\$service_name on mixed\\.$#" 780 | count: 1 781 | path: library/Cube/IcingaDb/IcingaDbServiceStatusCube.php 782 | 783 | - 784 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbServiceStatusCube\\:\\:getAvailableFactColumns\\(\\) return type has no value type specified in iterable type array\\.$#" 785 | count: 1 786 | path: library/Cube/IcingaDb/IcingaDbServiceStatusCube.php 787 | 788 | - 789 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\IcingaDb\\\\IcingaDbServiceStatusCube\\:\\:listAvailableDimensions\\(\\) has no return type specified\\.$#" 790 | count: 1 791 | path: library/Cube/IcingaDb/IcingaDbServiceStatusCube.php 792 | 793 | - 794 | message: "#^Call to an undefined method Icinga\\\\Module\\\\Cube\\\\Ido\\\\ZfSelectWrapper\\:\\:joinLeft\\(\\)\\.$#" 795 | count: 1 796 | path: library/Cube/Ido/CustomVarDimension.php 797 | 798 | - 799 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\CustomVarDimension\\:\\:__construct\\(\\) has parameter \\$varName with no type specified\\.$#" 800 | count: 1 801 | path: library/Cube/Ido/CustomVarDimension.php 802 | 803 | - 804 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\CustomVarDimension\\:\\:safeVarname\\(\\) has no return type specified\\.$#" 805 | count: 1 806 | path: library/Cube/Ido/CustomVarDimension.php 807 | 808 | - 809 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\CustomVarDimension\\:\\:safeVarname\\(\\) has parameter \\$name with no type specified\\.$#" 810 | count: 1 811 | path: library/Cube/Ido/CustomVarDimension.php 812 | 813 | - 814 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Ido\\\\CustomVarDimension\\:\\:\\$type \\(string\\) does not accept string\\|null\\.$#" 815 | count: 1 816 | path: library/Cube/Ido/CustomVarDimension.php 817 | 818 | - 819 | message: "#^Call to an undefined method Icinga\\\\Data\\\\ConnectionInterface\\:\\:getResource\\(\\)\\.$#" 820 | count: 1 821 | path: library/Cube/Ido/DataView/Hoststatus.php 822 | 823 | - 824 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\DataView\\\\Hoststatus\\:\\:__construct\\(\\) has parameter \\$columns with no value type specified in iterable type array\\.$#" 825 | count: 1 826 | path: library/Cube/Ido/DataView/Hoststatus.php 827 | 828 | - 829 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\DbCube\\:\\:chooseFacts\\(\\) has parameter \\$facts with no value type specified in iterable type array\\.$#" 830 | count: 1 831 | path: library/Cube/Ido/DbCube.php 832 | 833 | - 834 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\DbCube\\:\\:fetchAll\\(\\) return type has no value type specified in iterable type array\\.$#" 835 | count: 1 836 | path: library/Cube/Ido/DbCube.php 837 | 838 | - 839 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\DbCube\\:\\:fetchAll\\(\\) should return array but returns array\\|null\\.$#" 840 | count: 1 841 | path: library/Cube/Ido/DbCube.php 842 | 843 | - 844 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\DbCube\\:\\:getAvailableFactColumns\\(\\) return type has no value type specified in iterable type array\\.$#" 845 | count: 1 846 | path: library/Cube/Ido/DbCube.php 847 | 848 | - 849 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\DbCube\\:\\:setDbName\\(\\) has parameter \\$name with no type specified\\.$#" 850 | count: 1 851 | path: library/Cube/Ido/DbCube.php 852 | 853 | - 854 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Ido\\\\DbCube\\:\\:\\$factColumns type has no value type specified in iterable type array\\.$#" 855 | count: 1 856 | path: library/Cube/Ido/DbCube.php 857 | 858 | - 859 | message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#" 860 | count: 1 861 | path: library/Cube/Ido/IdoCube.php 862 | 863 | - 864 | message: "#^Cannot call method getDbAdapter\\(\\) on mixed\\.$#" 865 | count: 1 866 | path: library/Cube/Ido/IdoCube.php 867 | 868 | - 869 | message: "#^Cannot call method getRestrictions\\(\\) on Icinga\\\\User\\|null\\.$#" 870 | count: 1 871 | path: library/Cube/Ido/IdoCube.php 872 | 873 | - 874 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoCube\\:\\:filterProtectedCustomvars\\(\\) should return array\\ but returns array\\|false\\.$#" 875 | count: 1 876 | path: library/Cube/Ido/IdoCube.php 877 | 878 | - 879 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoCube\\:\\:getIdoVersion\\(\\) should return string but returns string\\|false\\|null\\.$#" 880 | count: 1 881 | path: library/Cube/Ido/IdoCube.php 882 | 883 | - 884 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoCube\\:\\:getMonitoringRestriction\\(\\) has no return type specified\\.$#" 885 | count: 1 886 | path: library/Cube/Ido/IdoCube.php 887 | 888 | - 889 | message: "#^Parameter \\#1 \\$connection of method Icinga\\\\Module\\\\Cube\\\\Ido\\\\DbCube\\:\\:setConnection\\(\\) expects Icinga\\\\Data\\\\Db\\\\DbConnection, mixed given\\.$#" 890 | count: 1 891 | path: library/Cube/Ido/IdoCube.php 892 | 893 | - 894 | message: "#^Parameter \\#2 \\$subject of function preg_split expects string, mixed given\\.$#" 895 | count: 1 896 | path: library/Cube/Ido/IdoCube.php 897 | 898 | - 899 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoCube\\:\\:\\$availableFacts type has no value type specified in iterable type array\\.$#" 900 | count: 1 901 | path: library/Cube/Ido/IdoCube.php 902 | 903 | - 904 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoCube\\:\\:\\$idoVersion \\(string\\) does not accept string\\|false\\|null\\.$#" 905 | count: 1 906 | path: library/Cube/Ido/IdoCube.php 907 | 908 | - 909 | message: "#^Call to an undefined method Icinga\\\\Data\\\\SimpleQuery\\:\\:getSelectQuery\\(\\)\\.$#" 910 | count: 2 911 | path: library/Cube/Ido/IdoHostStatusCube.php 912 | 913 | - 914 | message: "#^Call to an undefined method Icinga\\\\Data\\\\SimpleQuery\\:\\:requireColumn\\(\\)\\.$#" 915 | count: 1 916 | path: library/Cube/Ido/IdoHostStatusCube.php 917 | 918 | - 919 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoHostStatusCube\\:\\:getAvailableFactColumns\\(\\) return type has no value type specified in iterable type array\\.$#" 920 | count: 1 921 | path: library/Cube/Ido/IdoHostStatusCube.php 922 | 923 | - 924 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoHostStatusCube\\:\\:listAvailableDimensions\\(\\) return type has no value type specified in iterable type array\\.$#" 925 | count: 1 926 | path: library/Cube/Ido/IdoHostStatusCube.php 927 | 928 | - 929 | message: "#^Call to an undefined method Icinga\\\\Data\\\\SimpleQuery\\:\\:getSelectQuery\\(\\)\\.$#" 930 | count: 2 931 | path: library/Cube/Ido/IdoServiceStatusCube.php 932 | 933 | - 934 | message: "#^Call to an undefined method Icinga\\\\Data\\\\SimpleQuery\\:\\:requireColumn\\(\\)\\.$#" 935 | count: 1 936 | path: library/Cube/Ido/IdoServiceStatusCube.php 937 | 938 | - 939 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoServiceStatusCube\\:\\:getAvailableFactColumns\\(\\) return type has no value type specified in iterable type array\\.$#" 940 | count: 1 941 | path: library/Cube/Ido/IdoServiceStatusCube.php 942 | 943 | - 944 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\IdoServiceStatusCube\\:\\:listAvailableDimensions\\(\\) return type has no value type specified in iterable type array\\.$#" 945 | count: 1 946 | path: library/Cube/Ido/IdoServiceStatusCube.php 947 | 948 | - 949 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\Query\\\\HoststatusQuery\\:\\:joinSubQuery\\(\\) return type has no value type specified in iterable type array\\.$#" 950 | count: 1 951 | path: library/Cube/Ido/Query/HoststatusQuery.php 952 | 953 | - 954 | message: "#^Property Icinga\\\\Module\\\\Cube\\\\Ido\\\\Query\\\\HoststatusQuery\\:\\:\\$subQueryTargets type has no value type specified in iterable type array\\.$#" 955 | count: 1 956 | path: library/Cube/Ido/Query/HoststatusQuery.php 957 | 958 | - 959 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\ZfSelectWrapper\\:\\:__call\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" 960 | count: 1 961 | path: library/Cube/Ido/ZfSelectWrapper.php 962 | 963 | - 964 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\ZfSelectWrapper\\:\\:columns\\(\\) has no return type specified\\.$#" 965 | count: 1 966 | path: library/Cube/Ido/ZfSelectWrapper.php 967 | 968 | - 969 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\ZfSelectWrapper\\:\\:columns\\(\\) has parameter \\$cols with no type specified\\.$#" 970 | count: 1 971 | path: library/Cube/Ido/ZfSelectWrapper.php 972 | 973 | - 974 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\ZfSelectWrapper\\:\\:columns\\(\\) has parameter \\$correlationName with no type specified\\.$#" 975 | count: 1 976 | path: library/Cube/Ido/ZfSelectWrapper.php 977 | 978 | - 979 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\ZfSelectWrapper\\:\\:reset\\(\\) has no return type specified\\.$#" 980 | count: 1 981 | path: library/Cube/Ido/ZfSelectWrapper.php 982 | 983 | - 984 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Ido\\\\ZfSelectWrapper\\:\\:reset\\(\\) has parameter \\$part with no type specified\\.$#" 985 | count: 1 986 | path: library/Cube/Ido/ZfSelectWrapper.php 987 | 988 | - 989 | message: "#^Parameter \\#1 \\$rule of method ipl\\\\Stdlib\\\\Filter\\\\Chain\\:\\:add\\(\\) expects ipl\\\\Stdlib\\\\Filter\\\\Rule, ipl\\\\Stdlib\\\\Filter\\\\Rule\\|null given\\.$#" 990 | count: 1 991 | path: library/Cube/ProvidedHook/Cube/IcingaDbActions.php 992 | 993 | - 994 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Web\\\\Controller\\:\\:hasLegacyDimensionParams\\(\\) has parameter \\$dimensions with no value type specified in iterable type array\\.$#" 995 | count: 1 996 | path: library/Cube/Web/Controller.php 997 | 998 | - 999 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Web\\\\Controller\\:\\:transformLegacyDimensionParamsAndRedirect\\(\\) has parameter \\$legacyDimensions with no value type specified in iterable type array\\.$#" 1000 | count: 1 1001 | path: library/Cube/Web/Controller.php 1002 | 1003 | - 1004 | message: "#^Parameter \\#2 \\$default of method Icinga\\\\Web\\\\UrlParams\\:\\:shift\\(\\) expects string\\|null, false given\\.$#" 1005 | count: 1 1006 | path: library/Cube/Web/Controller.php 1007 | 1008 | - 1009 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Web\\\\IdoController\\:\\:hasIcingadbDimensionParams\\(\\) has parameter \\$dimensions with no value type specified in iterable type array\\.$#" 1010 | count: 1 1011 | path: library/Cube/Web/IdoController.php 1012 | 1013 | - 1014 | message: "#^Method Icinga\\\\Module\\\\Cube\\\\Web\\\\IdoController\\:\\:transformIcingadbDimensionParamsAndRedirect\\(\\) has parameter \\$icingadbDimensions with no value type specified in iterable type array\\.$#" 1015 | count: 1 1016 | path: library/Cube/Web/IdoController.php 1017 | 1018 | - 1019 | message: "#^Parameter \\#2 \\$default of method Icinga\\\\Web\\\\UrlParams\\:\\:shift\\(\\) expects string\\|null, false given\\.$#" 1020 | count: 1 1021 | path: library/Cube/Web/IdoController.php 1022 | --------------------------------------------------------------------------------