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', 'gtlc.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 'from' => [T_ZBX_RANGE_TIME, O_OPT, P_SYS, null, null], 44 'to' => [T_ZBX_RANGE_TIME, O_OPT, P_SYS, null, null] 45]; 46check_fields($fields); 47validateTimeSelectorPeriod(getRequest('from'), getRequest('to')); 48 49$availabilityReportMode = getRequest('mode', CProfile::get('web.avail_report.mode', AVAILABILITY_REPORT_BY_HOST)); 50CProfile::update('web.avail_report.mode', $availabilityReportMode, PROFILE_TYPE_INT); 51 52/* 53 * Permissions 54 */ 55if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) { 56 if (getRequest('hostgroupid') && !isReadableHostGroups([getRequest('hostgroupid')])) { 57 access_deny(); 58 } 59 if (getRequest('filter_groupid') && !isReadableHostGroups([getRequest('filter_groupid')])) { 60 access_deny(); 61 } 62 if (getRequest('filter_hostid') && !isReadableTemplates([getRequest('filter_hostid')])) { 63 access_deny(); 64 } 65 if (getRequest('tpl_triggerid')) { 66 $trigger = API::Trigger()->get([ 67 'triggerids' => $_REQUEST['tpl_triggerid'], 68 'output' => ['triggerid'], 69 'filter' => ['flags' => null] 70 ]); 71 if (!$trigger) { 72 access_deny(); 73 } 74 } 75} 76else { 77 if (getRequest('filter_groupid') && !isReadableHostGroups([getRequest('filter_groupid')])) { 78 access_deny(); 79 } 80 if (getRequest('filter_hostid') && !isReadableHosts([getRequest('filter_hostid')])) { 81 access_deny(); 82 } 83} 84if (getRequest('triggerid') && !isReadableTriggers([getRequest('triggerid')])) { 85 access_deny(); 86} 87 88/* 89 * Filter 90 */ 91if (hasRequest('filter_rst')) { 92 $_REQUEST['filter_groupid'] = 0; 93 $_REQUEST['filter_hostid'] = 0; 94 95 if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) { 96 $_REQUEST['tpl_triggerid'] = 0; 97 $_REQUEST['hostgroupid'] = 0; 98 } 99} 100 101if (!hasRequest('filter_rst')) { 102 $_REQUEST['filter_groupid'] = getRequest('filter_groupid', 103 CProfile::get('web.avail_report.'.$availabilityReportMode.'.groupid', 0) 104 ); 105 $_REQUEST['filter_hostid'] = getRequest('filter_hostid', 106 CProfile::get('web.avail_report.'.$availabilityReportMode.'.hostid', 0) 107 ); 108 109 if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) { 110 $_REQUEST['tpl_triggerid'] = getRequest('tpl_triggerid', 111 CProfile::get('web.avail_report.'.$availabilityReportMode.'.tpl_triggerid', 0) 112 ); 113 114 $_REQUEST['hostgroupid'] = getRequest('hostgroupid', 115 CProfile::get('web.avail_report.'.$availabilityReportMode.'.hostgroupid', 0) 116 ); 117 } 118} 119 120CProfile::update('web.avail_report.'.$availabilityReportMode.'.groupid', getRequest('filter_groupid', 0), 121 PROFILE_TYPE_ID 122); 123CProfile::update('web.avail_report.'.$availabilityReportMode.'.hostid', getRequest('filter_hostid', 0), 124 PROFILE_TYPE_ID 125); 126 127if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) { 128 CProfile::update('web.avail_report.'.$availabilityReportMode.'.tpl_triggerid', getRequest('tpl_triggerid', 0), 129 PROFILE_TYPE_ID 130 ); 131 132 CProfile::update('web.avail_report.'.$availabilityReportMode.'.hostgroupid', getRequest('hostgroupid', 0), 133 PROFILE_TYPE_ID 134 ); 135} 136 137$timeselector_options = [ 138 'profileIdx' => 'web.avail_report.filter', 139 'profileIdx2' => 0, 140 'from' => getRequest('from'), 141 'to' => getRequest('to') 142]; 143updateTimeSelectorPeriod($timeselector_options); 144 145$config = select_config(); 146 147/* 148 * Header 149 */ 150$triggerData = isset($_REQUEST['triggerid']) 151 ? API::Trigger()->get([ 152 'triggerids' => $_REQUEST['triggerid'], 153 'output' => API_OUTPUT_EXTEND, 154 'selectHosts' => API_OUTPUT_EXTEND, 155 'expandDescription' => true 156 ]) 157 : null; 158 159$reportWidget = (new CWidget())->setTitle(_('Availability report')); 160 161if ($triggerData) { 162 $triggerData = reset($triggerData); 163 $host = reset($triggerData['hosts']); 164 165 $triggerData['hostid'] = $host['hostid']; 166 $triggerData['hostname'] = $host['name']; 167 168 $reportWidget->setControls((new CTag('nav', true, 169 (new CList()) 170 ->addItem(new CLink($triggerData['hostname'], '?filter_groupid='.$_REQUEST['filter_groupid'])) 171 ->addItem($triggerData['description']) 172 )) 173 ->setAttribute('aria-label', _('Content controls')) 174 ); 175 176 $table = (new CTableInfo()) 177 ->addRow(new CImg('chart4.php?triggerid='.$_REQUEST['triggerid'])); 178 179 $reportWidget->addItem(BR()) 180 ->addItem($table) 181 ->show(); 182} 183elseif (hasRequest('filter_hostid')) { 184 $reportWidget->setControls((new CForm('get')) 185 ->setAttribute('aria-label', _('Main filter')) 186 ->addItem((new CList()) 187 ->addItem([ 188 new CLabel(_('Mode'), 'mode'), 189 (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN), 190 new CComboBox('mode', $availabilityReportMode, 'submit()', [ 191 AVAILABILITY_REPORT_BY_HOST => _('By host'), 192 AVAILABILITY_REPORT_BY_TEMPLATE => _('By trigger template') 193 ]) 194 ]) 195 )); 196 197 /* 198 * Filter 199 */ 200 $data = [ 201 'filter' => [ 202 'timeline' => getTimeSelectorPeriod($timeselector_options), 203 'active_tab' => CProfile::get('web.avail_report.filter.active', 1) 204 ] 205 ]; 206 207 $filter_column = new CFormList(); 208 209 $filter_hostid = getRequest('filter_hostid'); 210 $filter_groupid = getRequest('filter_groupid'); 211 $tpl_triggerid = getRequest('tpl_triggerid'); 212 $hostgroupid = getRequest('hostgroupid'); 213 214 // Sanitize $filter_groupid and prepare "Template group" or "Host group" combo box (for both view modes). 215 216 $options = [ 217 'output' => ['name'], 218 'with_triggers' => true, 219 'preservekeys' => true 220 ]; 221 222 if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) { 223 $options['templated_hosts'] = true; 224 } 225 elseif ($availabilityReportMode == AVAILABILITY_REPORT_BY_HOST) { 226 $options['monitored_hosts'] = true; 227 } 228 229 $groups = API::HostGroup()->get($options); 230 $groups = enrichParentGroups($groups); 231 CArrayHelper::sort($groups, ['name']); 232 233 if (!array_key_exists($filter_groupid, $groups)) { 234 $filter_groupid = 0; 235 } 236 237 $filter_groupid_combobox = (new CComboBox('filter_groupid', $filter_groupid, 'javascript: submit();')) 238 ->setAttribute('autofocus', 'autofocus') 239 ->addItem(0, _('all')); 240 241 foreach ($groups as $groupid => $group) { 242 $filter_groupid_combobox->addItem($groupid, $group['name']); 243 } 244 245 if ($filter_groupid == 0) { 246 $filter_groupids = null; 247 } 248 else { 249 $filter_groupids = [$filter_groupid]; 250 $parent = $groups[$filter_groupid]['name'].'/'; 251 252 foreach ($groups as $groupid => $group) { 253 if (strpos($group['name'], $parent) === 0) { 254 $filter_groupids[] = $groupid; 255 } 256 } 257 } 258 259 if ($availabilityReportMode == AVAILABILITY_REPORT_BY_TEMPLATE) { 260 // Sanitize $filter_hostid and prepare "Template" combo box. 261 262 $templates = API::Template()->get([ 263 'output' => ['name'], 264 'groupids' => $filter_groupids, 265 'with_triggers' => true, 266 'preservekeys' => true 267 ]); 268 CArrayHelper::sort($templates, ['name']); 269 270 if (!array_key_exists($filter_hostid, $templates)) { 271 $filter_hostid = 0; 272 } 273 274 $filter_hostid_combobox = (new CComboBox('filter_hostid', $filter_hostid, 'javascript: submit();')) 275 ->addItem(0, _('all')); 276 277 foreach ($templates as $templateid => $template) { 278 $filter_hostid_combobox->addItem($templateid, $template['name']); 279 } 280 281 // Sanitize $tpl_triggerid and prepare "Template Trigger" combo box. 282 283 $triggers = API::Trigger()->get([ 284 'output' => ['description'], 285 'selectHosts' => ['name'], 286 'selectItems' => ['status'], 287 'templateids' => ($filter_hostid == 0) ? null : $filter_hostid, 288 'groupids' => $filter_groupids, 289 'templated' => true, 290 'filter' => ['status' => TRIGGER_STATUS_ENABLED, 'flags' => [ZBX_FLAG_DISCOVERY_NORMAL]], 291 'sortfield' => 'description', 292 'preservekeys' => true 293 ]); 294 295 foreach ($triggers as $triggerid => $trigger) { 296 foreach ($trigger['items'] as $item) { 297 if ($item['status'] != ITEM_STATUS_ACTIVE) { 298 unset($triggers[$triggerid]); 299 300 break; 301 } 302 } 303 } 304 305 if (!array_key_exists($tpl_triggerid, $triggers)) { 306 $tpl_triggerid = 0; 307 } 308 309 $tpl_triggerid_combobox = (new CComboBox('tpl_triggerid', $tpl_triggerid, 'javascript: submit()')) 310 ->addItem(0, _('all')); 311 312 $tpl_triggerids = []; 313 314 foreach ($triggers as $triggerid => $trigger) { 315 $tpl_triggerid_combobox->addItem($triggerid, 316 (($filter_hostid == 0) ? $trigger['hosts'][0]['name'].NAME_DELIMITER : '').$trigger['description'] 317 ); 318 319 $tpl_triggerids[$triggerid] = true; 320 } 321 322 // Sanitize $hostgroupid and prepare "Host Group" combo box. 323 324 $host_groups = API::HostGroup()->get([ 325 'output' => ['name'], 326 'monitored_hosts' => true, 327 'preservekeys' => true 328 ]); 329 $host_groups = enrichParentGroups($host_groups); 330 CArrayHelper::sort($host_groups, ['name']); 331 332 if (!array_key_exists($hostgroupid, $host_groups)) { 333 $hostgroupid = 0; 334 } 335 336 $hostgroupid_combobox = (new CComboBox('hostgroupid', $hostgroupid, 'javascript: submit()')) 337 ->addItem(0, _('all')); 338 339 foreach ($host_groups as $groupid => $group) { 340 $hostgroupid_combobox->addItem($groupid, $group['name']); 341 } 342 343 $hostgroupids = []; 344 345 if ($hostgroupid != 0) { 346 $hostgroupids[$hostgroupid] = true; 347 $parent = $host_groups[$hostgroupid]['name'].'/'; 348 349 foreach ($host_groups as $groupid => $group) { 350 if (strpos($group['name'], $parent) === 0) { 351 $hostgroupids[$groupid] = true; 352 } 353 } 354 } 355 356 // Gather all templated triggers, originating from host templates, which belong to requested template groups. 357 358 $templated_triggers_all = ($tpl_triggerid == 0) ? $tpl_triggerids : [$tpl_triggerid => true]; 359 $templated_triggers_new = $templated_triggers_all; 360 361 while ($templated_triggers_new) { 362 $templated_triggers_new = API::Trigger()->get([ 363 'output' => ['triggerid'], 364 'templated' => true, 365 'filter' => ['templateid' => array_keys($templated_triggers_new)], 366 'preservekeys' => true 367 ]); 368 $templated_triggers_new = array_diff_key($templated_triggers_new, $templated_triggers_all); 369 $templated_triggers_all += $templated_triggers_new; 370 } 371 372 if ($templated_triggers_all) { 373 // Select monitored host triggers, derived from templates and belonging to the requested groups. 374 $triggers = API::Trigger()->get([ 375 'output' => ['triggerid', 'description', 'expression', 'value'], 376 'selectHosts' => ['name'], 377 'expandDescription' => true, 378 'monitored' => true, 379 'groupids' => ($hostgroupid == 0) ? null : array_keys($hostgroupids), 380 'filter' => ['templateid' => array_keys($templated_triggers_all)], 381 'limit' => $config['search_limit'] + 1 382 ]); 383 } 384 else { 385 // No trigger templates means there are no derived triggers. 386 $triggers = []; 387 } 388 389 $filter_column 390 ->addRow(_('Template group'), $filter_groupid_combobox) 391 ->addRow(_('Template'), $filter_hostid_combobox) 392 ->addRow(_('Template trigger'), $tpl_triggerid_combobox) 393 ->addRow(_('Host group'), $hostgroupid_combobox); 394 } 395 // Report by host. 396 elseif ($availabilityReportMode == AVAILABILITY_REPORT_BY_HOST) { 397 // Sanitize $filter_hostid and prepare "Host" combo box. 398 399 $hosts = API::Host()->get([ 400 'output' => ['name'], 401 'groupids' => $filter_groupids, 402 'monitored_hosts' => true, 403 'with_triggers' => true, 404 'preservekeys' => true 405 ]); 406 CArrayHelper::sort($hosts, ['name']); 407 408 if (!array_key_exists($filter_hostid, $hosts)) { 409 $filter_hostid = 0; 410 } 411 412 // Select monitored host triggers, derived from templates and belonging to the requested groups. 413 $triggers = API::Trigger()->get([ 414 'output' => ['triggerid', 'description', 'expression', 'value'], 415 'selectHosts' => ['name'], 416 'expandDescription' => true, 417 'monitored' => true, 418 'groupids' => $filter_groupids, 419 'hostids' => ($filter_hostid == 0) ? null : [$filter_hostid], 420 'limit' => $config['search_limit'] + 1 421 ]); 422 423 $hosts_combobox = (new CComboBox('filter_hostid', $filter_hostid, 'javascript: submit();')) 424 ->addItem(0, _('all')); 425 426 foreach ($hosts as $hostid => $host) { 427 $hosts_combobox->addItem($hostid, $host['name']); 428 } 429 430 $filter_column 431 ->addRow(_('Host group'), $filter_groupid_combobox) 432 ->addRow(_('Host'), $hosts_combobox); 433 } 434 435 // Now just prepare needed data. 436 437 foreach ($triggers as &$trigger) { 438 $trigger['host_name'] = $trigger['hosts'][0]['name']; 439 } 440 unset($trigger); 441 442 $reportWidget->addItem( 443 (new CFilter(new CUrl('report2.php'))) 444 ->setProfile($data['filter']['timeline']['profileIdx']) 445 ->setActiveTab($data['filter']['active_tab']) 446 ->addFormItem((new CVar('mode', $availabilityReportMode))->removeId()) 447 ->addTimeSelector($data['filter']['timeline']['from'], $data['filter']['timeline']['to'], true, 448 ZBX_DATE_TIME) 449 ->addFilterTab(_('Filter'), [$filter_column]) 450 ); 451 452 /* 453 * Triggers 454 */ 455 $triggerTable = (new CTableInfo())->setHeader([_('Host'), _('Name'), _('Problems'), _('Ok'), _('Graph')]); 456 457 CArrayHelper::sort($triggers, ['host_name', 'description']); 458 459 $paging = getPagingLine($triggers, ZBX_SORT_UP, new CUrl('report2.php')); 460 461 foreach ($triggers as $trigger) { 462 $availability = calculateAvailability($trigger['triggerid'], $data['filter']['timeline']['from_ts'], 463 $data['filter']['timeline']['to_ts'] 464 ); 465 466 $triggerTable->addRow([ 467 $trigger['host_name'], 468 new CLink($trigger['description'], 469 (new CUrl('zabbix.php')) 470 ->setArgument('action', 'problem.view') 471 ->setArgument('filter_triggerids[]', $trigger['triggerid']) 472 ->setArgument('filter_set', '1') 473 ), 474 ($availability['true'] < 0.00005) 475 ? '' 476 : (new CSpan(sprintf('%.4f%%', $availability['true'])))->addClass(ZBX_STYLE_RED), 477 ($availability['false'] < 0.00005) 478 ? '' 479 : (new CSpan(sprintf('%.4f%%', $availability['false'])))->addClass(ZBX_STYLE_GREEN), 480 new CLink(_('Show'), 'report2.php?filter_groupid='.$_REQUEST['filter_groupid']. 481 '&filter_hostid='.$_REQUEST['filter_hostid'].'&triggerid='.$trigger['triggerid'] 482 ) 483 ]); 484 } 485 486 $obj_data = [ 487 'id' => 'timeline_1', 488 'domid' => 'avail_report', 489 'loadSBox' => 0, 490 'loadImage' => 0, 491 'dynamic' => 0, 492 'mainObject' => 1 493 ]; 494 zbx_add_post_js( 495 'timeControl.addObject("avail_report", '.zbx_jsvalue($data['filter']).', '.zbx_jsvalue($obj_data).');' 496 ); 497 zbx_add_post_js('timeControl.processObjects();'); 498 499 $reportWidget 500 ->addItem([$triggerTable, $paging]) 501 ->show(); 502} 503 504require_once dirname(__FILE__).'/include/page_footer.php'; 505