Codebase list icingaweb2-module-cube / 9f04a49
Merging upstream version 1.2.1. Signed-off-by: David Kunz <david.kunz@dknet.ch> David Kunz 1 year, 9 months ago
44 changed file(s) with 1904 addition(s) and 1932 deletion(s). Raw diff Collapse all Expand all
0 name: L10n Update
1
2 on:
3 push:
4 branches:
5 - master
6
7 jobs:
8 trigger-update:
9 name: L10n Update Trigger
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Repository dispatch
14 uses: peter-evans/repository-dispatch@v1
15 with:
16 token: ${{ secrets.ICINGABOT_TOKEN }}
17 repository: Icinga/L10n
18 event-type: update
19 client-payload: '{"origin": "${{ github.repository }}", "commit": "${{ github.sha }}"}'
1616 strategy:
1717 fail-fast: false
1818 matrix:
19 php: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4']
19 php: ['7.2', '7.3', '7.4', '8.0', '8.1']
2020 os: ['ubuntu-latest']
21 include:
22 - php: '5.6'
23 allow_failure: true
24 - php: '7.0'
25 allow_failure: true
2621
2722 steps:
2823 - name: Checkout code base
2924 uses: actions/checkout@v2
3025
3126 - name: Setup PHP
32 uses: shivammathur/setup-php@v1
27 uses: shivammathur/setup-php@v2
3328 with:
3429 php-version: ${{ matrix.php }}
3530 tools: phpcs
0 The Icinga Web 2 Cube
1 =====================
0 # Icinga Cube
21
3 The Cube is a tiny but useful [Icinga Web 2](https://github.com/Icinga/icingaweb2)
2 [![PHP Support](https://img.shields.io/badge/php-%3E%3D%207.0-777BB4?logo=PHP)](https://php.net/)
3 ![Build Status](https://github.com/icinga/icingaweb2-module-cube/workflows/PHP%20Tests/badge.svg?branch=master)
4 [![Github Tag](https://img.shields.io/github/tag/Icinga/icingaweb2-module-cube.svg)](https://github.com/Icinga/icingaweb2-module-cube)
5
6 ![Icinga Logo](https://icinga.com/wp-content/uploads/2014/06/icinga_logo.png)
7
8 The Icinga Cube is a tiny but useful [Icinga Web 2](https://github.com/Icinga/icingaweb2)
49 module. It currently shows host and service statistics (total count, health) grouped by
510 various custom variables in multiple dimensions.
611
5661 For Developers
5762 --------------
5863
59 Our main intention when developing the Cube was too provide a modular small
64 Our main intention when developing the Icinga Cube was too provide a modular small
6065 framework allowing you to drill into data provided from various sources. So it
6166 provides hooks for custom Cubes, related Renderers or just custom action links.
6267
6368 For DBAs
6469 --------
6570
66 Yes, it's mostly what the name suggests. Just a configurable Rollup Cube,
71 Yes, it's mostly what the name suggests. Just a configurable rollup cube,
6772 providing a frontend allowing you to add and flip dimensions at will. You
6873 can slice and dice your data, drill down and pivot/rotate the whole cube.
6974
7075 Requirements
7176 ------------
7277
73 If you are using PostgreSQL, you need at least 9.5 which provides the `ROLLUP`
74 feature.
78 Icinga Web 2 v2.9 and the icinga-php-library v0.9.0.
79
80 If you are using PostgreSQL, you need at least 9.5 which provides the `ROLLUP` feature.
7581
7682 Installation
7783 ------------
7884
79 Nothing special. As with every Icinga Web 2 module, drop this to one of your
80 `modules` folders and make sure that the folder name is `cube`.
85 Install it [like any other module](https://icinga.com/docs/icinga-web-2/latest/doc/08-Modules/#installation).
86 Use `cube` as name.
8187
8288 Configuration
8389 -------------
8894 -----------
8995
9096 Someone able to access the cube will only see statistics for the objects they have permissions to view. Blacklist properties also apply.
91 Restrictions applied to other modules will have no effect, at least not unless those modules provide their own Cube hooks taking care about such.
97 Restrictions applied to other modules will have no effect, at least not unless those modules provide their own Icinga Cube hooks taking care about such.
22
33 namespace Icinga\Module\Cube\Controllers;
44
5 use Icinga\Application\Modules\Module;
6 use Icinga\Module\Cube\IcingaDb\IcingaDbHostStatusCube;
57 use Icinga\Module\Cube\Ido\IdoHostStatusCube;
8 use Icinga\Module\Cube\ProvidedHook\Icingadb\IcingadbSupport;
69 use Icinga\Module\Cube\Web\IdoController;
710
811 class HostsController extends IdoController
1619
1720 protected function getCube()
1821 {
22 if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) {
23 return new IcingaDbHostStatusCube();
24 }
25
1926 return new IdoHostStatusCube();
2027 }
2128 }
22
33 namespace Icinga\Module\Cube\Controllers;
44
5 use Icinga\Application\Modules\Module;
6 use Icinga\Module\Cube\IcingaDb\IcingaDbServiceStatusCube;
57 use Icinga\Module\Cube\Ido\IdoServiceStatusCube;
8 use Icinga\Module\Cube\ProvidedHook\Icingadb\IcingadbSupport;
69 use Icinga\Module\Cube\Web\IdoController;
710
811 class ServicesController extends IdoController
1619
1720 protected function getCube()
1821 {
22 if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) {
23 return new IcingaDbServiceStatusCube();
24 }
25
1926 return new IdoServiceStatusCube();
2027 }
2128 }
33 namespace Icinga\Module\Cube\Forms;
44
55 use Icinga\Module\Cube\Cube;
6 use Icinga\Module\Cube\Web\Form\QuickForm;
7 use Icinga\Module\Cube\Web\IconHelper;
8
9 class DimensionsForm extends QuickForm
6 use Icinga\Module\Cube\Dimension;
7 use Icinga\Module\Cube\DimensionParams;
8 use Icinga\Web\Form;
9 use Icinga\Web\Notification;
10
11 class DimensionsForm extends Form
1012 {
1113 /**
1214 * @var Cube
1921 return $this;
2022 }
2123
22 public function setup()
24 public function createElements(array $formData)
2325 {
2426 $cube = $this->cube;
25
26 if (count($cube->listDimensions()) < 3) {
27 $dimensions = array_diff(
28 $cube->listAdditionalDimensions(),
29 $cube->listDimensions()
30 );
31
32 if (! empty($dimensions)) {
33 $dimensions = array_combine($dimensions, $dimensions);
34 }
27 $dimensions = $cube->listDimensions();
28 $cnt = count($dimensions);
29
30 if ($cnt < 3) {
31 $allDimensions = $cube->listAdditionalDimensions();
3532
3633 $this->addElement('select', 'addDimension', [
37 'multiOptions' => [null => $this->translate('+ Add a dimension')] + $dimensions,
34 'multiOptions' => [null => $this->translate('+ Add a dimension')] + $allDimensions,
3835 'decorators' => ['ViewHelper'],
3936 'class' => 'autosubmit'
4037 ]);
4138 }
4239
43 $dimensions = $cube->listDimensions();
44 $cnt = count($dimensions);
45 foreach ($dimensions as $pos => $dimension) {
46 $this->addDimensionButtons($dimension, $pos, $cnt);
40 $pos = 0;
41 foreach ($dimensions as $dimension) {
42 $this->addDimensionButtons($dimension, $pos++, $cnt);
4743 }
4844
4945 foreach ($cube->getSlices() as $key => $value) {
50 $this->addSlice($key, $value);
51 }
52
53
54 $this->setSubmitLabel(false);
55 }
56
57 protected function addSlice($key, $value)
46 $this->addSlice($this->cube->getDimension($key), $value);
47 }
48
49 $this->addAttribs(['class' => 'icinga-controls']);
50 }
51
52 protected function addSlice(Dimension $dimension, $value)
5853 {
5954 $view = $this->getView();
6055
61 $this->addElement('submit', 'removeSlice_' . $key, array(
62 'label' => IconHelper::instance()->iconCharacter('cancel'),
63 'decorators' => array('ViewHelper')
64 ));
56 $sliceId = sha1($dimension->getName());
57 $this->addElement('button', 'removeSlice_' . $sliceId, [
58 'label' => $view->icon('cancel'),
59 'decorators' => ['ViewHelper'],
60 'value' => $dimension->getName(),
61 'type' => 'submit',
62 'escape' => false,
63 'class' => 'dimension-control'
64 ]);
6565
6666 $label = $view->escape(
6767 sprintf(
6868 '%s: %s = %s',
6969 $view->translate('Slice/Filter'),
70 $key,
70 $dimension->getLabel(),
7171 $value
7272 )
7373 );
7474
75 $this->addHtml(
76 '<span class="dimension-name">' . $label . '</span>',
77 array('name' => 'slice_' . $key)
75 $this->addElement('note', 'slice_' . $sliceId, [
76 'class' => 'dimension-name',
77 'value' => '<span class="dimension-name">' . $label . '</span>',
78 'decorators' => ['ViewHelper']
79 ]);
80
81 $this->addDisplayGroup(
82 [
83 'removeSlice_' . $sliceId,
84 'slice_' . $sliceId,
85 ],
86 $dimension->getName(),
87 [
88 'class' => 'dimensions',
89 'decorators' => [
90 'FormElements',
91 'Fieldset'
92 ]
93 ]
7894 );
79
80 $this->addSimpleDisplayGroup(
81 array(
82 'removeSlice_' . $key,
83 'slice_' . $key,
84 ),
85 $key,
86 array('class' => 'dimensions')
95 }
96
97 protected function addDimensionButtons(Dimension $dimension, $pos, $total)
98 {
99 $view = $this->getView();
100 $dimensionId = sha1($dimension->getName());
101
102 $this->addElement('note', 'dimension_' . $dimensionId, [
103 'class' => 'dimension-name',
104 'value' => '<span class="dimension-name">' . $view->escape($dimension->getLabel()) . '</span>',
105 'decorators' => ['ViewHelper']
106 ]);
107
108 $this->addElement('button', 'removeDimension_' . $dimensionId, [
109 'label' => $view->icon('cancel'),
110 'decorators' => ['ViewHelper'],
111 'title' => sprintf($this->translate('Remove dimension "%s"'), $dimension->getLabel()),
112 'value' => $dimension->getName(),
113 'type' => 'submit',
114 'escape' => false,
115 'class' => 'dimension-control'
116 ]);
117
118 if ($pos > 0) {
119 $this->addElement('button', 'moveDimensionUp_' . $dimensionId, [
120 'label' => $view->icon('angle-double-up'),
121 'decorators' => ['ViewHelper'],
122 'title' => sprintf($this->translate('Move dimension "%s" up'), $dimension->getLabel()),
123 'value' => $dimension->getName(),
124 'type' => 'submit',
125 'escape' => false,
126 'class' => 'dimension-control'
127 ]);
128 }
129
130 if ($pos + 1 !== $total) {
131 $this->addElement('button', 'moveDimensionDown_' . $dimensionId, [
132 'label' => $view->icon('angle-double-down'),
133 'decorators' => ['ViewHelper'],
134 'title' => sprintf($this->translate('Move dimension "%s" down'), $dimension->getLabel()),
135 'value' => $dimension->getName(),
136 'type' => 'submit',
137 'escape' => false,
138 'class' => 'dimension-control'
139 ]);
140 }
141
142 $this->addDisplayGroup(
143 [
144 'removeDimension_' . $dimensionId,
145 'moveDimensionUp_' . $dimensionId,
146 'moveDimensionDown_' . $dimensionId,
147 'dimension_' . $dimensionId,
148 ],
149 $dimensionId,
150 [
151 'class' => 'dimensions',
152 'decorators' => [
153 'FormElements',
154 'Fieldset'
155 ]
156 ]
87157 );
88158 }
89159
90 protected function addDimensionButtons($dimension, $pos, $total)
91 {
92 $this->addHtml(
93 '<span class="dimension-name">' . $this->getView()->escape($dimension) . '</span>',
94 array('name' => 'dimension_' . $dimension)
95 );
96 $icons = IconHelper::instance();
97 $this->addElement('submit', 'removeDimension_' . $dimension, array(
98 'label' => $icons->iconCharacter('cancel'),
99 'decorators' => array('ViewHelper'),
100 'title' => sprintf($this->translate('Remove dimension "%s"'), $dimension),
101 ));
102
103 $this->addElement('submit', 'moveDimensionUp_' . $dimension, array(
104 'label' => $icons->iconCharacter('angle-double-left'),
105 'decorators' => array('ViewHelper'),
106 'title' => sprintf($this->translate('Move dimension "%s" up'), $dimension),
107 ));
108
109 $this->addElement('submit', 'moveDimensionDown_' . $dimension, array(
110 'label' => $icons->iconCharacter('angle-double-right'),
111 'decorators' => array('ViewHelper'),
112 'title' => sprintf($this->translate('Move dimension "%s" down'), $dimension),
113 ));
114
115 if ($pos === 0) {
116 $this->getElement('moveDimensionUp_' . $dimension)->disabled = 'disabled';
117 }
118
119 if ($pos + 1 === $total) {
120 $this->getElement('moveDimensionDown_' . $dimension)->disabled = 'disabled';
121 }
122
123 $this->addSimpleDisplayGroup(
124 array(
125 'removeDimension_' . $dimension,
126 'moveDimensionUp_' . $dimension,
127 'moveDimensionDown_' . $dimension,
128 'dimension_' . $dimension,
129 ),
130 $dimension,
131 array('class' => 'dimensions')
132 );
133 }
134
135 public function onRequest()
136 {
137 parent::onRequest();
138 if (! $this->hasBeenSent()) {
139 return;
140 }
141
142 $url = $this->getSuccessUrl();
143 $post = $this->getRequest()->getPost();
144 $this->populate($post);
145 $cube = $this->cube;
146 $dimension = null;
147
148 foreach ($this->getElements() as $el) {
149 if (! $el->getValue()) {
150 // Skip unpressed buttons
151 continue;
160 public function onSuccess()
161 {
162 $url = $this->getRequest()->getUrl();
163
164 if ($dimension = $this->getValue('addDimension')) {
165 $url->setParam('dimensions', DimensionParams::fromUrl($url)->add($dimension)->getParams());
166 Notification::success($this->translate('New dimension has been added'));
167 } else {
168 $updateDimensions = false;
169 foreach ($this->cube->listDimensions() as $name => $_) {
170 $dimensionId = sha1($name);
171
172 switch (true) {
173 case ($el = $this->getElement('removeDimension_' . $dimensionId)) && $el->isChecked():
174 $this->cube->removeDimension($name);
175 $updateDimensions = true;
176 break 2;
177 case ($el = $this->getElement('moveDimensionUp_' . $dimensionId)) && $el->isChecked():
178 $this->cube->moveDimensionUp($name);
179 $updateDimensions = true;
180 break 2;
181 case ($el = $this->getElement('moveDimensionDown_' . $dimensionId)) && $el->isChecked():
182 $this->cube->moveDimensionDown($name);
183 $updateDimensions = true;
184 break 2;
185 }
152186 }
153 $name = $el->getName();
154 $pos = strpos($name, '_');
155
156 if ($pos === false || $pos === 0) {
157 continue;
187
188 if ($updateDimensions) {
189 $dimensions = array_merge(array_keys($this->cube->listDimensions()), $this->cube->listSlices());
190 $url->setParam('dimensions', DimensionParams::update($dimensions)->getParams());
191 } else {
192 foreach ($this->cube->listSlices() as $slice) {
193 $sliceId = sha1($slice);
194
195 if (($el = $this->getElement('removeSlice_' . $sliceId)) && $el->isChecked()) {
196 $url->getParams()->remove(rawurlencode($slice));
197 }
198 }
158199 }
159
160 $action = substr($name, 0, $pos);
161 $name = substr($name, $pos + 1);
162
163 switch ($action) {
164 case 'removeSlice':
165 $dimension = $name;
166 $url->getParams()->remove($dimension);
167 break 2;
168
169 case 'removeDimension':
170 $dimension = $name;
171 $cube->removeDimension($dimension);
172 break 2;
173
174 case 'moveDimensionUp':
175 $dimension = $name;
176 $cube->moveDimensionUp($dimension);
177 break 2;
178
179 case 'moveDimensionDown':
180 $dimension = $name;
181 $cube->moveDimensionDown($dimension);
182 break 2;
183 default:
184 }
185 }
186
187 if ($dimension) {
188 $dimensions = array_merge($cube->listDimensions(), $cube->listSlices());
189 if ($action !== 'removeSlice') {
190 $url->setParam('dimensions', implode(',', $dimensions));
191 }
192 $this->redirectAndExit($url);
193 }
194
195 if ($dimension = $this->getSentValue('addDimension')) {
196 $dimensions = $url->getParam('dimensions');
197 if (empty($dimensions)) {
198 $dimensions = $dimension;
199 } else {
200 $dimensions .= ',' . $dimension;
201 }
202 $url->setParam('dimensions', $dimensions);
203
204 $this->setSuccessUrl($url->without('addDimension'));
205 $this->redirectOnSuccess($this->translate('New dimension has been added'));
206 }
200 }
201
202 $this->setRedirectUrl($url);
207203 }
208204 }
+0
-16
application/views/helpers/FormSimpleNote.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 // Avoid complaints about missing namespace and invalid class name
4 // @codingStandardsIgnoreStart
5 class Zend_View_Helper_FormSimpleNote extends Zend_View_Helper_FormElement
6 {
7 // @codingStandardsIgnoreEnd
8
9 public function formSimpleNote($name, $value = null)
10 {
11 $info = $this->_getInfo($name, $value);
12 extract($info); // name, value, attribs, options, listsep, disable
13 return $value;
14 }
15 }
22 <?= $this->tabs ?>
33 <h1><?= $this->escape($this->title) ?></h1>
44 <?php if ($this->form && ! $this->compact): ?>
5 <?= $this->qlink('Hide settings', $this->url()->without('showSettings'), null, array('class' => 'icon-wrench')) ?>
5 <?= $this->qlink('Hide settings', $this->url()->without('showSettings'), null, ['icon' => 'wrench']) ?>
66 <?php else: ?>
7 <?= $this->qlink('Show settings', $this->url()->with('showSettings', true), null, array('class' => 'icon-wrench')) ?>
7 <?= $this->qlink('Show settings', $this->url()->with('showSettings', true), null, ['icon' => 'wrench']) ?>
88 <?php endif ?>
99 <?php endif ?>
1010 </div>
1414 <?= $this->form ?>
1515 <?php endif ?>
1616 <?php if ($this->cube): ?>
17 <?= $cube->render($this) ?>
17 <?= $this->cube->render($this) ?>
1818 <?php endif ?>
1919 </div>
77
88 abstract class Cube
99 {
10 /** @var array<string, Dimension> Available dimensions */
11 protected $availableDimensions;
12
1013 /** @var array Fact names */
1114 protected $chosenFacts;
1215
5861 return null;
5962 }
6063
61 return implode(' -> ', $dimensions);
64 return implode(' -> ', array_map(function ($d) {
65 return $d->getLabel();
66 }, $dimensions));
6267 }
6368
6469 public function getSlicesLabel()
7075 return null;
7176 }
7277 foreach ($slices as $key => $value) {
73 $parts[] = sprintf('%s = %s', $key, $value);
78 $parts[] = sprintf('%s = %s', $this->getDimension($key)->getLabel(), $value);
7479 }
7580
7681 return implode(', ', $parts);
7782 }
7883
84 /**
85 * Create a new dimension
86 *
87 * @param string $name
88 * @return Dimension
89 */
90 abstract public function createDimension($name);
91
92 protected function registerAvailableDimensions()
93 {
94 if ($this->availableDimensions !== null) {
95 return;
96 }
97
98 $this->availableDimensions = [];
99 foreach ($this->listAvailableDimensions() as $label) {
100 $name = strtolower($label);
101 if (! isset($this->availableDimensions[$name])) {
102 $this->availableDimensions[$name] = $this->createDimension($name)->setLabel($label);
103 } else {
104 $this->availableDimensions[$name]->addLabel($label);
105 }
106 }
107 }
108
79109 public function listAdditionalDimensions()
80110 {
81 $list = array();
82
83 foreach ($this->listAvailableDimensions() as $dimension) {
84 if (! array_key_exists($dimension, $this->dimensions)) {
85 $list[] = $dimension;
111 $this->registerAvailableDimensions();
112
113 $list = [];
114 foreach ($this->availableDimensions as $name => $dimension) {
115 if (! $this->hasDimension($name)) {
116 $list[$name] = $dimension->getLabel();
86117 }
87118 }
88119
96127 $found = false;
97128 $after = null;
98129
99 foreach ($this->listDimensions() as $d) {
130 foreach ($this->listDimensions() as $k => $d) {
100131 if ($found) {
101132 $after = $d;
102133 break;
103134 }
104135
105 if ($d === $name) {
136 if ($k === $name) {
106137 $found = true;
107138 }
108139 }
113144 public function listDimensionsUpTo($name)
114145 {
115146 $res = array();
116 foreach ($this->listDimensions() as $d) {
147 foreach ($this->listDimensions() as $d => $_) {
117148 $res[] = $d;
118149 if ($d === $name) {
119150 break;
128159 $last = $found = null;
129160 $positions = array_keys($this->dimensions);
130161
131 while (list($k, $v) = each($positions)) {
162 foreach ($positions as $k => $v) {
132163 if ($v === $name) {
133164 $found = $k;
134165 break;
150181 $next = $found = null;
151182 $positions = array_keys($this->dimensions);
152183
153 while (list($k, $v) = each($positions)) {
184 foreach ($positions as $k => $v) {
154185 if ($found !== null) {
155186 $next = $k;
156187 break;
266297
267298 public function listDimensions()
268299 {
269 return array_values(
270 array_diff(array_keys($this->dimensions), $this->listSlices())
271 );
300 return array_diff_key($this->dimensions, $this->slices);
272301 }
273302
274303 public function listColumns()
275304 {
276 return array_merge($this->listDimensions(), $this->listFacts());
305 return array_merge(array_keys($this->listDimensions()), $this->listFacts());
277306 }
278307
279308 /**
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\CubeRenderer;
4
5 use Icinga\Module\Cube\CubeRenderer;
6
7 class HostStatusCubeRenderer extends CubeRenderer
8 {
9 protected function renderDimensionLabel($name, $row)
10 {
11 $htm = parent::renderDimensionLabel($name, $row);
12
13 if (($next = $this->cube->getDimensionAfter($name)) && isset($this->summaries->{$next->getName()})) {
14 $htm .= ' <span class="sum">(' . $this->summaries->{$next->getName()}->hosts_cnt . ')</span>';
15 }
16
17 return $htm;
18 }
19
20 protected function getDimensionClasses($name, $row)
21 {
22 $classes = parent::getDimensionClasses($name, $row);
23
24 $sums = $row;
25 if ($sums->hosts_down > 0) {
26 $classes[] = 'critical';
27 if ((int) $sums->hosts_unhandled_down === 0) {
28 $classes[] = 'handled';
29 }
30 } elseif ($sums->hosts_unreachable > 0) {
31 $classes[] = 'unreachable';
32 if ((int) $sums->hosts_unhandled_unreachable === 0) {
33 $classes[] = 'handled';
34 }
35 } else {
36 $classes[] = 'ok';
37 }
38
39 return $classes;
40 }
41
42 public function renderFacts($facts)
43 {
44 $indent = str_repeat(' ', 3);
45 $parts = array();
46
47 if ($facts->hosts_unhandled_down > 0) {
48 $parts['critical'] = $facts->hosts_unhandled_down;
49 }
50
51 if ($facts->hosts_down > 0 && $facts->hosts_down > $facts->hosts_unhandled_down) {
52 $parts['critical handled'] = $facts->hosts_down - $facts->hosts_unhandled_down;
53 }
54
55 if ($facts->hosts_unhandled_unreachable > 0) {
56 $parts['unreachable'] = $facts->hosts_unhandled_unreachable;
57 }
58
59 if ($facts->hosts_unreachable > 0 && $facts->hosts_unreachable > $facts->hosts_unhandled_unreachable) {
60 $parts['unreachable handled'] = $facts->hosts_unreachable - $facts->hosts_unhandled_unreachable;
61 }
62
63 if ($facts->hosts_cnt > $facts->hosts_down && $facts->hosts_cnt > $facts->hosts_unreachable) {
64 $parts['ok'] = $facts->hosts_cnt - $facts->hosts_down - $facts->hosts_unreachable;
65 }
66
67 $main = '';
68 $sub = '';
69 foreach ($parts as $class => $count) {
70 if ($main === '') {
71 $main = $this->makeBadgeHtml($class, $count);
72 } else {
73 $sub .= $this->makeBadgeHtml($class, $count);
74 }
75 }
76 if ($sub !== '') {
77 $sub = $indent
78 . '<span class="others">'
79 . "\n "
80 . $sub
81 . $indent
82 . "</span>\n";
83 }
84
85 return $main . $sub;
86 }
87
88 protected function makeBadgeHtml($class, $count)
89 {
90 $indent = str_repeat(' ', 3);
91 return sprintf(
92 '%s<span class="%s">%s</span>',
93 $indent,
94 $class,
95 $count
96 ) . "\n";
97 }
98
99 protected function getDetailsBaseUrl()
100 {
101 return 'cube/hosts/details';
102 }
103 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2019 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\CubeRenderer;
4
5 use Icinga\Module\Cube\CubeRenderer;
6
7 class ServiceStatusCubeRenderer extends CubeRenderer
8 {
9 public function renderFacts($facts)
10 {
11 $indent = str_repeat(' ', 3);
12 $parts = [];
13
14 if ($facts->services_unhandled_critical > 0) {
15 $parts['critical'] = $facts->services_unhandled_critical;
16 }
17
18 if ($facts->services_critical > 0 && $facts->services_critical > $facts->services_unhandled_critical) {
19 $parts['critical handled'] = $facts->services_critical - $facts->services_unhandled_critical;
20 }
21
22 if ($facts->services_unhandled_warning > 0) {
23 $parts['warning'] = $facts->services_unhandled_warning;
24 }
25
26 if ($facts->services_warning > 0 && $facts->services_warning > $facts->services_unhandled_warning) {
27 $parts['warning handled'] = $facts->services_warning - $facts->services_unhandled_warning;
28 }
29
30 if ($facts->services_unhandled_unknown > 0) {
31 $parts['unknown'] = $facts->services_unhandled_unknown;
32 }
33
34 if ($facts->services_unknown > 0 && $facts->services_unknown > $facts->services_unhandled_unknown) {
35 $parts['unknown handled'] = $facts->services_unknown - $facts->services_unhandled_unknown;
36 }
37
38 if ($facts->services_cnt > $facts->services_critical && $facts->services_cnt > $facts->services_warning
39 && $facts->services_cnt > $facts->services_unknown) {
40 $parts['ok'] = $facts->services_cnt - $facts->services_critical - $facts->services_warning -
41 $facts->services_unknown;
42 }
43
44 $main = '';
45 $sub = '';
46 foreach ($parts as $class => $count) {
47 if ($main === '') {
48 $main = $this->makeBadgeHtml($class, $count);
49 } else {
50 $sub .= $this->makeBadgeHtml($class, $count);
51 }
52 }
53 if ($sub !== '') {
54 $sub = $indent
55 . '<span class="others">'
56 . "\n "
57 . $sub
58 . $indent
59 . "</span>\n";
60 }
61
62 return $main . $sub;
63 }
64
65 /**
66 * @inheritdoc
67 */
68 protected function renderDimensionLabel($name, $row)
69 {
70 $htm = parent::renderDimensionLabel($name, $row);
71
72 if (($next = $this->cube->getDimensionAfter($name)) && isset($this->summaries->{$next->getName()})) {
73 $htm .= ' <span class="sum">(' . $this->summaries->{$next->getName()}->services_cnt . ')</span>';
74 }
75
76 return $htm;
77 }
78
79 protected function getDimensionClasses($name, $row)
80 {
81 $classes = parent::getDimensionClasses($name, $row);
82
83 $sums = $row;
84 if ($sums->services_critical > 0) {
85 $classes[] = 'critical';
86 if ((int) $sums->services_unhandled_critical === 0) {
87 $classes[] = 'handled';
88 }
89 } elseif ($sums->services_warning > 0) {
90 $classes[] = 'warning';
91 if ((int) $sums->services_unhandled_warning === 0) {
92 $classes[] = 'handled';
93 }
94 } elseif ($sums->services_unknown > 0) {
95 $classes[] = 'unknown';
96 if ((int) $sums->services_unhandled_unknown === 0) {
97 $classes[] = 'handled';
98 }
99 } else {
100 $classes[] = 'ok';
101 }
102
103 return $classes;
104 }
105
106 protected function makeBadgeHtml($class, $count)
107 {
108 $indent = str_repeat(' ', 3);
109
110 return sprintf(
111 '%s<span class="%s">%s</span>',
112 $indent,
113 $class,
114 $count
115 ) . "\n";
116 }
117
118 protected function getDetailsBaseUrl()
119 {
120 return 'cube/services/details';
121 }
122 }
2222 /** @var Cube */
2323 protected $cube;
2424
25 /** @var array Our dimension in regular order */
25 /** @var array Our dimensions */
2626 protected $dimensions;
2727
28 /** @var array Our dimension in reversed order as a quick lookup source */
28 /** @var array Our dimensions in regular order */
29 protected $dimensionOrder;
30
31 /** @var array Our dimensions in reversed order as a quick lookup source */
2932 protected $reversedDimensions;
3033
3134 /** @var array Level (deepness) for each dimension (0, 1, 2...) */
9295 protected function initializeLastRow()
9396 {
9497 $object = (object) array();
95 foreach ($this->dimensions as $key) {
96 $object->$key = null;
98 foreach ($this->dimensions as $dimension) {
99 $object->{$dimension->getName()} = null;
97100 }
98101
99102 $this->lastRow = $object;
111114 $min = 3;
112115 $cnt = count($this->dimensions);
113116 if ($cnt < $min) {
117 $pos = 0;
114118 $diff = $min - $cnt;
115 $dimensions = $this->dimensions;
116 $this->dimensions = array();
117 foreach ($dimensions as $key => $dimension) {
118 $this->dimensions[$key + $diff] = $dimension;
119 }
120 }
121
122 $this->reversedDimensions = array_reverse($this->dimensions);
123 $this->dimensionLevels = array_flip($this->dimensions);
119 $this->dimensionOrder = [];
120 foreach ($this->dimensions as $name => $_) {
121 $this->dimensionOrder[$pos++ + $diff] = $name;
122 }
123 } else {
124 $this->dimensionOrder = array_keys($this->dimensions);
125 }
126
127 $this->reversedDimensions = array_reverse($this->dimensionOrder);
128 $this->dimensionLevels = array_flip($this->dimensionOrder);
124129 return $this;
125130 }
126131
148153 */
149154 protected function startsDimension($row)
150155 {
151 foreach ($this->dimensions as $name) {
156 foreach ($this->dimensionOrder as $name) {
152157 if ($row->$name === null) {
153 $this->summaries->{$name} = $this->extractFacts($row);
158 $this->summaries->$name = $this->extractFacts($row);
154159 return true;
155160 }
156161 }
178183 $this->view = $view;
179184 $this->initialize();
180185 $htm = $this->beginContainer();
181
182186 foreach ($this->cube->fetchAll() as $row) {
183187 $htm .= $this->renderRow($row);
184188 }
203207 protected function beginDimensionsForRow($row)
204208 {
205209 $last = $this->lastRow;
206 foreach ($this->dimensions as $name) {
210 foreach ($this->dimensionOrder as $name) {
207211 if ($last->$name !== $row->$name) {
208212 return $this->beginDimensionsUpFrom($name, $row);
209213 }
217221 $htm = '';
218222 $found = false;
219223
220 foreach ($this->dimensions as $name) {
224 foreach ($this->dimensionOrder as $name) {
221225 if ($name === $dimension) {
222226 $found = true;
223227 }
233237 protected function closeDimensionsForRow($row)
234238 {
235239 $last = $this->lastRow;
236 foreach ($this->dimensions as $name) {
240 foreach ($this->dimensionOrder as $name) {
237241 if ($last->$name !== $row->$name) {
238242 return $this->closeDimensionsDownTo($name);
239243 }
289293 $this->started = true;
290294 }
291295 $view = $this->view;
296 $dimension = $this->cube->getDimension($name);
292297
293298 return
294299 $indent . '<div class="'
296301 . '">' . "\n"
297302 . $indent . ' <div class="header"><a href="'
298303 . $this->getDetailsUrl($name, $row)
299 . '" title="' . $view->escape(sprintf('Show details for %s: %s', $name, $row->$name)) . '"'
304 . '" title="' . $view->escape(sprintf('Show details for %s: %s', $dimension->getLabel(), $row->$name)) . '"'
300305 . ' data-base-target="_next">'
301306 . $this->renderDimensionLabel($name, $row)
302307 . '</a><a class="icon-filter" href="'
328333 {
329334 $cube = $this->cube;
330335
331 $params = array(
332 'dimensions' => implode(
333 ',',
334 array_merge($cube->listDimensions(), $cube->listSlices())
335 )
336 );
336 $dimensions = array_merge(array_keys($cube->listDimensions()), $cube->listSlices());
337 $params = [
338 'dimensions' => DimensionParams::update($dimensions)->getParams()
339 ];
337340
338341 foreach ($this->cube->listDimensionsUpTo($name) as $dimensionName) {
339342 $params[$dimensionName] = $row->$dimensionName;
+0
-296
library/Cube/DbCube.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube;
4
5 use Icinga\Data\Db\DbConnection;
6
7 abstract class DbCube extends Cube
8 {
9 /** @var DbConnection */
10 protected $connection;
11
12 /** @var \Zend_Db_Adapter_Abstract */
13 protected $db;
14
15 /** @var ZfSelectWrapper The inner query fetching all required data */
16 protected $innerQuery;
17
18 /** @var \Zend_Db_Select The rollup query, creating grouped sums over innerQuery */
19 protected $rollupQuery;
20
21 /** @var \Zend_Db_Select The outer query, orders respecting NULL values, rollup first */
22 protected $fullQuery;
23
24 /** @var string Database name. Allows to eventually join over multiple dbs */
25 protected $dbName;
26
27 /** @var array Key/value array containing our chosen facts and the corresponding SQL expression */
28 protected $factColumns = array();
29
30 /**
31 * A DbCube must provide a list of all available columns
32 *
33 * This is a key/value array with the key being the fact name / column alias
34 * and
35 *
36 * @return array
37 */
38 abstract public function getAvailableFactColumns();
39
40 /**
41 * @return \Zend_Db_Select
42 */
43 abstract public function prepareInnerQuery();
44
45 /**
46 * Set a database connection
47 *
48 * @param DbConnection $connection
49 * @return $this
50 */
51 public function setConnection(DbConnection $connection)
52 {
53 $this->connection = $connection;
54 $this->db = $connection->getDbAdapter();
55 return $this;
56 }
57
58 /**
59 * Prepare the query and fetch all data
60 *
61 * @return array
62 */
63 public function fetchAll()
64 {
65 $query = $this->fullQuery();
66 return $this->db()->fetchAll($query);
67 }
68
69 /**
70 * Choose a one or more facts
71 *
72 * This also initializes a fact column lookup array
73 *
74 * @param array $facts
75 * @return $this
76 */
77 public function chooseFacts(array $facts)
78 {
79 parent::chooseFacts($facts);
80
81 $this->factColumns = array();
82 $columns = $this->getAvailableFactColumns();
83 foreach ($this->chosenFacts as $name) {
84 $this->factColumns[$name] = $columns[$name];
85 }
86
87 return $this;
88 }
89
90 /**
91 * @param $name
92 * @return $this
93 */
94 public function setDbName($name)
95 {
96 $this->dbName = $name;
97 return $this;
98 }
99
100 /**
101 * Gives back the table name, eventually prefixed with a defined DB name
102 *
103 * @param string $name
104 * @return string
105 */
106 public function tableName($name)
107 {
108 if ($this->dbName === null) {
109 return $name;
110 } else {
111 return $this->dbName . '.' . $name;
112 }
113 }
114
115 /**
116 * Returns an eventually defined DB name
117 *
118 * @return string|null
119 */
120 public function getDbName()
121 {
122 return $this->dbName;
123 }
124
125 /**
126 * Get our inner query
127 *
128 * Hint: mostly used to get rid of NULL values
129 *
130 * @return ZfSelectWrapper
131 */
132 public function innerQuery()
133 {
134 if ($this->innerQuery === null) {
135 $this->innerQuery = new ZfSelectWrapper($this->prepareInnerQuery());
136 }
137
138 return $this->innerQuery;
139 }
140
141 /**
142 * We first prepare the queries and to finalize it later on
143 *
144 * This way dimensions can be added one by one, they will be allowed to
145 * optionally join additional tables or apply other modifications late
146 * in the process
147 *
148 * @return void
149 */
150 public function finalizeInnerQuery()
151 {
152 $query = $this->innerQuery()->unwrap();
153 $columns = array();
154 foreach ($this->dimensions as $name => $dimension) {
155 $dimension->addToCube($this);
156 if ($this->hasSlice($name)) {
157 $query->where(
158 $dimension->getColumnExpression($this) . ' = ?',
159 $this->slices[$name]
160 );
161 } else {
162 $columns[$name] = $dimension->getColumnExpression($this);
163 }
164 }
165
166 $c = [];
167
168 foreach ($columns + $this->factColumns as $k => $v) {
169 $c[$this->db()->quoteIdentifier([$k])] = $v;
170 }
171
172 $query->columns($c);
173 }
174
175 /**
176 * Lazy-load our full query
177 *
178 * @return \Zend_Db_Select
179 */
180 protected function fullQuery()
181 {
182 if ($this->fullQuery === null) {
183 $this->fullQuery = $this->prepareFullQuery();
184 }
185
186 return $this->fullQuery;
187 }
188
189 /**
190 * Lazy-load our full query
191 *
192 * @return \Zend_Db_Select
193 */
194 protected function rollupQuery()
195 {
196 if ($this->rollupQuery === null) {
197 $this->rollupQuery = $this->prepareRollupQuery();
198 }
199
200 return $this->rollupQuery;
201 }
202
203 /**
204 * The full query wraps the rollup query in a sub-query to work around
205 * MySQL limitations. This is required to not get into trouble when ordering,
206 * especially combined with the need to keep control over (eventually desired)
207 * NULL value fact columns
208 *
209 * @return \Zend_Db_Select
210 */
211 protected function prepareFullQuery()
212 {
213 $alias = 'rollup';
214 $cols = $this->listColumns();
215
216 $columns = array();
217
218 foreach ($cols as $col) {
219 $columns[$this->db()->quoteIdentifier([$col])] = $alias . '.' . $this->db()->quoteIdentifier([$col]);
220 }
221
222 $select = $this->db()->select()->from(
223 array($alias => $this->rollupQuery()),
224 $columns
225 );
226
227 foreach ($columns as $col) {
228 $select->order('(' . $col . ' IS NOT NULL)');
229 $select->order($col);
230 }
231
232 return $select;
233 }
234
235 /**
236 * Provide access to our DB
237 *
238 * @return \Zend_Db_Adapter_Abstract
239 */
240 public function db()
241 {
242 return $this->db;
243 }
244
245 /**
246 * Whether our connection is PostgreSQL
247 *
248 * @return bool
249 */
250 public function isPgsql()
251 {
252 return $this->connection->getDbType() === 'pgsql';
253 }
254
255
256 /**
257 * This prepares the rollup query
258 *
259 * Inner query is wrapped in a subquery, summaries for all facts are
260 * fetched. Rollup considers all defined dimensions and expects them
261 * to exist as columns in the innerQuery
262 *
263 * @return \Zend_Db_Select
264 */
265 protected function prepareRollupQuery()
266 {
267 $alias = 'sub';
268
269 $dimensions = array_map(function ($val) {
270 return $this->db()->quoteIdentifier([$val]);
271 }, $this->listDimensions());
272 $this->finalizeInnerQuery();
273 $columns = array();
274 foreach ($dimensions as $dimension) {
275 $columns[$dimension] = $alias . '.' . $dimension;
276 }
277
278 foreach ($this->listFacts() as $fact) {
279 $columns[$fact] = 'SUM(' . $fact . ')';
280 }
281
282 $select = $this->db()->select()->from(
283 array($alias => $this->innerQuery()->unwrap()),
284 $columns
285 );
286
287 if ($this->isPgsql()) {
288 $select->group('ROLLUP (' . implode(', ', $dimensions) . ')');
289 } else {
290 $select->group('(' . implode('), (', $dimensions) . ') WITH ROLLUP');
291 }
292
293 return $select;
294 }
295 }
1212 interface Dimension
1313 {
1414 /**
15 * The name of this dimenstion
15 * The name of this dimension
1616 *
1717 * @return string
1818 */
1919 public function getName();
20
21 /**
22 * The label of this dimension
23 *
24 * @return string
25 */
26 public function getLabel();
2027
2128 /**
2229 * Column expression
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2020 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube;
4
5 use Icinga\Web\Url;
6
7 class DimensionParams
8 {
9
10 /**
11 * @var array Raw dimensions
12 */
13 protected $dimensions = [];
14
15 /**
16 * @var string encoded dimensions separated by coma
17 */
18 protected $params;
19
20 // For the form: DimensionsParam::fromUrl($url)
21 public static function fromUrl(Url $url)
22 {
23 return static::fromString($url->getParam('dimensions'));
24 }
25
26 public static function fromArray(array $dimensions = [])
27 {
28 $self = new static();
29
30 $self->dimensions = array_filter($dimensions);
31
32 return $self;
33 }
34
35 // For the controller: DimensionsParam::fromArray($this->params->shift('dimensions'))
36 public static function fromString($dimensions)
37 {
38 return static::fromArray(explode(',', $dimensions));
39 }
40
41
42 /**
43 * @param $dimension
44 *
45 * @return $this
46 */
47 public function add($dimension)
48 {
49 if (! empty($dimension)) {
50 $this->dimensions[] = $dimension;
51 }
52
53 return $this;
54 }
55
56 /**
57 * Overwrite dimensions
58 *
59 * @param $dimensions
60 *
61 * @return $this
62 */
63 public static function update($dimensions)
64 {
65 $self = new static();
66 $self->dimensions = $dimensions;
67
68 return $self;
69 }
70
71 /**
72 * @return string encoded dimensions separated by coma
73 */
74 public function getParams()
75 {
76 return implode(',', $this->dimensions);
77 }
78
79 /**
80 * @return array
81 */
82 public function getDimensions()
83 {
84 return $this->dimensions;
85 }
86 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Hook;
4
5 use Exception;
6 use Icinga\Application\Hook;
7 use Icinga\Module\Cube\Cube;
8 use Icinga\Module\Cube\IcingaDb\IcingaDbCube;
9 use ipl\Web\Url;
10 use ipl\Html\HtmlDocument;
11 use ipl\Html\HtmlElement;
12 use ipl\Web\Widget\Icon;
13 use ipl\Web\Widget\Link;
14
15 /**
16 * ActionsHook
17 *
18 * Implement this hook in case your module wants to add links to the detail
19 * page shown for a slice.
20 *
21 * @package Icinga\Module\Cube\Hook
22 */
23 abstract class IcingaDbActionsHook
24 {
25 /** @var Link[] */
26 private $actionLinks = [];
27
28 /**
29 * Create additional action links for the given cube
30 *
31 * @param IcingaDbCube $cube
32 * @return void
33 */
34 abstract public function createActionLinks(IcingaDbCube $cube);
35
36 /**
37 * Return the action links for the cube
38 *
39 * @return Link[]
40 */
41 final protected function getActionLinks(): array
42 {
43 return $this->actionLinks;
44 }
45
46 /**
47 * Helper method to populate action links array
48 *
49 * @param Url $url
50 * @param string $title
51 * @param string $description
52 * @param string $icon
53 *
54 * @return $this
55 */
56 final protected function addActionLink(Url $url, string $title, string $description, string $icon): self
57 {
58 $linkContent = (new HtmlDocument());
59 $linkContent->addHtml(new Icon($icon));
60 $linkContent->addHtml(HtmlElement::create('span', ['class' => 'title'], $title));
61 $linkContent->addHtml(HtmlElement::create('p', null, $description));
62
63 $this->actionLinks[] = new Link($linkContent, $url);
64
65 return $this;
66 }
67
68 /**
69 * Helper method instantiating an Url object
70 *
71 * @param string $path
72 * @param array $params
73 * @return Url
74 */
75 final protected function makeUrl(string $path, array $params = null): Url
76 {
77 $url = Url::fromPath($path);
78 if ($params !== null) {
79 $url->getParams()->mergeValues($params);
80 }
81
82 return $url;
83 }
84
85 /**
86 * Render all links for all Hook implementations
87 *
88 * This is what the Cube calls when rendering details
89 *
90 * @param IcingaDbCube $cube
91 *
92 * @return string
93 */
94 public static function renderAll(Cube $cube)
95 {
96 $html = new HtmlDocument();
97
98 /** @var IcingaDbActionsHook $hook */
99 foreach (Hook::all('Cube/IcingaDbActions') as $hook) {
100 try {
101 $hook->createActionLinks($cube);
102 } catch (Exception $e) {
103 $html->addHtml(HtmlElement::create('li', ['class' => 'error'], $e->getMessage()));
104 }
105
106 foreach ($hook->getActionLinks() as $link) {
107 $html->addHtml(HtmlElement::create('li', null, $link));
108 }
109 }
110
111 if ($html->isEmpty()) {
112 $html->addHtml(
113 HtmlElement::create(
114 'li',
115 ['class' => 'error'],
116 t('No action links have been provided for this cube')
117 )
118 );
119 }
120
121 return $html->render();
122 }
123 }
0 <?php
1 // Icinga DB Web | (c) 2022 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\IcingaDb;
4
5 use Icinga\Module\Cube\Cube;
6 use Icinga\Module\Cube\Dimension;
7 use Icinga\Module\Icingadb\Model\CustomvarFlat;
8 use ipl\Orm\Model;
9 use ipl\Sql\Expression;
10
11 class CustomVariableDimension implements Dimension
12 {
13 protected $name;
14
15 protected $label;
16
17 protected $wantNull = false;
18
19 public function __construct($name)
20 {
21 $this->name = $name;
22 }
23
24 public function getName()
25 {
26 return $this->name;
27 }
28
29 public function getLabel()
30 {
31 return $this->label ?: $this->getName();
32 }
33
34 /**
35 * Set the label
36 *
37 * @param string $label
38 * @return $this
39 */
40 public function setLabel($label)
41 {
42 $this->label = $label;
43
44 return $this;
45 }
46
47 /**
48 * Add a label
49 *
50 * @param string $label
51 * @return $this
52 */
53 public function addLabel($label)
54 {
55 if ($this->label === null) {
56 $this->setLabel($label);
57 } else {
58 $this->label .= ' & ' . $label;
59 }
60
61 return $this;
62 }
63
64 /**
65 * Define whether null values should be shown
66 *
67 * @param bool $wantNull
68 * @return $this
69 */
70 public function wantNull($wantNull = true)
71 {
72 $this->wantNull = $wantNull;
73
74 return $this;
75 }
76
77 /**
78 * @param IcingaDbCube $cube
79 * @return Expression|string
80 */
81 public function getColumnExpression(Cube $cube)
82 {
83 $expression = $cube->getDb()->quoteIdentifier(['c_' . $this->getName(), 'flatvalue']);
84
85 if ($this->wantNull) {
86 return new Expression("COALESCE($expression, '-')");
87 }
88
89 return $expression;
90 }
91
92 public function addToCube(Cube $cube)
93 {
94 /** @var IcingaDbCube $cube */
95 $name = $this->getName();
96 $innerQuery = $cube->innerQuery();
97 $resolver = $innerQuery->getResolver();
98 $sourceTable = $innerQuery->getModel()->getTableName();
99
100 foreach ($resolver->resolveRelations($sourceTable . '.vars') as $relation) {
101 foreach ($relation->resolve() as list($source, $target, $relatedKeys)) {
102 /** @var Model $source */
103 /** @var Model $target */
104
105 $sourceAlias = $resolver->getAlias($source);
106 if ($sourceAlias !== $resolver->getAlias($innerQuery->getModel())) {
107 $sourceAlias = $cube->getDb()->quoteIdentifier(
108 [$sourceAlias . '_' . $name]
109 );
110 }
111
112 if ($target instanceof CustomvarFlat) {
113 $targetAlias = $cube->getDb()->quoteIdentifier(['c_' . $name]);
114 } else {
115 $targetAlias = $cube->getDb()->quoteIdentifier(
116 [$resolver->getAlias($target) . '_' . $name]
117 );
118 }
119
120 $conditions = [];
121 foreach ($relatedKeys as $fk => $ck) {
122 $conditions[] = sprintf(
123 '%s = %s',
124 $resolver->qualifyColumn($fk, $targetAlias),
125 $resolver->qualifyColumn($ck, $sourceAlias)
126 );
127 }
128
129 if ($target instanceof CustomvarFlat) {
130 $innerQuery->getSelectBase()->groupBy("$targetAlias.flatvalue");
131 $conditions[sprintf('%s = ?', $resolver->qualifyColumn('flatname', $targetAlias))] = $name;
132 }
133
134 $table = [$targetAlias => $target->getTableName()];
135 $innerQuery->getSelectBase()->join($table, $conditions);
136 }
137 }
138 }
139 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2022 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\IcingaDb;
4
5 use Icinga\Module\Cube\Cube;
6 use Icinga\Module\Icingadb\Common\Auth;
7 use Icinga\Module\Icingadb\Common\Database;
8 use ipl\Orm\Query;
9 use ipl\Sql\Select;
10
11 abstract class IcingaDbCube extends Cube
12 {
13 use Auth;
14 use Database;
15
16 /** @var Query The inner query fetching all required data */
17 protected $innerQuery;
18
19 /** @var Select The rollup query, creating grouped sums over innerQuery */
20 protected $rollupQuery;
21
22 /** @var Select The outer query, orders respecting NULL values, rollup first */
23 protected $fullQuery;
24
25 protected $objectsFilter;
26
27 abstract public function getObjectsFilter();
28 /**
29 * An IcingaDbCube must provide a list of all available columns
30 *
31 * This is a key/value array with the key being the fact name / column alias
32 * and
33 *
34 * @return array
35 */
36 abstract public function getAvailableFactColumns();
37
38 /**
39 * @return Query
40 */
41 abstract public function prepareInnerQuery();
42
43 /**
44 * Get our inner query
45 *
46 * Hint: mostly used to get rid of NULL values
47 *
48 * @return Query
49 */
50 public function innerQuery()
51 {
52 if ($this->innerQuery === null) {
53 $this->innerQuery = $this->prepareInnerQuery();
54 }
55
56 return $this->innerQuery;
57 }
58
59 /**
60 * Get our rollup query
61 *
62 * @return Select
63 */
64 protected function rollupQuery()
65 {
66 if ($this->rollupQuery === null) {
67 $this->rollupQuery = $this->prepareRollupQuery();
68 }
69
70 return $this->rollupQuery;
71 }
72
73 /**
74 * Add a specific named dimension
75 *
76 * @param string $name
77 * @return $this
78 */
79 public function addDimensionByName($name)
80 {
81 $this->addDimension($this->createDimension($name));
82
83 return $this;
84 }
85
86 /**
87 * We first prepare the queries and to finalize it later on
88 *
89 * This way dimensions can be added one by one, they will be allowed to
90 * optionally join additional tables or apply other modifications late
91 * in the process
92 *
93 * @return void
94 */
95 protected function finalizeInnerQuery()
96 {
97 $query = $this->innerQuery()->getSelectBase();
98 $columns = [];
99 foreach ($this->dimensions as $name => $dimension) {
100 $quotedDimension = $this->getDb()->quoteIdentifier([$name]);
101 $dimension->addToCube($this);
102 $columns[$quotedDimension] = $dimension->getColumnExpression($this);
103
104 if ($this->hasSlice($name)) {
105 $query->where(
106 $dimension->getColumnExpression($this) . ' = ?',
107 $this->slices[$name]
108 );
109 } else {
110 $columns[$quotedDimension] = $dimension->getColumnExpression($this);
111 }
112 }
113
114 $query->columns($columns);
115 }
116
117 protected function prepareRollupQuery()
118 {
119 $dimensions = $this->listDimensions();
120 $this->finalizeInnerQuery();
121
122 $columns = [];
123 $groupBy = [];
124 foreach ($dimensions as $name => $dimension) {
125 $quotedDimension = $this->getDb()->quoteIdentifier([$name]);
126
127 $columns[$quotedDimension] = 'f.' . $quotedDimension;
128 $groupBy[] = $quotedDimension;
129 }
130
131 $availableFacts = $this->getAvailableFactColumns();
132
133 foreach ($this->chosenFacts as $alias) {
134 $columns[$alias] = 'SUM(f.' . $availableFacts[$alias] . ')';
135 }
136
137 if (! empty($groupBy)) {
138 $groupBy[count($groupBy) - 1] .= ' WITH ROLLUP';
139 }
140
141 $rollupQuery = new Select();
142 $rollupQuery->from(['f' => $this->innerQuery()->assembleSelect()])
143 ->columns($columns)
144 ->groupBy($groupBy);
145
146 return $rollupQuery;
147 }
148
149 protected function prepareFullQuery()
150 {
151 $rollupQuery = $this->rollupQuery();
152 $columns = [];
153 foreach ($this->listColumns() as $column) {
154 $quotedColumn = $this->getDb()->quoteIdentifier([$column]);
155 $columns[$quotedColumn] = 'rollup.' . $this->getDb()->quoteIdentifier([$column]);
156 }
157
158 $fullQuery = new Select();
159 $fullQuery->from(['rollup' => $rollupQuery])->columns($columns);
160
161 foreach ($columns as $quotedColumn => $_) {
162 $fullQuery->orderBy("($quotedColumn IS NOT NULL)");
163 $fullQuery->orderBy($quotedColumn);
164 }
165
166 return $fullQuery;
167 }
168
169 /**
170 * Lazy-load our full query
171 *
172 * @return Select
173 */
174 protected function fullQuery()
175 {
176 if ($this->fullQuery === null) {
177 $this->fullQuery = $this->prepareFullQuery();
178 }
179
180 return $this->fullQuery;
181 }
182
183 public function fetchAll()
184 {
185 $query = $this->fullQuery();
186 return $this->getDb()->fetchAll($query);
187 }
188 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2022 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\IcingaDb;
4
5 use Icinga\Module\Cube\CubeRenderer\HostStatusCubeRenderer;
6 use Icinga\Module\Icingadb\Model\CustomvarFlat;
7 use Icinga\Module\Icingadb\Model\Host;
8 use Icinga\Module\Icingadb\Model\HoststateSummary;
9 use ipl\Stdlib\Filter;
10
11 class IcingaDbHostStatusCube extends IcingaDbCube
12 {
13 public function getRenderer()
14 {
15 return new HostStatusCubeRenderer($this);
16 }
17
18 public function getAvailableFactColumns()
19 {
20 return [
21 'hosts_cnt' => 'hosts_total',
22 'hosts_down' => 'hosts_down_handled + f.hosts_down_unhandled',
23 'hosts_unhandled_down' => 'hosts_down_unhandled',
24 'hosts_unreachable' => 'hosts_unreachable',
25 'hosts_unhandled_unreachable' => 'hosts_unreachable_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 $db = $this->getDb();
43
44 $query = CustomvarFlat::on($db);
45 $this->applyRestrictions($query);
46
47 $query
48 ->columns('flatname')
49 ->orderBy('flatname')
50 ->filter(Filter::like('host.id', '*'));
51 $query->getSelectBase()->groupBy('flatname');
52
53 return $db->fetchCol($query->assembleSelect());
54 }
55
56 public function prepareInnerQuery()
57 {
58 $query = HoststateSummary::on($this->getDb());
59 $query->columns(array_diff_key($query->getModel()->getColumns(), (new Host())->getColumns()));
60 $query->disableDefaultSort();
61 $this->applyRestrictions($query);
62
63 $this->innerQuery = $query;
64 return $this->innerQuery;
65 }
66
67 /**
68 * Return Filter for Hosts cube.
69 *
70 * @return Filter\Any|Filter\Chain
71 */
72 public function getObjectsFilter()
73 {
74 if ($this->objectsFilter === null) {
75 $this->finalizeInnerQuery();
76
77 $hosts = $this->innerQuery()->columns(['host' => 'host.name']);
78 $hosts->getSelectBase()->resetGroupBy();
79
80 $filter = Filter::any();
81
82 foreach ($hosts as $object) {
83 $filter->add(Filter::equal('host.name', $object->host));
84 }
85
86 $this->objectsFilter = $filter;
87 }
88
89 return $this->objectsFilter;
90 }
91 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2022 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\IcingaDb;
4
5 use Icinga\Module\Cube\CubeRenderer\ServiceStatusCubeRenderer;
6 use Icinga\Module\Icingadb\Model\CustomvarFlat;
7 use Icinga\Module\Icingadb\Model\Service;
8 use Icinga\Module\Icingadb\Model\ServicestateSummary;
9 use ipl\Stdlib\Filter;
10
11 class IcingaDbServiceStatusCube extends IcingaDbCube
12 {
13 public function getRenderer()
14 {
15 return new ServiceStatusCubeRenderer($this);
16 }
17
18 public function createDimension($name)
19 {
20 $this->registerAvailableDimensions();
21
22 if (isset($this->availableDimensions[$name])) {
23 return clone $this->availableDimensions[$name];
24 }
25
26 return new CustomVariableDimension($name);
27 }
28
29 public function getAvailableFactColumns()
30 {
31 return [
32 'services_cnt' => 'services_total',
33 'services_critical' => 'services_critical_handled + f.services_critical_unhandled',
34 'services_unhandled_critical' => 'services_critical_unhandled',
35 'services_warning' => 'services_warning_handled + f.services_warning_unhandled',
36 'services_unhandled_warning' => 'services_warning_unhandled',
37 'services_unknown' => 'services_unknown_handled + f.services_unknown_unhandled',
38 'services_unhandled_unknown' => 'services_unknown_unhandled',
39 ];
40 }
41
42 public function listAvailableDimensions()
43 {
44 $db = $this->getDb();
45
46 $query = CustomvarFlat::on($db);
47 $this->applyRestrictions($query);
48
49 $query
50 ->columns('flatname')
51 ->orderBy('flatname')
52 ->filter(Filter::like('service.id', '*'));
53 $query->getSelectBase()->groupBy('flatname');
54
55 return $db->fetchCol($query->assembleSelect());
56 }
57
58 public function prepareInnerQuery()
59 {
60 $query = ServicestateSummary::on($this->getDb());
61 $query->columns(array_diff_key($query->getModel()->getColumns(), (new Service())->getColumns()));
62 $query->disableDefaultSort();
63 $this->applyRestrictions($query);
64
65 return $query;
66 }
67
68 /**
69 * Return Filter for Services cube.
70 *
71 * @return Filter\Any|Filter\Chain
72 */
73 public function getObjectsFilter()
74 {
75 if ($this->objectsFilter === null) {
76 $this->finalizeInnerQuery();
77
78 $services = $this->innerQuery()->columns([
79 'host_name' => 'host.name',
80 'service_name' => 'service.name'
81 ]);
82
83 $services->getSelectBase()->resetGroupBy();
84 $filter = Filter::any();
85
86 foreach ($services as $service) {
87 $filter->add(
88 Filter::all(
89 Filter::equal('service.name', $service->service_name),
90 Filter::equal('host.name', $service->host_name)
91 )
92 );
93 }
94
95 $this->objectsFilter = $filter;
96 }
97
98 return $this->objectsFilter;
99 }
100 }
2424 * @var string custom variable name
2525 */
2626 protected $varName;
27
28 /**
29 * @var string custom variable label
30 */
31 protected $varLabel;
2732
2833 /**
2934 * @var bool Whether null values should be shown
6267 */
6368 public function getName()
6469 {
65 return strtolower($this->varName);
70 return $this->varName;
71 }
72
73 /**
74 * @return string
75 */
76 public function getLabel()
77 {
78 return $this->varLabel ?: $this->getName();
79 }
80
81 /**
82 * Set the label
83 *
84 * @param string $label
85 * @return $this
86 */
87 public function setLabel($label)
88 {
89 $this->varLabel = $label;
90
91 return $this;
92 }
93
94 /**
95 * Add a label
96 *
97 * @param string $label
98 * @return $this
99 */
100 public function addLabel($label)
101 {
102 if ($this->varLabel === null) {
103 $this->setLabel($label);
104 } else {
105 $this->varLabel .= ' & ' . $label;
106 }
107
108 return $this;
66109 }
67110
68111 public function getColumnExpression(Cube $cube)
95138 $name = $this->safeVarname($this->varName);
96139 /** @var $cube IdoCube */
97140 $alias = $cube->db()->quoteIdentifier(['c_' . $name]);
141
142 if ($cube->isPgsql()) {
143 $on = "LOWER($alias.varname) = ?";
144 $name = strtolower($name);
145 } else {
146 $on = $alias . '.varname = ? COLLATE latin1_general_ci';
147 }
148
98149 $cube->innerQuery()->joinLeft(
99150 array($alias => $cube->tableName('icinga_customvariablestatus')),
100 $cube->db()->quoteInto($alias . '.varname = ?', $name)
151 $cube->db()->quoteInto($on, $name)
101152 . ' AND ' . $alias . '.object_id = ' . $objectId,
102153 array()
103154 )->group($alias . '.varvalue');
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2021 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Ido\DataView;
4
5 use Icinga\Data\ConnectionInterface;
6 use Icinga\Module\Cube\Ido\Query\HoststatusQuery;
7
8 class Hoststatus extends \Icinga\Module\Monitoring\DataView\Hoststatus
9 {
10 public function __construct(ConnectionInterface $connection, array $columns = null)
11 {
12 $this->connection = $connection;
13 $this->query = new HoststatusQuery($connection->getResource(), $columns);
14 }
15 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Ido;
4
5 use Icinga\Data\Db\DbConnection;
6 use Icinga\Module\Cube\Cube;
7
8 abstract class DbCube extends Cube
9 {
10 /** @var DbConnection */
11 protected $connection;
12
13 /** @var \Zend_Db_Adapter_Abstract */
14 protected $db;
15
16 /** @var ZfSelectWrapper The inner query fetching all required data */
17 protected $innerQuery;
18
19 /** @var \Zend_Db_Select The rollup query, creating grouped sums over innerQuery */
20 protected $rollupQuery;
21
22 /** @var \Zend_Db_Select The outer query, orders respecting NULL values, rollup first */
23 protected $fullQuery;
24
25 /** @var string Database name. Allows to eventually join over multiple dbs */
26 protected $dbName;
27
28 /** @var array Key/value array containing our chosen facts and the corresponding SQL expression */
29 protected $factColumns = array();
30
31 /**
32 * A DbCube must provide a list of all available columns
33 *
34 * This is a key/value array with the key being the fact name / column alias
35 * and
36 *
37 * @return array
38 */
39 abstract public function getAvailableFactColumns();
40
41 /**
42 * @return \Zend_Db_Select
43 */
44 abstract public function prepareInnerQuery();
45
46 /**
47 * Set a database connection
48 *
49 * @param DbConnection $connection
50 * @return $this
51 */
52 public function setConnection(DbConnection $connection)
53 {
54 $this->connection = $connection;
55 $this->db = $connection->getDbAdapter();
56 return $this;
57 }
58
59 /**
60 * Prepare the query and fetch all data
61 *
62 * @return array
63 */
64 public function fetchAll()
65 {
66 $query = $this->fullQuery();
67 return $this->db()->fetchAll($query);
68 }
69
70 /**
71 * Choose a one or more facts
72 *
73 * This also initializes a fact column lookup array
74 *
75 * @param array $facts
76 * @return $this
77 */
78 public function chooseFacts(array $facts)
79 {
80 parent::chooseFacts($facts);
81
82 $this->factColumns = array();
83 $columns = $this->getAvailableFactColumns();
84 foreach ($this->chosenFacts as $name) {
85 $this->factColumns[$name] = $columns[$name];
86 }
87
88 return $this;
89 }
90
91 /**
92 * @param $name
93 * @return $this
94 */
95 public function setDbName($name)
96 {
97 $this->dbName = $name;
98 return $this;
99 }
100
101 /**
102 * Gives back the table name, eventually prefixed with a defined DB name
103 *
104 * @param string $name
105 * @return string
106 */
107 public function tableName($name)
108 {
109 if ($this->dbName === null) {
110 return $name;
111 } else {
112 return $this->dbName . '.' . $name;
113 }
114 }
115
116 /**
117 * Returns an eventually defined DB name
118 *
119 * @return string|null
120 */
121 public function getDbName()
122 {
123 return $this->dbName;
124 }
125
126 /**
127 * Get our inner query
128 *
129 * Hint: mostly used to get rid of NULL values
130 *
131 * @return ZfSelectWrapper
132 */
133 public function innerQuery()
134 {
135 if ($this->innerQuery === null) {
136 $this->innerQuery = new ZfSelectWrapper($this->prepareInnerQuery());
137 }
138
139 return $this->innerQuery;
140 }
141
142 /**
143 * We first prepare the queries and to finalize it later on
144 *
145 * This way dimensions can be added one by one, they will be allowed to
146 * optionally join additional tables or apply other modifications late
147 * in the process
148 *
149 * @return void
150 */
151 public function finalizeInnerQuery()
152 {
153 $query = $this->innerQuery()->unwrap();
154 $columns = array();
155 foreach ($this->dimensions as $name => $dimension) {
156 $dimension->addToCube($this);
157 if ($this->hasSlice($name)) {
158 $query->where(
159 $dimension->getColumnExpression($this) . ' = ?',
160 $this->slices[$name]
161 );
162 } else {
163 $columns[$name] = $dimension->getColumnExpression($this);
164 }
165 }
166
167 $c = [];
168
169 foreach ($columns + $this->factColumns as $k => $v) {
170 $c[$this->db()->quoteIdentifier([$k])] = $v;
171 }
172
173 $query->columns($c);
174 }
175
176 /**
177 * Lazy-load our full query
178 *
179 * @return \Zend_Db_Select
180 */
181 protected function fullQuery()
182 {
183 if ($this->fullQuery === null) {
184 $this->fullQuery = $this->prepareFullQuery();
185 }
186
187 return $this->fullQuery;
188 }
189
190 /**
191 * Lazy-load our full query
192 *
193 * @return \Zend_Db_Select
194 */
195 protected function rollupQuery()
196 {
197 if ($this->rollupQuery === null) {
198 $this->rollupQuery = $this->prepareRollupQuery();
199 }
200
201 return $this->rollupQuery;
202 }
203
204 /**
205 * The full query wraps the rollup query in a sub-query to work around
206 * MySQL limitations. This is required to not get into trouble when ordering,
207 * especially combined with the need to keep control over (eventually desired)
208 * NULL value fact columns
209 *
210 * @return \Zend_Db_Select
211 */
212 protected function prepareFullQuery()
213 {
214 $alias = 'rollup';
215 $cols = $this->listColumns();
216
217 $columns = array();
218
219 foreach ($cols as $col) {
220 $columns[$this->db()->quoteIdentifier([$col])] = $alias . '.' . $this->db()->quoteIdentifier([$col]);
221 }
222
223 $select = $this->db()->select()->from(
224 array($alias => $this->rollupQuery()),
225 $columns
226 );
227
228 foreach ($columns as $col) {
229 $select->order('(' . $col . ' IS NOT NULL)');
230 $select->order($col);
231 }
232
233 return $select;
234 }
235
236 /**
237 * Provide access to our DB
238 *
239 * @return \Zend_Db_Adapter_Abstract
240 */
241 public function db()
242 {
243 return $this->db;
244 }
245
246 /**
247 * Whether our connection is PostgreSQL
248 *
249 * @return bool
250 */
251 public function isPgsql()
252 {
253 return $this->connection->getDbType() === 'pgsql';
254 }
255
256
257 /**
258 * This prepares the rollup query
259 *
260 * Inner query is wrapped in a subquery, summaries for all facts are
261 * fetched. Rollup considers all defined dimensions and expects them
262 * to exist as columns in the innerQuery
263 *
264 * @return \Zend_Db_Select
265 */
266 protected function prepareRollupQuery()
267 {
268 $alias = 'sub';
269
270 $dimensions = array_map(function ($val) {
271 return $this->db()->quoteIdentifier([$val]);
272 }, array_keys($this->listDimensions()));
273 $this->finalizeInnerQuery();
274 $columns = array();
275 foreach ($dimensions as $dimension) {
276 $columns[$dimension] = $alias . '.' . $dimension;
277 }
278
279 foreach ($this->listFacts() as $fact) {
280 $columns[$fact] = 'SUM(' . $fact . ')';
281 }
282
283 $select = $this->db()->select()->from(
284 array($alias => $this->innerQuery()->unwrap()),
285 $columns
286 );
287
288 if ($this->isPgsql()) {
289 $select->group('ROLLUP (' . implode(', ', $dimensions) . ')');
290 } else {
291 $select->group('(' . implode('), (', $dimensions) . ') WITH ROLLUP');
292 }
293
294 return $select;
295 }
296 }
77 use Icinga\Data\Filter\Filter;
88 use Icinga\Exception\ConfigurationError;
99 use Icinga\Exception\QueryException;
10 use Icinga\Module\Cube\DbCube;
1110 use Icinga\Module\Monitoring\Backend\MonitoringBackend;
1211 use Icinga\Util\GlobFilter;
1312
4948 {
5049 $this->backend = $backend;
5150
52 $this->setConnection($backend->getResource());
51 $resource = $backend->getResource();
52 $resource->getDbAdapter()
53 ->getConnection()
54 ->setAttribute(\PDO::ATTR_CASE, \PDO::CASE_NATURAL);
55
56 $this->setConnection($resource);
5357
5458 return $this;
5559 }
22
33 namespace Icinga\Module\Cube\Ido;
44
5 use Icinga\Module\Cube\CubeRenderer\HostStatusCubeRenderer;
6 use Icinga\Module\Cube\Ido\DataView\Hoststatus;
7
58 class IdoHostStatusCube extends IdoCube
69 {
710 public function getRenderer()
811 {
9 return new IdoHostStatusCubeRenderer($this);
12 return new HostStatusCubeRenderer($this);
1013 }
1114
1215 /**
1518 public function getAvailableFactColumns()
1619 {
1720 return array(
18 'hosts_cnt' => 'COUNT(*)',
19 'hosts_down' => 'SUM(CASE WHEN hs.current_state = 1 THEN 1 ELSE 0 END)',
20 'hosts_unhandled_down' => 'SUM(CASE WHEN hs.current_state = 1'
21 'hosts_cnt' => 'SUM(CASE WHEN hs.has_been_checked = 1 THEN 1 ELSE 0 END)',
22 'hosts_down' => 'SUM(CASE WHEN hs.has_been_checked = 1 AND hs.current_state = 1'
23 . ' THEN 1 ELSE 0 END)',
24 'hosts_unhandled_down' => 'SUM(CASE WHEN hs.has_been_checked = 1 AND hs.current_state = 1'
2125 . ' AND hs.problem_has_been_acknowledged = 0 AND hs.scheduled_downtime_depth = 0'
2226 . ' THEN 1 ELSE 0 END)',
2327 'hosts_unreachable' => 'SUM(CASE WHEN hs.current_state = 2 THEN 1 ELSE 0 END)',
2529 . ' AND hs.problem_has_been_acknowledged = 0 AND hs.scheduled_downtime_depth = 0'
2630 . ' THEN 1 ELSE 0 END)',
2731 );
32 }
33
34 public function createDimension($name)
35 {
36 $this->registerAvailableDimensions();
37
38 if (isset($this->availableDimensions[$name])) {
39 return clone $this->availableDimensions[$name];
40 }
41
42 return new CustomVarDimension($name, CustomVarDimension::TYPE_HOST);
2843 }
2944
3045 /**
3954 public function addDimensionByName($name)
4055 {
4156 if (count($this->filterProtectedCustomvars(array($name))) === 1) {
42 $this->addDimension(new CustomVarDimension($name, CustomVarDimension::TYPE_HOST));
57 $this->addDimension($this->createDimension($name));
4358 }
4459
4560 return $this;
8297 {
8398 $this->requireBackend();
8499
85 $view = $this->backend->select()->from('hoststatus');
100 $view = new Hoststatus($this->backend);
86101
87102 $view->getQuery()->requireColumn('host_state');
88103
+0
-111
library/Cube/Ido/IdoHostStatusCubeRenderer.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Ido;
4
5 use Icinga\Module\Cube\CubeRenderer;
6
7 /**
8 * Class IdoHostStatusCubeRenderer
9 * @package Icinga\Module\Cube\Ido
10 */
11 class IdoHostStatusCubeRenderer extends CubeRenderer
12 {
13 /**
14 * @inheritdoc
15 */
16 protected function renderDimensionLabel($name, $row)
17 {
18 $htm = parent::renderDimensionLabel($name, $row);
19
20 if (($next = $this->cube->getDimensionAfter($name)) && isset($this->summaries->$next)) {
21 $htm .= ' <span class="sum">(' . $this->summaries->$next->hosts_cnt . ')</span>';
22 }
23
24 return $htm;
25 }
26
27 protected function getDimensionClasses($name, $row)
28 {
29 $classes = parent::getDimensionClasses($name, $row);
30
31 $sums = $row;
32 if ($sums->hosts_down > 0) {
33 $classes[] = 'critical';
34 if ((int) $sums->hosts_unhandled_down === 0) {
35 $classes[] = 'handled';
36 }
37 } elseif ($sums->hosts_unreachable > 0) {
38 $classes[] = 'unreachable';
39 if ((int) $sums->hosts_unhandled_unreachable === 0) {
40 $classes[] = 'handled';
41 }
42 } else {
43 $classes[] = 'ok';
44 }
45
46 return $classes;
47 }
48
49 public function renderFacts($facts)
50 {
51 $indent = str_repeat(' ', 3);
52 $parts = array();
53
54 if ($facts->hosts_unhandled_down > 0) {
55 $parts['critical'] = $facts->hosts_unhandled_down;
56 }
57
58 if ($facts->hosts_down > 0 && $facts->hosts_down > $facts->hosts_unhandled_down) {
59 $parts['critical handled'] = $facts->hosts_down - $facts->hosts_unhandled_down;
60 }
61
62 if ($facts->hosts_unhandled_unreachable > 0) {
63 $parts['unreachable'] = $facts->hosts_unhandled_unreachable;
64 }
65
66 if ($facts->hosts_unreachable > 0 && $facts->hosts_unreachable > $facts->hosts_unhandled_unreachable) {
67 $parts['unreachable handled'] = $facts->hosts_unreachable - $facts->hosts_unhandled_unreachable;
68 }
69
70 if ($facts->hosts_cnt > $facts->hosts_down && $facts->hosts_cnt > $facts->hosts_unreachable) {
71 $parts['ok'] = $facts->hosts_cnt - $facts->hosts_down - $facts->hosts_unreachable;
72 }
73
74 $main = '';
75 $sub = '';
76 foreach ($parts as $class => $count) {
77 if ($main === '') {
78 $main = $this->makeBadgeHtml($class, $count);
79 } else {
80 $sub .= $this->makeBadgeHtml($class, $count);
81 }
82 }
83 if ($sub !== '') {
84 $sub = $indent
85 . '<span class="others">'
86 . "\n "
87 . $sub
88 . $indent
89 . "</span>\n";
90 }
91
92 return $main . $sub;
93 }
94
95 protected function makeBadgeHtml($class, $count)
96 {
97 $indent = str_repeat(' ', 3);
98 return sprintf(
99 '%s<span class="%s">%s</span>',
100 $indent,
101 $class,
102 $count
103 ) . "\n";
104 }
105
106 protected function getDetailsBaseUrl()
107 {
108 return 'cube/hosts/details';
109 }
110 }
22
33 namespace Icinga\Module\Cube\Ido;
44
5 use Icinga\Application\Config;
5 use Icinga\Module\Cube\CubeRenderer\ServiceStatusCubeRenderer;
66
77 class IdoServiceStatusCube extends IdoCube
88 {
99 public function getRenderer()
1010 {
11 return new IdoStatusCubeRenderer($this);
11 return new ServiceStatusCubeRenderer($this);
1212 }
1313
1414 public function getAvailableFactColumns()
1515 {
1616 return [
17 'services_cnt' => 'COUNT(*)',
18 'services_critical' => 'SUM(CASE WHEN ss.current_state = 2 THEN 1 ELSE 0 END)',
19 'services_unhandled_critical' => 'SUM(CASE WHEN ss.current_state = 2'
17 'services_cnt' => 'SUM(CASE WHEN ss.has_been_checked = 1 THEN 1 ELSE 0 END)',
18 'services_critical' => 'SUM(CASE WHEN ss.has_been_checked = 1 AND ss.current_state = 2'
19 . ' THEN 1 ELSE 0 END)',
20 'services_unhandled_critical' => 'SUM(CASE WHEN ss.has_been_checked = 1 AND ss.current_state = 2'
2021 . ' AND ss.problem_has_been_acknowledged = 0 AND ss.scheduled_downtime_depth = 0'
2122 . ' THEN 1 ELSE 0 END)',
2223 'services_warning' => 'SUM(CASE WHEN ss.current_state = 1 THEN 1 ELSE 0 END)',
9192 public function addDimensionByName($name)
9293 {
9394 if (count($this->filterProtectedCustomvars([$name])) === 1) {
94 $this->addDimension(new CustomVarDimension($name, CustomVarDimension::TYPE_SERVICE));
95 $this->addDimension($this->createDimension($name));
9596 }
9697
9798 return $this;
9899 }
100
101 public function createDimension($name)
102 {
103 $this->registerAvailableDimensions();
104
105 if (isset($this->availableDimensions[$name])) {
106 return clone $this->availableDimensions[$name];
107 }
108
109 return new CustomVarDimension($name, CustomVarDimension::TYPE_SERVICE);
110 }
99111 }
+0
-123
library/Cube/Ido/IdoStatusCubeRenderer.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2019 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Ido;
4
5 use Icinga\Module\Cube\CubeRenderer;
6
7 class IdoStatusCubeRenderer extends CubeRenderer
8 {
9 public function renderFacts($facts)
10 {
11 $indent = str_repeat(' ', 3);
12 $parts = [];
13
14 if ($facts->services_unhandled_critical > 0) {
15 $parts['critical'] = $facts->services_unhandled_critical;
16 }
17
18 if ($facts->services_critical > 0 && $facts->services_critical > $facts->services_unhandled_critical) {
19 $parts['critical handled'] = $facts->services_critical - $facts->services_unhandled_critical;
20 }
21
22 if ($facts->services_unhandled_warning > 0) {
23 $parts['warning'] = $facts->services_unhandled_warning;
24 }
25
26 if ($facts->services_warning > 0 && $facts->services_warning > $facts->services_unhandled_warning) {
27 $parts['warning handled'] = $facts->services_warning - $facts->services_unhandled_warning;
28 }
29
30 if ($facts->services_unhandled_unknown > 0) {
31 $parts['unknown'] = $facts->services_unhandled_unknown;
32 }
33
34 if ($facts->services_unknown > 0 && $facts->services_unknown > $facts->services_unhandled_unknown) {
35 $parts['unknown handled'] = $facts->services_unknown - $facts->services_unhandled_unknown;
36 }
37
38 if ($facts->services_cnt > $facts->services_critical && $facts->services_cnt > $facts->services_warning
39 && $facts->services_cnt > $facts->services_unknown) {
40 $parts['ok'] = $facts->services_cnt - $facts->services_critical - $facts->services_warning -
41 $facts->services_unknown;
42 }
43
44 $main = '';
45 $sub = '';
46 foreach ($parts as $class => $count) {
47 if ($main === '') {
48 $main = $this->makeBadgeHtml($class, $count);
49 } else {
50 $sub .= $this->makeBadgeHtml($class, $count);
51 }
52 }
53 if ($sub !== '') {
54 $sub = $indent
55 . '<span class="others">'
56 . "\n "
57 . $sub
58 . $indent
59 . "</span>\n";
60 }
61
62 return $main . $sub;
63 }
64
65 /**
66 * @inheritdoc
67 */
68 protected function renderDimensionLabel($name, $row)
69 {
70 $htm = parent::renderDimensionLabel($name, $row);
71
72 if (($next = $this->cube->getDimensionAfter($name)) && isset($this->summaries->$next)) {
73 $htm .= ' <span class="sum">(' . $this->summaries->$next->services_cnt . ')</span>';
74 }
75
76 return $htm;
77 }
78
79 protected function getDimensionClasses($name, $row)
80 {
81 $classes = parent::getDimensionClasses($name, $row);
82
83 $sums = $row;
84 if ($sums->services_critical > 0) {
85 $classes[] = 'critical';
86 if ((int) $sums->services_unhandled_critical === 0) {
87 $classes[] = 'handled';
88 }
89 } elseif ($sums->services_warning > 0) {
90 $classes[] = 'warning';
91 if ((int) $sums->services_unhandled_warning === 0) {
92 $classes[] = 'handled';
93 }
94 } elseif ($sums->services_unknown > 0) {
95 $classes[] = 'unknown';
96 if ((int) $sums->services_unhandled_unknown === 0) {
97 $classes[] = 'handled';
98 }
99 } else {
100 $classes[] = 'ok';
101 }
102
103 return $classes;
104 }
105
106 protected function makeBadgeHtml($class, $count)
107 {
108 $indent = str_repeat(' ', 3);
109
110 return sprintf(
111 '%s<span class="%s">%s</span>',
112 $indent,
113 $class,
114 $count
115 ) . "\n";
116 }
117
118 protected function getDetailsBaseUrl()
119 {
120 return 'cube/services/details';
121 }
122 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2021 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Ido\Query;
4
5 use Exception;
6 use Icinga\Application\Version;
7 use Icinga\Data\Filter\FilterExpression;
8 use Icinga\Exception\NotImplementedError;
9 use Icinga\Module\Monitoring\Backend\Ido\Query\IdoQuery;
10
11 class HoststatusQuery extends \Icinga\Module\Monitoring\Backend\Ido\Query\HoststatusQuery
12 {
13 protected $subQueryTargets = array(
14 'hostgroups' => 'hostgroup',
15 'servicegroups' => 'servicegroup',
16 'services' => 'servicestatus'
17 );
18
19 protected function joinSubQuery(IdoQuery $query, $name, $filter, $and, $negate, &$additionalFilter)
20 {
21 if ($name === 'servicestatus') {
22 return ['s.host_object_id', 'ho.object_id'];
23 }
24
25 return parent::joinSubQuery($query, $name, $filter, $and, $negate, $additionalFilter);
26 }
27
28 protected function createSubQueryFilter(FilterExpression $filter, $queryName)
29 {
30 try {
31 return parent::createSubQueryFilter($filter, $queryName);
32 } catch (Exception $e) {
33 if (version_compare(Version::VERSION, '2.10.0', '>=')) {
34 throw $e;
35 }
36
37 if ($e->getMessage() === 'Undefined array key 0' && basename($e->getFile()) === 'IdoQuery.php') {
38 // Ensures compatibility with earlier Icinga Web 2 versions
39 throw new NotImplementedError('');
40 } else {
41 throw $e;
42 }
43 }
44 }
45 }
0 <?php
1
2 namespace Icinga\Module\Cube\Ido;
3
4 /**
5 * Since version 1.1.0 we're using the monitoring module's queries as the cubes' base queries.
6 * Before, the host object table was available using the alias 'o'. Now it's 'ho'.
7 * Without this wrapper, the action link hook provided by the director would fail because it relies on the alias 'o'.
8 */
9 class ZfSelectWrapper
10 {
11 /** @var \Zend_Db_Select */
12 protected $select;
13
14 public function __construct(\Zend_Db_Select $select)
15 {
16 $this->select = $select;
17 }
18
19 /**
20 * Get the underlying Zend_Db_Select query
21 *
22 * @return \Zend_Db_Select
23 */
24 public function unwrap()
25 {
26 return $this->select;
27 }
28
29 /**
30 * {@see \Zend_Db_Select::reset()}
31 */
32 public function reset($part = null)
33 {
34 $this->select->reset($part);
35
36 return $this;
37 }
38
39 /**
40 * {@see \Zend_Db_Select::columns()}
41 */
42 public function columns($cols = '*', $correlationName = null)
43 {
44 if (is_array($cols)) {
45 foreach ($cols as $alias => &$col) {
46 if (substr($col, 0, 2) === 'o.') {
47 $col = 'ho.' . substr($col, 2);
48 }
49 }
50 }
51
52 return $this->select->columns($cols, $correlationName);
53 }
54
55 /**
56 * Proxy Zend_Db_Select method calls
57 *
58 * @param string $name The name of the method to call
59 * @param array $arguments Arguments for the method to call
60 *
61 * @return mixed
62 *
63 * @throws \BadMethodCallException If the called method does not exist
64 */
65 public function __call($name, array $arguments)
66 {
67 if (! method_exists($this->select, $name)) {
68 $class = get_class($this);
69 $message = "Call to undefined method $class::$name";
70
71 throw new \BadMethodCallException($message);
72 }
73
74 return call_user_func_array([$this->select, $name], $arguments);
75 }
76 }
0 <?php
1 namespace Icinga\Module\Cube\ProvidedHook\Cube;
2
3 use Icinga\Module\Cube\Hook\IcingaDbActionsHook;
4 use Icinga\Module\Cube\IcingaDb\IcingaDbCube;
5 use Icinga\Module\Cube\Icingadb\IcingadbServiceStatusCube;
6
7 class IcingaDbActions extends IcingaDbActionsHook
8 {
9 public function createActionLinks(IcingaDbCube $cube)
10 {
11 $type = 'host';
12 if ($cube instanceof IcingadbServiceStatusCube) {
13 $type = 'service';
14 }
15
16 $url = 'icingadb/'. $type . 's';
17
18 $paramsWithPrefix = [];
19 foreach ($cube->getSlices() as $dimension => $slice) {
20 $paramsWithPrefix[$type . '.vars.' . $dimension] = $slice;
21 }
22
23 if ($type === 'host') {
24 $this->addActionLink(
25 $this->makeUrl($url, $paramsWithPrefix),
26 t('Show hosts status'),
27 t('This shows all matching hosts and their current state in Icinga DB Web'),
28 'server'
29 );
30 } else {
31 $this->addActionLink(
32 $this->makeUrl($url, $paramsWithPrefix),
33 t('Show services status'),
34 t('This shows all matching hosts and their current state in Icinga DB Web'),
35 'cog'
36 );
37 }
38 }
39 }
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2022 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\ProvidedHook\Icingadb;
4
5 use Icinga\Module\Icingadb\Hook\IcingadbSupportHook;
6
7 class IcingadbSupport extends IcingadbSupportHook
8 {
9
10 }
22
33 namespace Icinga\Module\Cube\Web;
44
5 use Icinga\Module\Cube\Web\Form\FormLoader;
6 use Icinga\Module\Cube\Web\Form\QuickForm;
5 use Icinga\Application\Modules\Module;
6 use Icinga\Module\Cube\DimensionParams;
7 use Icinga\Module\Cube\Forms\DimensionsForm;
8 use Icinga\Module\Cube\Hook\IcingaDbActionsHook;
9 use Icinga\Module\Cube\ProvidedHook\Icingadb\IcingadbSupport;
710 use Icinga\Web\Controller as WebController;
811 use Icinga\Web\View;
912
3538 ])->activate('details');
3639
3740 $this->cube->chooseFacts(array_keys($this->cube->getAvailableFactColumns()));
38 $dimensions = $this->params->shift('dimensions');
41 $vars = DimensionParams::fromString($this->params->shift('dimensions', ''))->getDimensions();
3942 $wantNull = $this->params->shift('wantNull');
40 $vars = preg_split('/,/', $dimensions, -1, PREG_SPLIT_NO_EMPTY);
43
4144 foreach ($vars as $var) {
4245 $this->cube->addDimensionByName($var);
4346 if ($wantNull) {
5053 }
5154
5255 foreach ($this->params->toArray() as $param) {
53 $this->cube->slice($param[0], urldecode($param[1]));
56 $this->cube->slice(rawurldecode($param[0]), rawurldecode($param[1]));
5457 }
5558
5659 $this->view->title = $this->cube->getSlicesLabel();
57 $this->view->links = ActionLinks::renderAll($this->cube, $this->view);
60 if (Module::exists('icingadb') && IcingadbSupport::useIcingaDbAsBackend()) {
61 $this->view->links = IcingaDbActionsHook::renderAll($this->cube);
62 } else {
63 $this->view->links = ActionLinks::renderAll($this->cube, $this->view);
64 }
5865
5966 $this->render('cube-details', null, true);
6067 }
6572 $showSettings = $this->params->shift('showSettings');
6673
6774 $this->cube->chooseFacts(array_keys($this->cube->getAvailableFactColumns()));
68 $dimensions = $this->params->shift('dimensions');
75 $vars = DimensionParams::fromString($this->params->shift('dimensions', ''))->getDimensions();
6976 $wantNull = $this->params->shift('wantNull');
70 $vars = preg_split('/,/', $dimensions, -1, PREG_SPLIT_NO_EMPTY);
77
7178 foreach ($vars as $var) {
7279 $this->cube->addDimensionByName($var);
7380 if ($wantNull) {
8087 }
8188
8289 foreach ($this->params->toArray() as $param) {
83 $this->cube->slice($param[0], urldecode($param[1]));
90 $this->cube->slice(rawurldecode($param[0]), rawurldecode($param[1]));
8491 }
8592
8693 $this->view->title = sprintf(
95102 }
96103
97104 if ($showSettings) {
98 $this->view->form = $this->loadForm('Dimensions')
99 ->setCube($this->cube)
100 ->handleRequest();
105 $this->view->form = (new DimensionsForm())->setCube($this->cube);
106 $this->view->form->handleRequest();
101107 } else {
102108 $this->setAutorefreshInterval(15);
103109 }
104110
105111 $this->render('cube-index', null, true);
106112 }
107
108 /**
109 * Load a form with a specific name
110 *
111 * @param $name
112 * @return QuickForm
113 */
114 public function loadForm($name)
115 {
116 return FormLoader::load($name, $this->Module());
117 }
118113 }
+0
-54
library/Cube/Web/Form/CsrfToken.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Web\Form;
4
5 class CsrfToken
6 {
7 /**
8 * Check whether the given token is valid
9 *
10 * @param string $token Token
11 *
12 * @return bool
13 */
14 public static function isValid($token)
15 {
16 if (strpos($token, '|') === false) {
17 return false;
18 }
19
20 list($seed, $token) = explode('|', $elementValue);
21
22 if (!is_numeric($seed)) {
23 return false;
24 }
25
26 return $token === hash('sha256', self::getSessionId() . $seed);
27 }
28
29 /**
30 * Create a new token
31 *
32 * @return string
33 */
34 public static function generate()
35 {
36 $seed = mt_rand();
37 $token = hash('sha256', self::getSessionId() . $seed);
38
39 return sprintf('%s|%s', $seed, $token);
40 }
41
42 /**
43 * Get current session id
44 *
45 * TODO: we should do this through our App or Session object
46 *
47 * @return string
48 */
49 protected static function getSessionId()
50 {
51 return session_id();
52 }
53 }
+0
-10
library/Cube/Web/Form/Element/FormElement.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Web\Form\Element;
4
5 use Zend_Form_Element_Xhtml;
6
7 class FormElement extends Zend_Form_Element_Xhtml
8 {
9 }
+0
-23
library/Cube/Web/Form/Element/SimpleNote.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Web\Form\Element;
4
5 class SimpleNote extends FormElement
6 {
7 public $helper = 'formSimpleNote';
8
9 /**
10 * Always ignore this element
11 * @codingStandardsIgnoreStart
12 *
13 * @var boolean
14 */
15 protected $_ignore = true;
16 // @codingStandardsIgnoreEnd
17
18 public function isValid($value, $context = null)
19 {
20 return true;
21 }
22 }
+0
-38
library/Cube/Web/Form/FormLoader.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Web\Form;
4
5 use Icinga\Application\Icinga;
6 use Icinga\Application\Modules\Module;
7 use Icinga\Exception\ProgrammingError;
8
9 class FormLoader
10 {
11 public static function load($name, Module $module = null)
12 {
13 if ($module === null) {
14 $basedir = Icinga::app()->getApplicationDir('forms');
15 $ns = '\\Icinga\\Web\\Forms\\';
16 } else {
17 $basedir = $module->getFormDir();
18 $ns = '\\Icinga\\Module\\' . ucfirst($module->getName()) . '\\Forms\\';
19 }
20 if (preg_match('~^[a-z0-9/]+$~i', $name)) {
21 $parts = preg_split('~/~', $name);
22 $class = ucfirst(array_pop($parts)) . 'Form';
23 $file = sprintf('%s/%s/%s.php', rtrim($basedir, '/'), implode('/', $parts), $class);
24 if (file_exists($file)) {
25 require_once($file);
26 $class = $ns . $class;
27 $options = array();
28 if ($module !== null) {
29 $options['icingaModule'] = $module;
30 }
31
32 return new $class($options);
33 }
34 }
35 throw new ProgrammingError(sprintf('Cannot load %s (%s), no such form', $name, $file));
36 }
37 }
+0
-155
library/Cube/Web/Form/QuickBaseForm.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Web\Form;
4
5 use Icinga\Application\Modules\Module;
6 use Zend_Form;
7
8 abstract class QuickBaseForm extends Zend_Form
9 {
10 /**
11 * The Icinga module this form belongs to. Usually only set if the
12 * form is initialized through the FormLoader
13 *
14 * @var Module
15 */
16 protected $icingaModule;
17
18 protected $icingaModuleName;
19
20 private $hintCount = 0;
21
22 public function __construct($options = null)
23 {
24 $this->callZfConstructor($this->handleOptions($options))
25 ->initializePrefixPaths();
26 }
27
28 protected function callZfConstructor($options = null)
29 {
30 parent::__construct($options);
31 return $this;
32 }
33
34 protected function initializePrefixPaths()
35 {
36 if ($this->icingaModule) {
37 $this->addPrefixPathsForModule($this->icingaModule);
38 }
39 }
40
41 public function addPrefixPathsForModule(Module $module)
42 {
43 $basedir = sprintf(
44 '%s/%s/Web/Form',
45 $module->getLibDir(),
46 ucfirst($module->getName())
47 );
48
49 $this->addPrefixPaths(array(
50 array(
51 'prefix' => __NAMESPACE__ . '\\Element\\',
52 'path' => $basedir . '/Element',
53 'type' => static::ELEMENT
54 )
55 ));
56
57 return $this;
58 }
59
60 public function addHidden($name, $value = null)
61 {
62 $this->addElement('hidden', $name);
63 $el = $this->getElement($name);
64 $el->setDecorators(array('ViewHelper'));
65 if ($value !== null) {
66 $this->setDefault($name, $value);
67 $el->setValue($value);
68 }
69
70 return $this;
71 }
72
73 // TODO: Should be an element
74 public function addHtmlHint($html, $options = array())
75 {
76 return $this->addHtml('<div class="hint">' . $html . '</div>', $options);
77 }
78
79 public function addHtml($html, $options = array())
80 {
81 if (array_key_exists('name', $options)) {
82 $name = $options['name'];
83 unset($options['name']);
84 } else {
85 $name = '_HINT' . ++$this->hintCount;
86 }
87
88 $this->addElement('simpleNote', $name, $options);
89 $this->getElement($name)
90 ->setValue($html)
91 ->setIgnore(true)
92 ->setDecorators(array('ViewHelper'));
93
94 return $this;
95 }
96
97 public function optionalEnum($enum, $nullLabel = null)
98 {
99 if ($nullLabel === null) {
100 $nullLabel = $this->translate('- please choose -');
101 }
102
103 return array(null => $nullLabel) + $enum;
104 }
105
106 protected function handleOptions($options = null)
107 {
108 if ($options === null) {
109 return $options;
110 }
111
112 if (array_key_exists('icingaModule', $options)) {
113 /** @var Module icingaModule */
114 $this->icingaModule = $options['icingaModule'];
115 $this->icingaModuleName = $this->icingaModule->getName();
116 unset($options['icingaModule']);
117 }
118
119 return $options;
120 }
121
122 public function setIcingaModule(Module $module)
123 {
124 $this->icingaModule = $module;
125 return $this;
126 }
127
128 protected function loadForm($name, Module $module = null)
129 {
130 if ($module === null) {
131 $module = $this->icingaModule;
132 }
133
134 return FormLoader::load($name, $module);
135 }
136
137 protected function valueIsEmpty($value)
138 {
139 if (is_array($value)) {
140 return empty($value);
141 }
142
143 return strlen($value) === 0;
144 }
145
146 public function translate($string)
147 {
148 if ($this->icingaModuleName === null) {
149 return t($string);
150 } else {
151 return mt($this->icingaModuleName, $string);
152 }
153 }
154 }
+0
-500
library/Cube/Web/Form/QuickForm.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Web\Form;
4
5 use Icinga\Application\Icinga;
6 use Icinga\Exception\ProgrammingError;
7 use Icinga\Web\Notification;
8 use Icinga\Web\Request;
9 use Icinga\Web\Response;
10 use Icinga\Web\Url;
11 use Exception;
12
13 /**
14 * QuickForm wants to be a base class for simple forms
15 */
16 abstract class QuickForm extends QuickBaseForm
17 {
18 const ID = '__FORM_NAME';
19
20 const CSRF = '__FORM_CSRF';
21
22 /**
23 * The name of this form
24 */
25 protected $formName;
26
27 /**
28 * Whether the form has been sent
29 */
30 protected $hasBeenSent;
31
32 /**
33 * Whether the form has been sent
34 */
35 protected $hasBeenSubmitted;
36
37 /**
38 * The submit caption, element - still tbd
39 */
40 // protected $submit;
41
42 /**
43 * Our request
44 */
45 protected $request;
46
47 protected $successUrl;
48
49 protected $successMessage;
50
51 protected $submitLabel;
52
53 protected $submitButtonName;
54
55 protected $deleteButtonName;
56
57 protected $fakeSubmitButtonName;
58
59 /**
60 * Whether form elements have already been created
61 */
62 protected $didSetup = false;
63
64 protected $isApiRequest = false;
65
66 public function __construct($options = null)
67 {
68 parent::__construct($options);
69
70 $this->setMethod('post');
71 $this->getActionFromRequest()
72 ->createIdElement()
73 ->regenerateCsrfToken()
74 ->setPreferredDecorators();
75 }
76
77 protected function getActionFromRequest()
78 {
79 $this->setAction(Url::fromRequest());
80 return $this;
81 }
82
83 protected function setPreferredDecorators()
84 {
85 $this->setAttrib('class', 'autofocus');
86 $this->setDecorators(
87 array(
88 'Description',
89 array('FormErrors', array('onlyCustomFormErrors' => true)),
90 'FormElements',
91 'Form'
92 )
93 );
94
95 return $this;
96 }
97
98 protected function addSubmitButtonIfSet()
99 {
100 if (false === ($label = $this->getSubmitLabel())) {
101 return;
102 }
103
104 if ($this->submitButtonName && $el = $this->getElement($this->submitButtonName)) {
105 return;
106 }
107
108 $el = $this->createElement('submit', $label)
109 ->setLabel($label)
110 ->setDecorators(array('ViewHelper'));
111 $this->submitButtonName = $el->getName();
112 $this->addElement($el);
113
114 $fakeEl = $this->createElement('submit', '_FAKE_SUBMIT')
115 ->setLabel($label)
116 ->setDecorators(array('ViewHelper'));
117 $this->fakeSubmitButtonName = $fakeEl->getName();
118 $this->addElement($fakeEl);
119
120 $this->addDisplayGroup(
121 array($this->fakeSubmitButtonName),
122 'fake_button',
123 array(
124 'decorators' => array('FormElements'),
125 'order' => 1,
126 )
127 );
128
129 $grp = array(
130 $this->submitButtonName,
131 $this->deleteButtonName
132 );
133 $this->addDisplayGroup($grp, 'buttons', array(
134 'decorators' => array(
135 'FormElements',
136 array('HtmlTag', array('tag' => 'dl')),
137 'DtDdWrapper',
138 ),
139 'order' => 1000,
140 ));
141 }
142
143 protected function addSimpleDisplayGroup($elements, $name, $options)
144 {
145 if (! array_key_exists('decorators', $options)) {
146 $options['decorators'] = array(
147 'FormElements',
148 array('HtmlTag', array('tag' => 'dl')),
149 'Fieldset',
150 );
151 }
152
153 return $this->addDisplayGroup($elements, $name, $options);
154 }
155
156 protected function createIdElement()
157 {
158 $this->detectName();
159 $this->addHidden(self::ID, $this->getName());
160 $this->getElement(self::ID)->setIgnore(true);
161 return $this;
162 }
163
164 public function getSentValue($name, $default = null)
165 {
166 $request = $this->getRequest();
167 if ($request->isPost() && $this->hasBeenSent()) {
168 return $request->getPost($name);
169 } else {
170 return $default;
171 }
172 }
173
174 public function getSubmitLabel()
175 {
176 if ($this->submitLabel === null) {
177 return $this->translate('Submit');
178 }
179
180 return $this->submitLabel;
181 }
182
183 public function setSubmitLabel($label)
184 {
185 $this->submitLabel = $label;
186 return $this;
187 }
188
189 public function setApiRequest($isApiRequest = true)
190 {
191 $this->isApiRequest = $isApiRequest;
192 return $this;
193 }
194
195 public function isApiRequest()
196 {
197 return $this->isApiRequest;
198 }
199
200 public function regenerateCsrfToken()
201 {
202 if (! $element = $this->getElement(self::CSRF)) {
203 $this->addHidden(self::CSRF, CsrfToken::generate());
204 $element = $this->getElement(self::CSRF);
205 }
206 $element->setIgnore(true);
207
208 return $this;
209 }
210
211 public function removeCsrfToken()
212 {
213 $this->removeElement(self::CSRF);
214 return $this;
215 }
216
217 public function setSuccessUrl($url, $params = null)
218 {
219 if (! $url instanceof Url) {
220 $url = Url::fromPath($url);
221 }
222 if ($params !== null) {
223 $url->setParams($params);
224 }
225 $this->successUrl = $url;
226 return $this;
227 }
228
229 public function getSuccessUrl()
230 {
231 $url = $this->successUrl ?: $this->getAction();
232 if (! $url instanceof Url) {
233 $url = Url::fromPath($url);
234 }
235
236 return $url;
237 }
238
239 protected function beforeSetup()
240 {
241 }
242
243 public function setup()
244 {
245 }
246
247 protected function onSetup()
248 {
249 }
250
251 public function setAction($action)
252 {
253 if ($action instanceof Url) {
254 $action = $action->getAbsoluteUrl('&');
255 }
256
257 return parent::setAction($action);
258 }
259
260 public function hasBeenSubmitted()
261 {
262 if ($this->hasBeenSubmitted === null) {
263 $req = $this->getRequest();
264 if ($req->isPost()) {
265 if (! $this->hasSubmitButton()) {
266 return $this->hasBeenSubmitted = $this->hasBeenSent();
267 }
268
269 $this->hasBeenSubmitted = $this->pressedButton(
270 $this->fakeSubmitButtonName,
271 $this->getSubmitLabel()
272 ) || $this->pressedButton(
273 $this->submitButtonName,
274 $this->getSubmitLabel()
275 );
276 } else {
277 $this->hasBeenSubmitted = false;
278 }
279 }
280
281 return $this->hasBeenSubmitted;
282 }
283
284 protected function hasSubmitButton()
285 {
286 return $this->submitButtonName !== null;
287 }
288
289 protected function pressedButton($name, $label)
290 {
291 $req = $this->getRequest();
292 if (! $req->isPost()) {
293 return false;
294 }
295
296 $req = $this->getRequest();
297 $post = $req->getPost();
298
299 return array_key_exists($name, $post)
300 && $post[$name] === $label;
301 }
302
303 protected function beforeValidation($data = array())
304 {
305 }
306
307 public function prepareElements()
308 {
309 if (! $this->didSetup) {
310 $this->beforeSetup();
311 $this->setup();
312 $this->addSubmitButtonIfSet();
313 $this->onSetup();
314 $this->didSetup = true;
315 }
316
317 return $this;
318 }
319
320 public function handleRequest(Request $request = null)
321 {
322 if ($request === null) {
323 $request = $this->getRequest();
324 } else {
325 $this->setRequest($request);
326 }
327
328 $this->prepareElements();
329
330 if ($this->hasBeenSent()) {
331 $post = $request->getPost();
332 if ($this->hasBeenSubmitted()) {
333 $this->beforeValidation($post);
334 if ($this->isValid($post)) {
335 try {
336 $this->onSuccess();
337 } catch (Exception $e) {
338 $this->addException($e);
339 $this->onFailure();
340 }
341 } else {
342 $this->onFailure();
343 }
344 } else {
345 $this->setDefaults($post);
346 }
347 } else {
348 // Well...
349 }
350
351 return $this;
352 }
353
354 public function addException(Exception $e, $elementName = null)
355 {
356 $file = preg_split('/[\/\\\]/', $e->getFile(), -1, PREG_SPLIT_NO_EMPTY);
357 $file = array_pop($file);
358 $msg = sprintf(
359 '%s (%s:%d)',
360 $e->getMessage(),
361 $file,
362 $e->getLine()
363 );
364
365 if ($el = $this->getElement($elementName)) {
366 $el->addError($msg);
367 } else {
368 $this->addError($msg);
369 }
370 }
371
372 public function onSuccess()
373 {
374 $this->redirectOnSuccess();
375 }
376
377 public function setSuccessMessage($message)
378 {
379 $this->successMessage = $message;
380 return $this;
381 }
382
383 public function getSuccessMessage($message = null)
384 {
385 if ($message !== null) {
386 return $message;
387 }
388 if ($this->successMessage === null) {
389 return t('Form has successfully been sent');
390 }
391 return $this->successMessage;
392 }
393
394 public function redirectOnSuccess($message = null)
395 {
396 if ($this->isApiRequest()) {
397 // TODO: Set the status line message?
398 $this->successMessage = $this->getSuccessMessage($message);
399 return;
400 }
401
402 $url = $this->getSuccessUrl();
403 $this->notifySuccess($this->getSuccessMessage($message));
404 $this->redirectAndExit($url);
405 }
406
407 public function onFailure()
408 {
409 }
410
411 public function notifySuccess($message = null)
412 {
413 if ($message === null) {
414 $message = t('Form has successfully been sent');
415 }
416 Notification::success($message);
417 return $this;
418 }
419
420 public function notifyError($message)
421 {
422 Notification::error($message);
423 return $this;
424 }
425
426 protected function redirectAndExit($url)
427 {
428 /** @var Response $response */
429 $response = Icinga::app()->getFrontController()->getResponse();
430 $response->redirectAndExit($url);
431 }
432
433 protected function setHttpResponseCode($code)
434 {
435 Icinga::app()->getFrontController()->getResponse()->setHttpResponseCode($code);
436 return $this;
437 }
438
439 protected function onRequest()
440 {
441 }
442
443 public function setRequest(Request $request)
444 {
445 if ($this->request !== null) {
446 throw new ProgrammingError('Unable to set request twice');
447 }
448
449 $this->request = $request;
450 $this->prepareElements();
451 $this->onRequest();
452 return $this;
453 }
454
455 /**
456 * @return Request
457 */
458 public function getRequest()
459 {
460 if ($this->request === null) {
461 /** @var Request $request */
462 $request = Icinga::app()->getFrontController()->getRequest();
463 $this->setRequest($request);
464 }
465 return $this->request;
466 }
467
468 public function hasBeenSent()
469 {
470 if ($this->hasBeenSent === null) {
471
472 /** @var Request $req */
473 if ($this->request === null) {
474 $req = Icinga::app()->getFrontController()->getRequest();
475 } else {
476 $req = $this->request;
477 }
478
479 if ($req->isPost()) {
480 $post = $req->getPost();
481 $this->hasBeenSent = array_key_exists(self::ID, $post) &&
482 $post[self::ID] === $this->getName();
483 } else {
484 $this->hasBeenSent = false;
485 }
486 }
487
488 return $this->hasBeenSent;
489 }
490
491 protected function detectName()
492 {
493 if ($this->formName !== null) {
494 $this->setName($this->formName);
495 } else {
496 $this->setName(get_class($this));
497 }
498 }
499 }
+0
-88
library/Cube/Web/IconHelper.php less more
0 <?php
1 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
2
3 namespace Icinga\Module\Cube\Web;
4
5 use Icinga\Exception\ProgrammingError;
6
7 /**
8 * Icon helper class - stolen from the Director
9 *
10 * Should help to reduce redundant icon-lookup code. Currently with hardcoded
11 * icons only, could easily provide support for all of them as follows:
12 *
13 * $confFile = Icinga::app()
14 * ->getApplicationDir('fonts/fontello-ifont/config.json');
15 *
16 * $font = json_decode(file_get_contents($confFile));
17 * // 'icon-' is to be found in $font->css_prefix_text
18 * foreach ($font->glyphs as $icon) {
19 * // $icon->css (= 'help') -> 0x . dechex($icon->code)
20 * }
21 */
22 class IconHelper
23 {
24 private $icons = array(
25 'minus' => 'e806',
26 'trash' => 'e846',
27 'plus' => 'e805',
28 'cancel' => 'e804',
29 'help' => 'e85b',
30 'angle-double-left' => 'e87a',
31 'angle-double-right' => 'e87b',
32 );
33
34 private $mappedUtf8Icons;
35
36 private $reversedUtf8Icons;
37
38 private static $instance;
39
40 public function __construct()
41 {
42 $this->prepareIconMappings();
43 }
44
45 public static function instance()
46 {
47 if (self::$instance === null) {
48 self::$instance = new static;
49 }
50
51 return self::$instance;
52 }
53
54 public function characterIconName($character)
55 {
56 if (array_key_exists($character, $this->reversedUtf8Icons)) {
57 return $this->reversedUtf8Icons[$character];
58 } else {
59 throw new ProgrammingError('There is no mapping for the given character');
60 }
61 }
62
63 protected function hexToCharacter($hex)
64 {
65 return json_decode('"\u' . $hex . '"');
66 }
67
68 public function iconCharacter($name)
69 {
70 if (array_key_exists($name, $this->mappedUtf8Icons)) {
71 return $this->mappedUtf8Icons[$name];
72 } else {
73 return $this->mappedUtf8Icons['help'];
74 }
75 }
76
77 protected function prepareIconMappings()
78 {
79 $this->mappedUtf8Icons = array();
80 $this->reversedUtf8Icons = array();
81 foreach ($this->icons as $name => $hex) {
82 $character = $this->hexToCharacter($hex);
83 $this->mappedUtf8Icons[$name] = $character;
84 $this->reversedUtf8Icons[$character] = $name;
85 }
86 }
87 }
+0
-77
library/Cube/ZfSelectWrapper.php less more
0 <?php
1
2 namespace Icinga\Module\Cube;
3
4 /**
5 * Since version 1.1.0 we're using the monitoring module's queries as the cubes' base queries.
6 * Before, the host object table was available using the alias 'o'. Now it's 'ho'.
7 * Without this wrapper, the action link hook provided by the director would fail because it relies on the alias 'o'.
8 */
9 class ZfSelectWrapper
10 {
11 /** @var \Zend_Db_Select */
12 protected $select;
13
14 public function __construct(\Zend_Db_Select $select)
15 {
16 $this->select = $select;
17 }
18
19 /**
20 * Get the underlying Zend_Db_Select query
21 *
22 * @return \Zend_Db_Select
23 */
24 public function unwrap()
25 {
26 return $this->select;
27 }
28
29 /**
30 * {@see \Zend_Db_Select::reset()}
31 */
32 public function reset($part = null)
33 {
34 $this->select->reset($part);
35
36 return $this;
37 }
38
39 /**
40 * {@see \Zend_Db_Select::columns()}
41 */
42 public function columns($cols = '*', $correlationName = null)
43 {
44 if (is_array($cols)) {
45 foreach ($cols as $alias => &$col) {
46 if (substr($col, 0, 2) === 'o.') {
47 $col = 'ho.' . substr($col, 2);
48 }
49 }
50 }
51
52 return $this->select->columns($cols, $correlationName);
53 }
54
55 /**
56 * Proxy Zend_Db_Select method calls
57 *
58 * @param string $name The name of the method to call
59 * @param array $arguments Arguments for the method to call
60 *
61 * @return mixed
62 *
63 * @throws \BadMethodCallException If the called method does not exist
64 */
65 public function __call($name, array $arguments)
66 {
67 if (! method_exists($this->select, $name)) {
68 $class = get_class($this);
69 $message = "Call to undefined method $class::$name";
70
71 throw new \BadMethodCallException($message);
72 }
73
74 return call_user_func_array([$this->select, $name], $arguments);
75 }
76 }
00 Name: Cube
1 Version: 1.1.0
2 Depends: monitoring (>= 2.3.4), director (>= 1.2.0)
1 Version: 1.2.1
2 Requires:
3 Libraries: icinga-php-library (>=0.9.0)
4 Modules: monitoring (>= 2.9.0), icingadb (>= 1.0.0)
35 Description: Cube for Icinga Web 2
46 The Cube allows you to analyze data across multiple dimensions in your
57 Icinga Web frontend. Currently it shows host statistics (total count,
1111 div.cube-dimension0 > .header {
1212 font-size: 1.2em;
1313 font-weight: bold;
14 border-bottom: 1px solid black;
14 border-bottom: 1px solid @text-color;
1515 }
1616
1717 div.cube-dimension1 {
2828 padding: 0.2em;
2929 margin-top: 0.3em;
3030 overflow: hidden;
31 border: 1px solid white;
3231 }
3332
3433 div.cube-dimension1 > .header {
4948 > .header {
5049 display: flex;
5150 text-align: center;
52 color: white;
51 color: @text-color-on-icinga-blue;
5352 margin-bottom: 1em;
5453
5554 > a:first-child {
8584
8685 > .body {
8786 text-align: center;
88
89 span.sum {
90 font-size: 3em;
91 display: block;
92 text-align: center;
93 color: white;
94 }
9587 }
9688 }
9789
9991 display: inline;
10092 border-radius: 0.1em;
10193 padding: 0.05em 0.2em;
102 color: white;
94 color: @text-color-on-icinga-blue;
10395 font-size: 3em;
10496 }
10597
229221 fieldset.dimensions {
230222 margin: 0;
231223 padding: 0;
232 border: none;
233224
234225 span {
235226 min-width: 18em;
236227 display: inline-block;
237228 }
238 input[type=submit] {
229
230 button[type=submit] {
239231 border: none;
240232 color: @text-color;
241 font-family: 'ifont';
242 font-weight: bold;
243 }
244
245 input[type=submit][disabled] {
246 color: white;
247 background-color: white;
233 background: none;
234
235 padding-left: .75em;
236 padding-right: .75em;
237
238 i:before {
239 margin-right: 0;
240 }
241
242 &:not([disabled]):hover {
243 .rounded-corners();
244 background-color: @icinga-blue;
245 color: @text-color-on-icinga-blue;
246 }
247 }
248
249 button[type=submit][disabled] {
250 color: @disabled-gray;
251 background-color: @body-bg-color;
248252 cursor: auto;
249253 }
250254
251 input[type=submit]:nth-of-type(2) {
252 -moz-transform: rotate(90deg);
253 -ms-transform: rotate(90deg);
254 -o-transform: rotate(90deg);
255 -webkit-transform: rotate(90deg);
256 transform: rotate(90deg);
257 }
258
259 input[type=submit]:nth-of-type(3) {
260 -moz-transform: rotate(90deg);
261 -ms-transform: rotate(90deg);
262 -o-transform: rotate(90deg);
263 -webkit-transform: rotate(90deg);
264 transform: rotate(90deg);
255 &:first-of-type button[type=submit]:nth-of-type(2) {
256 margin-left: 2.75em;
257 }
258
259 &:not(:first-of-type) {
260 button[type=submit]:nth-of-type(2):last-of-type {
261 margin-right: 2.75em;
262 }
265263 }
266264
267265 &:last-of-type {
292290 }
293291
294292 li.error {
295 background-color: @colorCritical;
296 color: white;
293 background-color: @color-critical;
294 color: @text-color-on-icinga-blue;
297295 padding: 1em;
298296 font-weight: bold;
299297 text-align: center;
305303 padding: 0.5em 1em;
306304 display: block;
307305 text-decoration: none;
306 color: @gray;
308307
309308 .title {
310309 margin-left: 5em;
311310 font-weight: bold;
311 color: @link-color;
312312 }
313313
314314 p {
321321 display: inline-block;
322322 }
323323 &:hover {
324 background: @text-color;
325 color: white;
324 background: @tr-hover-color;
326325 text-decoration: none;
327326 }
328327 }
329328 }
330329
331 /** Form, stolen from Director **/
330 /** Form **/
332331
333332 .content form {
334333 margin-bottom: 2em;
335334 }
336335
337
338 form input[type=submit] {
339 .button();
340 border-width: 1px;
336 form button[type=submit] {
341337 margin-top: 0.5em;
342
343 &:disabled {
344 border-color: @gray-light;
345 background-color: @gray-light;
346 color: #fff;
347 }
348 }
349
350 form input[type=submit]:first-of-type {
351 border-width: 2px;
352 }
353
354 form input[type=submit].link-button {
355 color: @icinga-blue;
356 background: none;
357 border: none;
358 padding: 0;
359
360 text-align: left;
361
362 &:hover {
363 text-decoration: underline;
364 }
365 }
366
367 form p.description {
368 padding: 1em 1em;
369 margin: 0;
370 font-style: italic;
338 }
339
340 select {
371341 width: 100%;
372342 }
373
374 input, select, select option, textarea {
375 -webkit-appearance: none;
376 -moz-appearance: none;
377 appearance: none;
378 }
379
380 form ul.form-errors {
381 margin-bottom: 0.5em;
382
383 ul.errors li {
384 background: @color-critical;
385 font-weight: bold;
386 padding: 0.5em 1em;
387 color: white;
388 }
389 }
390
391 select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */
392 display: none;
393 }
394
395 select {
396 border: 1px solid #ddd;
397 cursor: pointer;
398 background: none;
399 }
400
401 input[type=text], input[type=password], textarea, select {
402 max-width: 36em;
403 min-width: 20em;
404 width: 100%;
405 line-height: 2em;
406 height: 2.4em;
407 padding-left: 0.5em;
408 border-style: solid;
409 border-color: transparent;
410 border-bottom-color: @gray-lighter;
411 border-width: 1px 1px 1px 3px;
412 background-color: white;
413
414 &.search {
415 background: transparent url("../img/icons/search.png") no-repeat scroll 0.5em center / 1em 1em;
416 padding-left: 2em;
417 }
418 }
419
420 select[multiple] {
421 height: auto;
422 }
423
424 select option {
425 height: 2em;
426 padding-top: 0.3em;
427 }
428
429 select[multiple=multiple] {
430 height: auto;
431 }
432
433 label {
434 line-height: 2em;
435 }
436
437 form dl {
438 margin: 0;
439 padding: 0;
440 }
441
442
443 select::-moz-focus-inner { border: 0; }
444
445 select:-moz-focusring {
446 color: transparent;
447 text-shadow: 0 0 0 #000;
448 }
449
450 select, input[type=text], textarea {
451 &:hover {
452 border-style: dotted solid dotted solid;
453 border-color: @gray-light;
454 }
455
456 &:focus, &:focus:hover {
457 border-style: solid;
458 border-color: @icinga-blue;
459 outline: none;
460 }
461 }
462
463 select[value=""] {
464 color: blue;
465 border: 1px solid #666;
466 background-color: white;
467 }
468
469 select option {
470 color: inherit;
471 padding-left: 0.5em;
472 background-color: white;
473 }
474
475 select option[value=""] {
476 color: #aaa;
477 background-color: white;
478 }
11 // Icinga Web 2 Cube Module | (c) 2016 Icinga GmbH | GPLv2
22
33 $this->provideHook('cube/Actions', 'Cube/MonitoringActions');
4 $this->provideHook('cube/IcingaDbActions', 'Cube/IcingaDbActions');
5
6 $this->provideHook('icingadb/icingadbSupport');