1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19**/
20
21
22$table = (new CTableInfo())
23	->setHeader([
24		_('Host group'),
25		_('Without problems'),
26		_('With problems'),
27		_('Total')
28	]);
29
30// get host groups
31$groups = API::HostGroup()->get([
32	'output' => ['groupid', 'name'],
33	'groupids' => $data['filter']['groupids'],
34	'hostids' => isset($data['filter']['hostids']) ? $data['filter']['hostids'] : null,
35	'monitored_hosts' => true,
36	'preservekeys' => true
37]);
38CArrayHelper::sort($groups, ['name']);
39
40// get hosts
41$hosts = API::Host()->get([
42	'output' => ['hostid', 'name'],
43	'selectGroups' => ['groupid'],
44	'groupids' => array_keys($groups),
45	'hostids' => isset($data['filter']['hostids']) ? $data['filter']['hostids'] : null,
46	'filter' => ['maintenance_status' => $data['filter']['maintenance']],
47	'monitored_hosts' => true,
48	'preservekeys' => true
49]);
50CArrayHelper::sort($hosts, ['name']);
51
52// get triggers
53$triggers = API::Trigger()->get([
54	'output' => ['triggerid', 'priority'],
55	'selectHosts' => ['hostid'],
56	'search' => ($data['filter']['trigger_name'] !== '') ? ['description' => $data['filter']['trigger_name']] : null,
57	'filter' => [
58		'priority' => $data['filter']['severity'],
59		'value' => TRIGGER_VALUE_TRUE
60	],
61	'maintenance' => $data['filter']['maintenance'],
62	'monitored' => true
63]);
64
65if ($data['filter']['extAck']) {
66	$hosts_with_unack_triggers = [];
67
68	$triggers_unack = API::Trigger()->get([
69		'output' => ['triggerid'],
70		'selectHosts' => ['hostid'],
71		'search' => ($data['filter']['trigger_name'] !== '')
72			? ['description' => $data['filter']['trigger_name']]
73			: null,
74		'filter' => [
75			'priority' => $data['filter']['severity'],
76			'value' => TRIGGER_VALUE_TRUE
77		],
78		'withLastEventUnacknowledged' => true,
79		'maintenance' => $data['filter']['maintenance'],
80		'monitored' => true,
81		'preservekeys' => true
82	]);
83
84	foreach ($triggers_unack as $tunack) {
85		foreach ($tunack['hosts'] as $unack_host) {
86			$hosts_with_unack_triggers[$unack_host['hostid']] = $unack_host['hostid'];
87		}
88	}
89}
90
91$hosts_data = [];
92$problematic_host_list = [];
93$lastUnack_host_list = [];
94$highest_severity = [];
95$highest_severity2 = [];
96
97foreach ($triggers as $trigger) {
98	foreach ($trigger['hosts'] as $trigger_host) {
99		if (!isset($hosts[$trigger_host['hostid']])) {
100			continue;
101		}
102		else {
103			$host = $hosts[$trigger_host['hostid']];
104		}
105
106		if ($data['filter']['extAck'] && array_key_exists($host['hostid'], $hosts_with_unack_triggers)) {
107			if (!isset($lastUnack_host_list[$host['hostid']])) {
108				$lastUnack_host_list[$host['hostid']] = [];
109				$lastUnack_host_list[$host['hostid']]['host'] = $host['name'];
110				$lastUnack_host_list[$host['hostid']]['hostid'] = $host['hostid'];
111				$lastUnack_host_list[$host['hostid']]['severities'] = [];
112				$lastUnack_host_list[$host['hostid']]['severities'][TRIGGER_SEVERITY_DISASTER] = 0;
113				$lastUnack_host_list[$host['hostid']]['severities'][TRIGGER_SEVERITY_HIGH] = 0;
114				$lastUnack_host_list[$host['hostid']]['severities'][TRIGGER_SEVERITY_AVERAGE] = 0;
115				$lastUnack_host_list[$host['hostid']]['severities'][TRIGGER_SEVERITY_WARNING] = 0;
116				$lastUnack_host_list[$host['hostid']]['severities'][TRIGGER_SEVERITY_INFORMATION] = 0;
117				$lastUnack_host_list[$host['hostid']]['severities'][TRIGGER_SEVERITY_NOT_CLASSIFIED] = 0;
118			}
119			if (isset($triggers_unack[$trigger['triggerid']])) {
120				$lastUnack_host_list[$host['hostid']]['severities'][$trigger['priority']]++;
121			}
122
123			foreach ($host['groups'] as $gnum => $group) {
124				if (!isset($highest_severity2[$group['groupid']])) {
125					$highest_severity2[$group['groupid']] = 0;
126				}
127
128				if ($data['filter']['extAck'] == EXTACK_OPTION_UNACK) {
129					if ($trigger['priority'] > $highest_severity2[$group['groupid']]
130							&& array_key_exists($trigger['triggerid'], $triggers_unack)) {
131						$highest_severity2[$group['groupid']] = $trigger['priority'];
132					}
133				}
134				elseif ($trigger['priority'] > $highest_severity2[$group['groupid']]) {
135					$highest_severity2[$group['groupid']] = $trigger['priority'];
136				}
137
138				if (!isset($hosts_data[$group['groupid']])) {
139					$hosts_data[$group['groupid']] = [
140						'problematic' => 0,
141						'ok' => 0,
142						'lastUnack' => 0,
143						'hostids_all' => [],
144						'hostids_unack' => []
145					];
146				}
147
148				if (!isset($hosts_data[$group['groupid']]['hostids_unack'][$host['hostid']])) {
149					$hosts_data[$group['groupid']]['hostids_unack'][$host['hostid']] = $host['hostid'];
150					$hosts_data[$group['groupid']]['lastUnack']++;
151				}
152			}
153		}
154
155		if (!isset($problematic_host_list[$host['hostid']])) {
156			$problematic_host_list[$host['hostid']] = [];
157			$problematic_host_list[$host['hostid']]['host'] = $host['name'];
158			$problematic_host_list[$host['hostid']]['hostid'] = $host['hostid'];
159
160			$problematic_host_list[$host['hostid']]['severities'] = [];
161
162			for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
163				$problematic_host_list[$host['hostid']]['severities'][$severity] = 0;
164			}
165
166			krsort($problematic_host_list[$host['hostid']]['severities']);
167		}
168		$problematic_host_list[$host['hostid']]['severities'][$trigger['priority']]++;
169
170		foreach ($host['groups'] as $gnum => $group) {
171			if (!isset($highest_severity[$group['groupid']])) {
172				$highest_severity[$group['groupid']] = 0;
173			}
174
175			if ($trigger['priority'] > $highest_severity[$group['groupid']]) {
176				$highest_severity[$group['groupid']] = $trigger['priority'];
177			}
178
179			if (!isset($hosts_data[$group['groupid']])) {
180				$hosts_data[$group['groupid']] = [
181					'problematic' => 0,
182					'ok' => 0,
183					'lastUnack' => 0,
184					'hostids_all' => [],
185					'hostids_unack' => []
186				];
187			}
188
189			if (!isset($hosts_data[$group['groupid']]['hostids_all'][$host['hostid']])) {
190				$hosts_data[$group['groupid']]['hostids_all'][$host['hostid']] = $host['hostid'];
191
192				/*
193				 * Display acknowledged problem triggers in "Without problems" column when filter dashboard is
194				 * enabled and is set to display "Unacknowledged only". Host and trigger must not be in
195				 * unacknowledged lists. Count as problematic host otherwise.
196				 */
197				if ($data['filter']['extAck'] == EXTACK_OPTION_UNACK
198						&& !array_key_exists($host['hostid'], $hosts_with_unack_triggers)
199						&& !array_key_exists($trigger['triggerid'], $triggers_unack)) {
200					$hosts_data[$group['groupid']]['ok']++;
201				}
202				else {
203					$hosts_data[$group['groupid']]['problematic']++;
204				}
205			}
206		}
207	}
208}
209
210foreach ($hosts as $host) {
211	foreach ($host['groups'] as $group) {
212		if (!isset($groups[$group['groupid']])) {
213			continue;
214		}
215
216		if (!isset($groups[$group['groupid']]['hosts'])) {
217			$groups[$group['groupid']]['hosts'] = [];
218		}
219		$groups[$group['groupid']]['hosts'][$host['hostid']] = ['hostid' => $host['hostid']];
220
221		if (!isset($highest_severity[$group['groupid']])) {
222			$highest_severity[$group['groupid']] = 0;
223		}
224
225		if (!isset($hosts_data[$group['groupid']])) {
226			$hosts_data[$group['groupid']] = ['problematic' => 0, 'ok' => 0, 'lastUnack' => 0];
227		}
228
229		if (!isset($problematic_host_list[$host['hostid']])) {
230			$hosts_data[$group['groupid']]['ok']++;
231		}
232	}
233}
234
235foreach ($groups as $group) {
236	if (!isset($hosts_data[$group['groupid']])) {
237		continue;
238	}
239
240	$group_row = new CRow();
241
242	$name = new CLink($group['name'], 'tr_status.php?filter_set=1&groupid='.$group['groupid'].'&hostid=0');
243
244	$group_row->addItem($name);
245	$group_row->addItem((new CCol($hosts_data[$group['groupid']]['ok']))->addClass(ZBX_STYLE_NORMAL_BG));
246
247	if ($data['filter']['extAck']) {
248		if ($hosts_data[$group['groupid']]['lastUnack']) {
249			$table_inf = new CTableInfo();
250
251			// set trigger severities as table header starting from highest severity
252			$header = [];
253
254			for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
255				$header[] = ($data['filter']['severity'] === null || isset($data['filter']['severity'][$severity]))
256					? getSeverityName($severity, $data['config'])
257					: null;
258			}
259
260			krsort($header);
261			array_unshift($header, _('Host'));
262
263			$table_inf->setHeader($header);
264
265			$popup_rows = 0;
266
267			foreach ($group['hosts'] as $host) {
268				$hostid = $host['hostid'];
269				if (!isset($lastUnack_host_list[$hostid])) {
270					continue;
271				}
272
273				$host_data = $lastUnack_host_list[$hostid];
274
275				$r = new CRow();
276				$r->addItem(
277					(new CCol(
278						new CLink($host_data['host'],
279							'tr_status.php?filter_set=1&groupid='.$group['groupid'].'&hostid='.$hostid
280						)
281					))->addClass(ZBX_STYLE_NOWRAP)
282				);
283
284				foreach ($lastUnack_host_list[$host['hostid']]['severities'] as $severity => $trigger_count) {
285					if (!is_null($data['filter']['severity']) && !isset($data['filter']['severity'][$severity])) {
286						continue;
287					}
288					$r->addItem((new CCol($trigger_count))->addClass(getSeverityStyle($severity, $trigger_count)));
289				}
290				$table_inf->addRow($r);
291
292				if (++$popup_rows == ZBX_WIDGET_ROWS) {
293					break;
294				}
295			}
296			$lastUnack_count = (new CSpan($hosts_data[$group['groupid']]['lastUnack']))
297				->addClass(ZBX_STYLE_LINK_ACTION)
298				->setHint($table_inf);
299		}
300		else {
301			$lastUnack_count = 0;
302		}
303	}
304
305	// if hostgroup contains problematic hosts, hint should be built
306	if ($hosts_data[$group['groupid']]['problematic']) {
307		$table_inf = new CTableInfo();
308
309		// set trigger severities as table header starting from highest severity
310		$header = [];
311
312		for ($severity = TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity < TRIGGER_SEVERITY_COUNT; $severity++) {
313			$header[] = ($data['filter']['severity'] === null || isset($data['filter']['severity'][$severity]))
314				? getSeverityName($severity, $data['config'])
315				: null;
316		}
317
318		krsort($header);
319		array_unshift($header, _('Host'));
320
321		$table_inf->setHeader($header);
322
323		$popup_rows = 0;
324
325		foreach ($group['hosts'] as $host) {
326			$hostid = $host['hostid'];
327			if (!isset($problematic_host_list[$hostid])) {
328				continue;
329			}
330
331			$host_data = $problematic_host_list[$hostid];
332
333			$r = new CRow();
334			$r->addItem(new CLink($host_data['host'],
335				'tr_status.php?filter_set=1&groupid='.$group['groupid'].'&hostid='.$hostid
336			));
337
338			foreach ($problematic_host_list[$host['hostid']]['severities'] as $severity => $trigger_count) {
339				if (!is_null($data['filter']['severity']) && !isset($data['filter']['severity'][$severity])) {
340					continue;
341				}
342				$r->addItem((new CCol($trigger_count))->addClass(getSeverityStyle($severity, $trigger_count)));
343			}
344			$table_inf->addRow($r);
345
346			if (++$popup_rows == ZBX_WIDGET_ROWS) {
347				break;
348			}
349		}
350		$problematic_count = (new CSpan($hosts_data[$group['groupid']]['problematic']))
351			->addClass(ZBX_STYLE_LINK_ACTION)
352			->setHint($table_inf);
353	}
354	else {
355		$problematic_count = 0;
356	}
357
358	switch ($data['filter']['extAck']) {
359		case EXTACK_OPTION_ALL:
360			$group_row->addItem((new CCol($problematic_count))
361				->addClass(getSeverityStyle($highest_severity[$group['groupid']], $hosts_data[$group['groupid']]['problematic']))
362			);
363			$group_row->addItem($hosts_data[$group['groupid']]['problematic'] + $hosts_data[$group['groupid']]['ok']);
364			break;
365		case EXTACK_OPTION_UNACK:
366			$group_row->addItem((new CCol($lastUnack_count))
367				->addClass(getSeverityStyle(
368					isset($highest_severity2[$group['groupid']]) ? $highest_severity2[$group['groupid']] : 0,
369					$hosts_data[$group['groupid']]['lastUnack']
370				))
371			);
372			$group_row->addItem($hosts_data[$group['groupid']]['lastUnack'] + $hosts_data[$group['groupid']]['ok']);
373			break;
374		case EXTACK_OPTION_BOTH:
375			$unackspan = $lastUnack_count ? [$lastUnack_count, ' '._('of').' '] : null;
376			$group_row->addItem((new CCol([$unackspan, $problematic_count]))
377				->addClass(getSeverityStyle(
378					$highest_severity[$group['groupid']], $hosts_data[$group['groupid']]['problematic']
379				))
380			);
381			$group_row->addItem($hosts_data[$group['groupid']]['problematic'] + $hosts_data[$group['groupid']]['ok']);
382			break;
383	}
384
385	$table->addRow($group_row);
386}
387
388$output = [
389	'header' => _('Host status'),
390	'body' => (new CDiv([getMessages(), $table]))->toString(),
391	'footer' => (new CListItem(_s('Updated: %s', zbx_date2str(TIME_FORMAT_SECONDS))))->toString()
392];
393
394if ($data['user']['debug_mode'] == GROUP_DEBUG_MODE_ENABLED) {
395	CProfiler::getInstance()->stop();
396	$output['debug'] = CProfiler::getInstance()->make()->toString();
397}
398
399echo (new CJson())->encode($output);
400