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
22require_once dirname(__FILE__).'/include/config.inc.php';
23require_once dirname(__FILE__).'/include/hosts.inc.php';
24
25$page['title'] = _('Availability report');
26$page['file'] = 'report2.php';
27$page['scripts'] = ['class.calendar.js'];
28$page['type'] = detect_page_type(PAGE_TYPE_HTML);
29
30require_once dirname(__FILE__).'/include/page_header.php';
31
32// VAR	TYPE	OPTIONAL	FLAGS	VALIDATION	EXCEPTION
33$fields = [
34	'mode' =>				[T_ZBX_INT,	O_OPT,	P_SYS,			IN('0,1'),	null],
35	'hostgroupid' =>		[T_ZBX_INT,	O_OPT,	P_SYS,			DB_ID,		null],
36	'tpl_triggerid' =>		[T_ZBX_INT,	O_OPT,	P_SYS,			DB_ID,		null],
37	'triggerid' =>			[T_ZBX_INT,	O_OPT,	P_SYS|P_NZERO,	DB_ID,		null],
38	// filter
39	'filter_groupid'=>		[T_ZBX_INT,	O_OPT,	P_SYS,			DB_ID,		null],
40	'filter_hostid' =>		[T_ZBX_INT,	O_OPT,	P_SYS,			DB_ID,		null],
41	'filter_rst'=>			[T_ZBX_STR,	O_OPT,	P_SYS,			null,		null],
42	'filter_set' =>			[T_ZBX_STR,	O_OPT,	P_SYS,			null,		null],
43	'filter_timesince' =>	[T_ZBX_STR,	O_OPT,	P_UNSET_EMPTY,	null,		null],
44	'filter_timetill' =>	[T_ZBX_STR,	O_OPT,	P_UNSET_EMPTY,	null,		null]
45];
46check_fields($fields);
47
48$availabilityReportMode = getRequest('mode', CProfile::get('web.avail_report.mode', AVAILABILITY_REPORT_BY_HOST));
49CProfile::update('web.avail_report.mode', $availabilityReportMode, PROFILE_TYPE_INT);
50
51/*
52 * Permissions
53 */
54if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) {
55	if (getRequest('hostgroupid') && !API::HostGroup()->isReadable([$_REQUEST['hostgroupid']])
56			|| getRequest('filter_groupid') && !API::HostGroup()->isReadable([$_REQUEST['filter_groupid']])
57			|| getRequest('filter_hostid') && !API::Host()->isReadable([$_REQUEST['filter_hostid']])) {
58		access_deny();
59	}
60	if (getRequest('tpl_triggerid')) {
61		$trigger = API::Trigger()->get([
62			'triggerids' => $_REQUEST['tpl_triggerid'],
63			'output' => ['triggerid'],
64			'filter' => ['flags' => null]
65		]);
66		if (!$trigger) {
67			access_deny();
68		}
69	}
70}
71else {
72	if (getRequest('filter_groupid') && !API::HostGroup()->isReadable([$_REQUEST['filter_groupid']])
73			|| getRequest('filter_hostid') && !API::Host()->isReadable([$_REQUEST['filter_hostid']])) {
74		access_deny();
75	}
76}
77if (getRequest('triggerid') && !API::Trigger()->isReadable([$_REQUEST['triggerid']])) {
78	access_deny();
79}
80
81/*
82 * Filter
83 */
84if (hasRequest('filter_rst')) {
85	$_REQUEST['filter_groupid'] = 0;
86	$_REQUEST['filter_hostid'] = 0;
87	$_REQUEST['filter_timesince'] = 0;
88	$_REQUEST['filter_timetill'] = 0;
89
90	if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) {
91		$_REQUEST['tpl_triggerid'] = 0;
92		$_REQUEST['hostgroupid'] = 0;
93	}
94}
95
96if (!hasRequest('filter_rst')) {
97	$_REQUEST['filter_groupid'] = getRequest('filter_groupid',
98		CProfile::get('web.avail_report.'.$availabilityReportMode.'.groupid', 0)
99	);
100	$_REQUEST['filter_hostid'] = getRequest('filter_hostid',
101		CProfile::get('web.avail_report.'.$availabilityReportMode.'.hostid', 0)
102	);
103	$_REQUEST['filter_timesince'] = getRequest('filter_timesince',
104		CProfile::get('web.avail_report.'.$availabilityReportMode.'.timesince', 0)
105	);
106	$_REQUEST['filter_timetill'] = getRequest('filter_timetill',
107		CProfile::get('web.avail_report.'.$availabilityReportMode.'.timetill', 0)
108	);
109
110	if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) {
111		$_REQUEST['tpl_triggerid'] = getRequest('tpl_triggerid',
112			CProfile::get('web.avail_report.'.$availabilityReportMode.'.tpl_triggerid', 0)
113		);
114
115		$_REQUEST['hostgroupid'] = getRequest('hostgroupid',
116			CProfile::get('web.avail_report.'.$availabilityReportMode.'.hostgroupid', 0)
117		);
118	}
119}
120
121CProfile::update('web.avail_report.'.$availabilityReportMode.'.groupid', getRequest('filter_groupid', 0),
122	PROFILE_TYPE_ID
123);
124CProfile::update('web.avail_report.'.$availabilityReportMode.'.timesince', getRequest('filter_timesince', 0),
125	PROFILE_TYPE_STR
126);
127CProfile::update('web.avail_report.'.$availabilityReportMode.'.timetill', getRequest('filter_timetill', 0),
128	PROFILE_TYPE_STR
129);
130CProfile::update('web.avail_report.'.$availabilityReportMode.'.hostid', getRequest('filter_hostid', 0),
131	PROFILE_TYPE_ID
132);
133
134if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) {
135	CProfile::update('web.avail_report.'.$availabilityReportMode.'.tpl_triggerid', getRequest('tpl_triggerid', 0),
136		PROFILE_TYPE_ID
137	);
138
139	CProfile::update('web.avail_report.'.$availabilityReportMode.'.hostgroupid', getRequest('hostgroupid', 0),
140		PROFILE_TYPE_ID
141	);
142}
143
144$config = select_config();
145
146if ($_REQUEST['filter_timetill'] > 0 && $_REQUEST['filter_timesince'] > $_REQUEST['filter_timetill']) {
147	zbx_swap($_REQUEST['filter_timesince'], $_REQUEST['filter_timetill']);
148}
149
150$_REQUEST['filter_timesince'] = zbxDateToTime($_REQUEST['filter_timesince']
151	? $_REQUEST['filter_timesince'] : date(TIMESTAMP_FORMAT_ZERO_TIME, time() - SEC_PER_DAY));
152$_REQUEST['filter_timetill'] = zbxDateToTime($_REQUEST['filter_timetill']
153	? $_REQUEST['filter_timetill'] : date(TIMESTAMP_FORMAT_ZERO_TIME, time()));
154
155/*
156 * Header
157 */
158$triggerData = isset($_REQUEST['triggerid'])
159	? API::Trigger()->get([
160		'triggerids' => $_REQUEST['triggerid'],
161		'output' => API_OUTPUT_EXTEND,
162		'selectHosts' => API_OUTPUT_EXTEND,
163		'expandDescription' => true
164	])
165	: null;
166
167$reportWidget = (new CWidget())->setTitle(_('Availability report'));
168
169if ($triggerData) {
170	$triggerData = reset($triggerData);
171	$host = reset($triggerData['hosts']);
172
173	$triggerData['hostid'] = $host['hostid'];
174	$triggerData['hostname'] = $host['name'];
175
176	$reportWidget->setControls(
177		(new CList())
178			->addItem(new CLink($triggerData['hostname'], '?filter_groupid='.$_REQUEST['filter_groupid']))
179			->addItem($triggerData['description'])
180	);
181
182	$table = (new CTableInfo())
183		->addRow(new CImg('chart4.php?triggerid='.$_REQUEST['triggerid']));
184
185	$reportWidget->addItem(BR())
186		->addItem($table)
187		->show();
188}
189elseif (isset($_REQUEST['filter_hostid'])) {
190	$headerForm = (new CForm('get'))->addItem((new CList())
191		->addItem([
192			new CLabel(_('Mode'), 'mode'),
193			(new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
194			new CComboBox('mode', $availabilityReportMode, 'submit()', [
195				AVAILABILITY_REPORT_BY_HOST => _('By host'),
196				AVAILABILITY_REPORT_BY_TEMPLATE => _('By trigger template')
197			])
198		])
199	);
200	$reportWidget->setControls($headerForm);
201
202	$triggerOptions = [
203		'output' => ['triggerid', 'description', 'expression', 'value'],
204		'expandDescription' => true,
205		'monitored' => true,
206		'selectHosts' => ['name'],
207		'filter' => [],
208		'hostids' => null,
209		'limit' => $config['search_limit'] + 1
210	];
211
212	/*
213	 * Filter
214	 */
215	$filterForm = (new CFilter('web.avail_report.filter.state'))
216		->addVar('config', $availabilityReportMode)
217		->addVar('filter_timesince', date(TIMESTAMP_FORMAT, $_REQUEST['filter_timesince']))
218		->addVar('filter_timetill', date(TIMESTAMP_FORMAT, $_REQUEST['filter_timetill']));
219
220	$filterColumn1 = new CFormList();
221	$filterColumn2 = new CFormList();
222
223	// report by template
224	if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) {
225		// trigger options
226		if (!empty($_REQUEST['filter_hostid'])) {
227			$hosts = API::Host()->get([
228				'output' => ['hostid'],
229				'templateids' => $_REQUEST['filter_hostid']
230			]);
231
232			$triggerOptions['hostids'] = zbx_objectValues($hosts, 'hostid');
233		}
234		if (isset($_REQUEST['tpl_triggerid']) && !empty($_REQUEST['tpl_triggerid'])) {
235			$triggerOptions['filter']['templateid'] = $_REQUEST['tpl_triggerid'];
236		}
237		if (isset($_REQUEST['hostgroupid']) && !empty($_REQUEST['hostgroupid'])) {
238			$triggerOptions['groupids'] = $_REQUEST['hostgroupid'];
239		}
240
241		// filter template group
242		$groupsComboBox = new CComboBox('filter_groupid', $_REQUEST['filter_groupid'], 'javascript: submit();');
243		$groupsComboBox->addItem(0, _('all'));
244
245		$groups = API::HostGroup()->get([
246			'output' => ['groupid', 'name'],
247			'templated_hosts' => true,
248			'with_triggers' => true
249		]);
250		order_result($groups, 'name');
251
252		foreach ($groups as $group) {
253			$groupsComboBox->addItem($group['groupid'], $group['name']);
254		}
255		$filterColumn1->addRow(_('Template group'), $groupsComboBox);
256
257		// filter template
258		$templateComboBox = new CComboBox('filter_hostid', $_REQUEST['filter_hostid'], 'javascript: submit();');
259		$templateComboBox->addItem(0, _('all'));
260
261		$templates = API::Template()->get([
262			'output' => ['templateid', 'name'],
263			'groupids' => empty($_REQUEST['filter_groupid']) ? null : $_REQUEST['filter_groupid'],
264			'with_triggers' => true
265		]);
266		order_result($templates, 'name');
267
268		$templateIds = [];
269		foreach ($templates as $template) {
270			$templateIds[$template['templateid']] = $template['templateid'];
271
272			$templateComboBox->addItem($template['templateid'], $template['name']);
273		}
274		$filterColumn1->addRow(_('Template'), $templateComboBox);
275
276		// filter trigger
277		$triggerComboBox = new CComboBox('tpl_triggerid', getRequest('tpl_triggerid', 0), 'javascript: submit()');
278		$triggerComboBox->addItem(0, _('all'));
279
280		$sqlCondition = empty($_REQUEST['filter_hostid'])
281			? ' AND '.dbConditionInt('h.hostid', $templateIds)
282			: ' AND h.hostid='.zbx_dbstr($_REQUEST['filter_hostid']);
283
284		$sql =
285			'SELECT DISTINCT t.triggerid,t.description,h.name'.
286			' FROM triggers t,hosts h,items i,functions f'.
287			' WHERE f.itemid=i.itemid'.
288				' AND h.hostid=i.hostid'.
289				' AND t.status='.TRIGGER_STATUS_ENABLED.
290				' AND t.triggerid=f.triggerid'.
291				' AND h.status='.HOST_STATUS_TEMPLATE.
292				' AND i.status='.ITEM_STATUS_ACTIVE.
293					$sqlCondition.
294			' ORDER BY t.description';
295		$triggers = DBfetchArrayAssoc(DBselect($sql), 'triggerid');
296
297		foreach ($triggers as $trigger) {
298			$templateName = empty($_REQUEST['filter_hostid']) ? $trigger['name'].NAME_DELIMITER : '';
299
300			$triggerComboBox->addItem($trigger['triggerid'], $templateName.$trigger['description']);
301		}
302
303		if (isset($_REQUEST['tpl_triggerid']) && !isset($triggers[$_REQUEST['tpl_triggerid']])) {
304			unset($triggerOptions['filter']['templateid']);
305		}
306
307		$filterColumn1->addRow(_('Template trigger'), $triggerComboBox);
308
309		// filter host group
310		$hostGroupsComboBox = new CComboBox('hostgroupid', getRequest('hostgroupid', 0), 'javascript: submit()');
311		$hostGroupsComboBox->addItem(0, _('all'));
312
313		$hostGroups = API::HostGroup()->get([
314			'output' => ['groupid', 'name'],
315			'hostids' => $triggerOptions['hostids'],
316			'monitored_hosts' => true,
317			'preservekeys' => true
318		]);
319		order_result($hostGroups, 'name');
320
321		foreach ($hostGroups as $hostGroup) {
322			$hostGroupsComboBox->addItem($hostGroup['groupid'], $hostGroup['name']);
323		}
324
325		if (isset($_REQUEST['hostgroupid']) && !isset($hostGroups[$_REQUEST['hostgroupid']])) {
326			unset($triggerOptions['groupids']);
327		}
328
329		$filterColumn1->addRow(_('Filter by host group'), $hostGroupsComboBox);
330	}
331
332	// report by host
333	elseif ($availabilityReportMode == AVAILABILITY_REPORT_BY_HOST) {
334		// filter host group
335		$groupsComboBox = new CComboBox('filter_groupid', $_REQUEST['filter_groupid'], 'javascript: submit();');
336		$groupsComboBox->addItem(0, _('all'));
337
338		$groups = API::HostGroup()->get([
339			'output' => ['groupid', 'name'],
340			'monitored_hosts' => true,
341			'with_triggers' => true
342		]);
343		order_result($groups, 'name');
344
345		foreach ($groups as $group) {
346			$groupsComboBox->addItem($group['groupid'], $group['name']);
347		}
348		$filterColumn1->addRow(_('Host group'), $groupsComboBox);
349
350		// filter host
351		$hostsComboBox = new CComboBox('filter_hostid', $_REQUEST['filter_hostid'], 'javascript: submit();');
352		$hostsComboBox->addItem(0, _('all'));
353
354		$hosts = API::Host()->get([
355			'groupids' => empty($_REQUEST['filter_groupid']) ? null : $_REQUEST['filter_groupid'],
356			'output' => ['hostid', 'name'],
357			'monitored_hosts' => true,
358			'with_triggers' => true
359		]);
360		order_result($hosts, 'name');
361		$hosts = zbx_toHash($hosts, 'hostid');
362
363		foreach ($hosts as $host) {
364			$hostsComboBox->addItem($host['hostid'], $host['name']);
365		}
366		$filterColumn1->addRow(_('Host'), $hostsComboBox);
367
368		// trigger options
369		if (!empty($_REQUEST['filter_groupid'])) {
370			$triggerOptions['groupids'] = $_REQUEST['filter_groupid'];
371		}
372		if (!empty($_REQUEST['filter_hostid']) && isset($hosts[$_REQUEST['filter_hostid']])) {
373			$triggerOptions['hostids'] = $_REQUEST['filter_hostid'];
374		}
375	}
376
377	// filter period
378	$filterColumn2->addRow(_('From'), createDateSelector('filter_timesince', $_REQUEST['filter_timesince'], 'filter_timetill'));
379	$filterColumn2->addRow(_('To'), createDateSelector('filter_timetill', $_REQUEST['filter_timetill'], 'filter_timesince'));
380
381	$filterForm->addColumn($filterColumn1);
382	$filterForm->addColumn($filterColumn2);
383
384	$reportWidget->addItem($filterForm);
385
386	/*
387	 * Triggers
388	 */
389	$triggerTable = (new CTableInfo())
390		->setHeader([
391			($_REQUEST['filter_hostid'] == 0 || $availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) ? _('Host') : null,
392			_('Name'),
393			_('Problems'),
394			_('Ok'),
395			_('Graph')
396		]);
397
398	$triggers = API::Trigger()->get($triggerOptions);
399
400	if (getRequest('filter_hostid') == 0 || $availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) {
401		foreach ($triggers as &$trigger) {
402			$trigger['host_name'] = $trigger['hosts'][0]['name'];
403		}
404		unset($trigger);
405
406		CArrayHelper::sort($triggers, ['host_name', 'description']);
407	}
408	else {
409		CArrayHelper::sort($triggers, ['description']);
410	}
411
412	$paging = getPagingLine($triggers, ZBX_SORT_UP, new CUrl('report2.php'));
413
414	foreach ($triggers as $trigger) {
415		$availability = calculateAvailability($trigger['triggerid'], getRequest('filter_timesince'),
416			getRequest('filter_timetill')
417		);
418
419		$triggerTable->addRow([
420			($_REQUEST['filter_hostid'] == 0 || $availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE)
421				? $trigger['host_name'] : null,
422			new CLink($trigger['description'], 'events.php?filter_set=1&triggerid='.$trigger['triggerid']),
423			$availability['true'] < 0.00005
424				? ''
425				: (new CSpan(sprintf('%.4f%%', $availability['true'])))->addClass(ZBX_STYLE_RED),
426			$availability['false'] < 0.00005
427				?  ''
428				: (new CSpan(sprintf('%.4f%%', $availability['false'])))->addClass(ZBX_STYLE_GREEN),
429			new CLink(_('Show'), 'report2.php?filter_groupid='.$_REQUEST['filter_groupid'].
430				'&filter_hostid='.$_REQUEST['filter_hostid'].'&triggerid='.$trigger['triggerid'])
431		]);
432	}
433
434	$reportWidget->addItem([$triggerTable, $paging])
435		->show();
436}
437
438require_once dirname(__FILE__).'/include/page_footer.php';
439