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__).'/graphs.inc.php'; 23require_once dirname(__FILE__).'/screens.inc.php'; 24require_once dirname(__FILE__).'/maps.inc.php'; 25require_once dirname(__FILE__).'/users.inc.php'; 26 27/** 28 * @param array $filter 29 * @param array $filter['groupids'] (optional) 30 * @param array $filter['exclude_groupids'] (optional) 31 * @param array $filter['hostids'] (optional) 32 * @param string $filter['problem'] (optional) 33 * @param array $filter['severities'] (optional) 34 * @param int $filter['show_suppressed'] (optional) 35 * @param int $filter['hide_empty_groups'] (optional) 36 * @param int $filter['ext_ack'] (optional) 37 * @param int $filter['show_opdata'] (optional) 38 * 39 * @return array 40 */ 41function getSystemStatusData(array $filter) { 42 $filter_groupids = (array_key_exists('groupids', $filter) && $filter['groupids']) ? $filter['groupids'] : null; 43 $filter_hostids = (array_key_exists('hostids', $filter) && $filter['hostids']) ? $filter['hostids'] : null; 44 $filter_severities = (array_key_exists('severities', $filter) && $filter['severities']) 45 ? $filter['severities'] 46 : range(TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_COUNT - 1); 47 $filter_ext_ack = array_key_exists('ext_ack', $filter) 48 ? $filter['ext_ack'] 49 : EXTACK_OPTION_ALL; 50 $filter_evaltype = array_key_exists('evaltype', $filter) ? $filter['evaltype'] : TAG_EVAL_TYPE_AND_OR; 51 $filter_tags = array_key_exists('tags', $filter) ? $filter['tags'] : []; 52 53 if (array_key_exists('exclude_groupids', $filter) && $filter['exclude_groupids']) { 54 if ($filter_hostids === null) { 55 // Get all groups if no selected groups defined. 56 if ($filter_groupids === null) { 57 $filter_groupids = array_keys(API::HostGroup()->get([ 58 'output' => [], 59 'real_hosts' => true, 60 'preservekeys' => true 61 ])); 62 } 63 64 $filter_groupids = array_diff($filter_groupids, $filter['exclude_groupids']); 65 66 // Get available hosts. 67 $filter_hostids = array_keys(API::Host()->get([ 68 'output' => [], 69 'groupids' => $filter_groupids, 70 'preservekeys' => true 71 ])); 72 } 73 74 $exclude_hostids = array_keys(API::Host()->get([ 75 'output' => [], 76 'groupids' => $filter['exclude_groupids'], 77 'preservekeys' => true 78 ])); 79 80 $filter_hostids = array_diff($filter_hostids, $exclude_hostids); 81 } 82 83 $data = [ 84 'groups' => API::HostGroup()->get([ 85 'output' => ['groupid', 'name'], 86 'groupids' => $filter_groupids, 87 'hostids' => $filter_hostids, 88 'monitored_hosts' => true, 89 'preservekeys' => true 90 ]), 91 'triggers' => [], 92 'actions' => [], 93 'stats' => [] 94 ]; 95 96 CArrayHelper::sort($data['groups'], [['field' => 'name', 'order' => ZBX_SORT_UP]]); 97 98 $default_stats = []; 99 100 for ($severity = TRIGGER_SEVERITY_COUNT - 1; $severity >= TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity--) { 101 if (in_array($severity, $filter_severities)) { 102 $default_stats[$severity] = ['count' => 0, 'problems' => [], 'count_unack' => 0, 'problems_unack' => []]; 103 } 104 } 105 106 $data['stats'] = $default_stats; 107 108 foreach ($data['groups'] as &$group) { 109 $group['stats'] = $default_stats; 110 $group['has_problems'] = false; 111 } 112 unset($group); 113 114 $options = [ 115 'output' => ['eventid', 'objectid', 'clock', 'ns', 'name', 'acknowledged', 'severity'], 116 'groupids' => array_keys($data['groups']), 117 'hostids' => $filter_hostids, 118 'evaltype' => $filter_evaltype, 119 'tags' => $filter_tags, 120 'source' => EVENT_SOURCE_TRIGGERS, 121 'object' => EVENT_OBJECT_TRIGGER, 122 'suppressed' => false, 123 'sortfield' => ['eventid'], 124 'sortorder' => ZBX_SORT_DOWN, 125 'preservekeys' => true 126 ]; 127 128 if (array_key_exists('severities', $filter)) { 129 $filter_severities = implode(',', $filter['severities']); 130 $all_severities = implode(',', range(TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_COUNT - 1)); 131 132 if ($filter_severities !== '' && $filter_severities !== $all_severities) { 133 $options['severities'] = $filter['severities']; 134 } 135 } 136 137 if (array_key_exists('show_suppressed', $filter) && $filter['show_suppressed']) { 138 unset($options['suppressed']); 139 $options['selectSuppressionData'] = ['maintenanceid', 'suppress_until']; 140 } 141 142 if ($filter_ext_ack == EXTACK_OPTION_UNACK) { 143 $options['acknowledged'] = false; 144 } 145 146 if (array_key_exists('problem', $filter) && $filter['problem'] !== '') { 147 $options['search'] = ['name' => $filter['problem']]; 148 } 149 150 $problems = API::Problem()->get($options); 151 if ($problems) { 152 $triggerids = []; 153 154 foreach ($problems as $problem) { 155 $triggerids[$problem['objectid']] = true; 156 } 157 158 $options = [ 159 'output' => ['priority'], 160 'selectGroups' => ['groupid'], 161 'selectHosts' => ['name'], 162 'selectItems' => ['itemid', 'hostid', 'name', 'key_', 'value_type', 'units', 'valuemapid'], 163 'triggerids' => array_keys($triggerids), 164 'monitored' => true, 165 'skipDependent' => true, 166 'preservekeys' => true 167 ]; 168 169 if (array_key_exists('show_opdata', $filter) && $filter['show_opdata'] != OPERATIONAL_DATA_SHOW_NONE) { 170 $options['output'] = array_merge( 171 $options['output'], 172 ['url', 'expression', 'recovery_mode', 'recovery_expression', 'opdata'] 173 ); 174 } 175 176 $data['triggers'] = API::Trigger()->get($options); 177 178 foreach ($data['triggers'] as &$trigger) { 179 CArrayHelper::sort($trigger['hosts'], [['field' => 'name', 'order' => ZBX_SORT_UP]]); 180 } 181 unset($trigger); 182 183 foreach ($problems as $eventid => $problem) { 184 if (!array_key_exists($problem['objectid'], $data['triggers'])) { 185 unset($problems[$eventid]); 186 } 187 } 188 189 $visible_problems = []; 190 191 foreach ($problems as $eventid => $problem) { 192 $trigger = $data['triggers'][$problem['objectid']]; 193 194 $data['stats'][$problem['severity']]['count']++; 195 if ($problem['acknowledged'] == EVENT_NOT_ACKNOWLEDGED) { 196 $data['stats'][$problem['severity']]['count_unack']++; 197 } 198 199 // groups 200 foreach ($trigger['groups'] as $trigger_group) { 201 if (!array_key_exists($trigger_group['groupid'], $data['groups'])) { 202 continue; 203 } 204 205 $group = &$data['groups'][$trigger_group['groupid']]; 206 207 if (in_array($filter_ext_ack, [EXTACK_OPTION_ALL, EXTACK_OPTION_BOTH])) { 208 if ($group['stats'][$problem['severity']]['count'] < ZBX_WIDGET_ROWS) { 209 $group['stats'][$problem['severity']]['problems'][] = $problem; 210 $visible_problems[$eventid] = ['eventid' => $eventid]; 211 } 212 213 $group['stats'][$problem['severity']]['count']++; 214 } 215 216 if (in_array($filter_ext_ack, [EXTACK_OPTION_UNACK, EXTACK_OPTION_BOTH]) 217 && $problem['acknowledged'] == EVENT_NOT_ACKNOWLEDGED) { 218 if ($group['stats'][$problem['severity']]['count_unack'] < ZBX_WIDGET_ROWS) { 219 $group['stats'][$problem['severity']]['problems_unack'][] = $problem; 220 $visible_problems[$eventid] = ['eventid' => $eventid]; 221 } 222 223 $group['stats'][$problem['severity']]['count_unack']++; 224 } 225 226 $group['has_problems'] = true; 227 } 228 unset($group); 229 } 230 231 // actions & tags 232 $problems_data = API::Problem()->get([ 233 'output' => ['eventid', 'r_eventid', 'clock', 'objectid', 'severity'], 234 'eventids' => array_keys($visible_problems), 235 'selectAcknowledges' => ['userid', 'clock', 'message', 'action', 'old_severity', 'new_severity'], 236 'selectTags' => ['tag', 'value'], 237 'preservekeys' => true 238 ]); 239 240 // Remove problems that were resolved between requests or set tags. 241 foreach ($data['groups'] as $groupid => &$group) { 242 foreach ($group['stats'] as $severity => &$stat) { 243 foreach (['problems', 'problems_unack'] as $key) { 244 foreach ($stat[$key] as $event_no => &$problem) { 245 if (array_key_exists($problem['eventid'], $problems_data)) { 246 $problem['tags'] = $problems_data[$problem['eventid']]['tags']; 247 } 248 else { 249 if ($key === 'problems') { 250 $data['groups'][$groupid]['stats'][$severity]['count']--; 251 } 252 else { 253 $data['groups'][$groupid]['stats'][$severity]['count_unack']--; 254 } 255 unset($data['groups'][$groupid]['stats'][$severity][$key][$event_no]); 256 } 257 } 258 unset($problem); 259 } 260 } 261 unset($stat); 262 } 263 unset($group); 264 265 // actions 266 // Possible performance improvement: one API call may be saved, if r_clock for problem will be used. 267 $actions = getEventsActionsIconsData($problems_data, $data['triggers']); 268 $data['actions'] = [ 269 'all_actions' => $actions['data'], 270 'users' => API::User()->get([ 271 'output' => ['alias', 'name', 'surname'], 272 'userids' => array_keys($actions['userids']), 273 'preservekeys' => true 274 ]) 275 ]; 276 277 if (array_key_exists('show_opdata', $filter) && $filter['show_opdata'] != OPERATIONAL_DATA_SHOW_NONE) { 278 $maked_data = CScreenProblem::makeData( 279 ['problems' => $problems_data, 'triggers' => $data['triggers']], 280 ['show' => 0, 'details' => 0, 'show_opdata' => $filter['show_opdata']] 281 ); 282 $data['triggers'] = $maked_data['triggers']; 283 } 284 } 285 286 return $data; 287} 288 289/** 290 * @param array $filter 291 * @param array $filter['hostids'] (optional) 292 * @param string $filter['problem'] (optional) 293 * @param array $filter['severities'] (optional) 294 * @param int $filter['show_suppressed'] (optional) 295 * @param int $filter['hide_empty_groups'] (optional) 296 * @param int $filter['ext_ack'] (optional) 297 * @param int $filter['show_timeline'] (optional) 298 * @param int $filter['show_opdata'] (optional) 299 * @param array $data 300 * @param array $data['groups'] 301 * @param string $data['groups'][]['groupid'] 302 * @param string $data['groups'][]['name'] 303 * @param bool $data['groups'][]['has_problems'] 304 * @param array $data['groups'][]['stats'] 305 * @param int $data['groups'][]['stats']['count'] 306 * @param array $data['groups'][]['stats']['problems'] 307 * @param string $data['groups'][]['stats']['problems'][]['eventid'] 308 * @param string $data['groups'][]['stats']['problems'][]['objectid'] 309 * @param int $data['groups'][]['stats']['problems'][]['clock'] 310 * @param int $data['groups'][]['stats']['problems'][]['ns'] 311 * @param int $data['groups'][]['stats']['problems'][]['acknowledged'] 312 * @param array $data['groups'][]['stats']['problems'][]['tags'] 313 * @param string $data['groups'][]['stats']['problems'][]['tags'][]['tag'] 314 * @param string $data['groups'][]['stats']['problems'][]['tags'][]['value'] 315 * @param int $data['groups'][]['stats']['count_unack'] 316 * @param array $data['groups'][]['stats']['problems_unack'] 317 * @param array $data['triggers'] 318 * @param string $data['triggers'][<triggerid>]['expression'] 319 * @param string $data['triggers'][<triggerid>]['description'] 320 * @param array $data['triggers'][<triggerid>]['hosts'] 321 * @param string $data['triggers'][<triggerid>]['hosts'][]['name'] 322 * @param array $data['triggers'][<triggerid>]['opdata'] 323 * @param array $config 324 * @param string $config['severity_name_*'] 325 * 326 * @return CDiv 327 */ 328function makeSystemStatus(array $filter, array $data, array $config) { 329 $filter_severities = (array_key_exists('severities', $filter) && $filter['severities']) 330 ? $filter['severities'] 331 : range(TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_COUNT - 1); 332 $filter_hide_empty_groups = array_key_exists('hide_empty_groups', $filter) ? $filter['hide_empty_groups'] : 0; 333 $filter_ext_ack = array_key_exists('ext_ack', $filter) 334 ? $filter['ext_ack'] 335 : EXTACK_OPTION_ALL; 336 337 // indicator of sort field 338 $sort_div = (new CSpan())->addClass(ZBX_STYLE_ARROW_UP); 339 340 // Set trigger severities as table header starting from highest severity. 341 $header = [[_('Host group'), $sort_div]]; 342 343 for ($severity = TRIGGER_SEVERITY_COUNT - 1; $severity >= TRIGGER_SEVERITY_NOT_CLASSIFIED; $severity--) { 344 if (in_array($severity, $filter_severities)) { 345 $header[] = getSeverityName($severity, $config); 346 } 347 } 348 349 $table = (new CTableInfo()) 350 ->setHeader($header) 351 ->setHeadingColumn(0); 352 353 $url_group = (new CUrl('zabbix.php')) 354 ->setArgument('action', 'problem.view') 355 ->setArgument('filter_set', 1) 356 ->setArgument('filter_show', TRIGGERS_OPTION_RECENT_PROBLEM) 357 ->setArgument('filter_groupids', null) 358 ->setArgument('filter_hostids', array_key_exists('hostids', $filter) ? $filter['hostids'] : null) 359 ->setArgument('filter_name', array_key_exists('problem', $filter) ? $filter['problem'] : null) 360 ->setArgument('filter_show_suppressed', 361 (array_key_exists('show_suppressed', $filter) && $filter['show_suppressed'] == 1) 362 ? 1 363 : null 364 ); 365 366 foreach ($data['groups'] as $group) { 367 if ($filter_hide_empty_groups && !$group['has_problems']) { 368 continue; 369 } 370 371 $url_group->setArgument('filter_groupids', [$group['groupid']]); 372 $row = [new CLink($group['name'], $url_group->getUrl())]; 373 374 foreach ($group['stats'] as $severity => $stat) { 375 if ($stat['count'] == 0 && $stat['count_unack'] == 0) { 376 $row[] = ''; 377 continue; 378 } 379 380 $allTriggersNum = $stat['count']; 381 if ($allTriggersNum) { 382 $allTriggersNum = (new CLinkAction($allTriggersNum)) 383 ->setHint(makeProblemsPopup($stat['problems'], $data['triggers'], $data['actions'], $config, 384 $filter 385 )); 386 } 387 388 $unackTriggersNum = $stat['count_unack']; 389 if ($unackTriggersNum) { 390 $unackTriggersNum = (new CLinkAction($unackTriggersNum)) 391 ->setHint(makeProblemsPopup($stat['problems_unack'], $data['triggers'], $data['actions'], $config, 392 $filter 393 )); 394 } 395 396 switch ($filter_ext_ack) { 397 case EXTACK_OPTION_ALL: 398 $row[] = getSeverityCell($severity, null, $allTriggersNum); 399 break; 400 401 case EXTACK_OPTION_UNACK: 402 $row[] = getSeverityCell($severity, null, $unackTriggersNum); 403 break; 404 405 case EXTACK_OPTION_BOTH: 406 if ($stat['count_unack'] != 0) { 407 $row[] = getSeverityCell($severity, $config, [ 408 $unackTriggersNum, ' '._('of').' ', $allTriggersNum 409 ]); 410 } 411 else { 412 $row[] = getSeverityCell($severity, $config, $allTriggersNum); 413 } 414 break; 415 } 416 } 417 418 $table->addRow($row); 419 } 420 421 return $table; 422} 423 424/** 425 * @param array $data 426 * @param array $data['groups'] 427 * @param string $data['groups'][]['groupid'] 428 * @param string $data['groups'][]['name'] 429 * @param bool $data['groups'][]['has_problems'] 430 * @param array $data['groups'][]['stats'] 431 * @param int $data['groups'][]['stats'][]['count'] 432 * @param array $data['groups'][]['stats'][]['problems'] 433 * @param int $data['groups'][]['stats'][]['count_unack'] 434 * @param array $data['groups'][]['stats'][]['problems_unack'] 435 * 436 * @return array 437 */ 438function getSystemStatusTotals(array $data) { 439 $groups_totals = [ 440 0 => [ 441 'groupid' => 0, 442 'stats' => [] 443 ] 444 ]; 445 446 foreach ($data['stats'] as $severity => $value) { 447 $groups_totals[0]['stats'][$severity] = [ 448 'count' => $value['count'], 449 'problems' => [], 450 'count_unack' => $value['count_unack'], 451 'problems_unack' => [] 452 ]; 453 } 454 455 foreach ($data['groups'] as $group) { 456 foreach ($group['stats'] as $severity => $stat) { 457 foreach ($stat['problems'] as $problem) { 458 $groups_totals[0]['stats'][$severity]['problems'][$problem['eventid']] = $problem; 459 } 460 foreach ($stat['problems_unack'] as $problem) { 461 $groups_totals[0]['stats'][$severity]['problems_unack'][$problem['eventid']] = $problem; 462 } 463 } 464 } 465 466 return $groups_totals; 467} 468 469/** 470 * @param array $data 471 * @param array $data['data'] 472 * @param array $data['data']['groups'] 473 * @param array $data['data']['groups'][]['stats'] 474 * @param array $data['filter'] 475 * @param array $data['filter']['severities'] 476 * @param boolean $hide_empty_groups 477 * @param CUrl $groupurl 478 * 479 * @return CTableInfo 480 */ 481function makeSeverityTable(array $data, $hide_empty_groups = false, CUrl $groupurl = null) { 482 $table = new CTableInfo(); 483 484 foreach ($data['data']['groups'] as $group) { 485 if ($hide_empty_groups && !$group['has_problems']) { 486 // Skip row. 487 continue; 488 } 489 490 $groupurl->setArgument('filter_groupids', [$group['groupid']]); 491 $row = [new CLink($group['name'], $groupurl->getUrl())]; 492 493 foreach ($group['stats'] as $severity => $stat) { 494 if ($data['filter']['severities'] && !in_array($severity, $data['filter']['severities'])) { 495 // Skip cell. 496 continue; 497 } 498 499 $row[] = getSeverityTableCell($severity, $data, $stat); 500 } 501 502 $table->addRow($row); 503 } 504 505 return $table; 506} 507 508/** 509 * @param array $data 510 * @param array $data['data'] 511 * @param array $data['data']['groups'] 512 * @param array $data['data']['groups'][]['stats'] 513 * @param array $data['filter'] 514 * @param array $data['filter']['severities'] 515 * 516 * @return CDiv 517 */ 518function makeSeverityTotals(array $data) { 519 $table = new CDiv(); 520 521 foreach ($data['data']['groups'] as $group) { 522 foreach ($group['stats'] as $severity => $stat) { 523 if ($data['filter']['severities'] && !in_array($severity, $data['filter']['severities'])) { 524 // Skip cell. 525 continue; 526 } 527 $table->addItem(getSeverityTableCell($severity, $data, $stat, true)); 528 } 529 } 530 531 return $table; 532} 533 534/** 535 * @param int $severity 536 * @param array $data 537 * @param array $data['data'] 538 * @param array $data['data']['triggers'] 539 * @param array $data['data']['actions'] 540 * @param array $data['filter'] 541 * @param array $data['filter']['ext_ack'] 542 * @param array $data['severity_names'] 543 * @param array $stat 544 * @param int $stats['count'] 545 * @param array $stats['problems'] 546 * @param int $stats['count_unack'] 547 * @param array $stats['problems_unack'] 548 * @param boolean $is_total 549 * 550 * @return CCol|string 551 */ 552function getSeverityTableCell($severity, array $data, array $stat, $is_total = false) { 553 if (!$is_total && $stat['count'] == 0 && $stat['count_unack'] == 0) { 554 return ''; 555 } 556 557 $severity_name = $is_total ? ' '.getSeverityName($severity, $data['severity_names']) : ''; 558 $ext_ack = array_key_exists('ext_ack', $data['filter']) ? $data['filter']['ext_ack'] : EXTACK_OPTION_ALL; 559 560 $allTriggersNum = $stat['count']; 561 if ($allTriggersNum) { 562 $allTriggersNum = (new CLinkAction($allTriggersNum)) 563 ->setHint(makeProblemsPopup($stat['problems'], $data['data']['triggers'], $data['data']['actions'], 564 $data['severity_names'], $data['filter'] 565 )); 566 } 567 568 $unackTriggersNum = $stat['count_unack']; 569 if ($unackTriggersNum) { 570 $unackTriggersNum = (new CLinkAction($unackTriggersNum)) 571 ->setHint(makeProblemsPopup($stat['problems_unack'], $data['data']['triggers'], $data['data']['actions'], 572 $data['severity_names'], $data['filter'] 573 )); 574 } 575 576 switch ($ext_ack) { 577 case EXTACK_OPTION_ALL: 578 return getSeverityCell($severity, null, [ 579 (new CSpan($allTriggersNum))->addClass(ZBX_STYLE_TOTALS_LIST_COUNT), 580 $severity_name 581 ], false, $is_total); 582 583 case EXTACK_OPTION_UNACK: 584 return getSeverityCell($severity, null, [ 585 (new CSpan($unackTriggersNum))->addClass(ZBX_STYLE_TOTALS_LIST_COUNT), 586 $severity_name 587 ], false, $is_total); 588 589 case EXTACK_OPTION_BOTH: 590 return getSeverityCell($severity, $data['severity_names'], [ 591 (new CSpan([$unackTriggersNum, ' '._('of').' ', $allTriggersNum])) 592 ->addClass(ZBX_STYLE_TOTALS_LIST_COUNT), 593 $severity_name 594 ], false, $is_total); 595 596 default: 597 return ''; 598 } 599} 600 601function make_status_of_zbx() { 602 if (CWebUser::getType() == USER_TYPE_SUPER_ADMIN) { 603 global $ZBX_SERVER, $ZBX_SERVER_PORT; 604 605 $server_details = $ZBX_SERVER.':'.$ZBX_SERVER_PORT; 606 } 607 else { 608 $server_details = ''; 609 } 610 611 $table = (new CTableInfo()) 612 ->setHeader([_('Parameter'), _('Value'), _('Details')]) 613 ->setHeadingColumn(0); 614 615 $status = get_status(); 616 617 $table 618 ->addRow([_('Zabbix server is running'), 619 (new CSpan($status['is_running'] ? _('Yes') : _('No'))) 620 ->addClass($status['is_running'] ? ZBX_STYLE_GREEN : ZBX_STYLE_RED), 621 $server_details 622 ]) 623 ->addRow([_('Number of hosts (enabled/disabled)'), 624 $status['has_status'] ? $status['hosts_count'] : '', 625 $status['has_status'] 626 ? [ 627 (new CSpan($status['hosts_count_monitored']))->addClass(ZBX_STYLE_GREEN), ' / ', 628 (new CSpan($status['hosts_count_not_monitored']))->addClass(ZBX_STYLE_RED) 629 ] 630 : '' 631 ]) 632 ->addRow([_('Number of templates'), 633 $status['has_status'] ? $status['hosts_count_template'] : '', '' 634 ]); 635 636 $title = (new CSpan(_('Number of items (enabled/disabled/not supported)'))) 637 ->setTitle(_('Only items assigned to enabled hosts are counted')); 638 $table->addRow([$title, $status['has_status'] ? $status['items_count'] : '', 639 $status['has_status'] 640 ? [ 641 (new CSpan($status['items_count_monitored']))->addClass(ZBX_STYLE_GREEN), ' / ', 642 (new CSpan($status['items_count_disabled']))->addClass(ZBX_STYLE_RED), ' / ', 643 (new CSpan($status['items_count_not_supported']))->addClass(ZBX_STYLE_GREY) 644 ] 645 : '' 646 ]); 647 $title = (new CSpan(_('Number of triggers (enabled/disabled [problem/ok])'))) 648 ->setTitle(_('Only triggers assigned to enabled hosts and depending on enabled items are counted')); 649 $table->addRow([$title, $status['has_status'] ? $status['triggers_count'] : '', 650 $status['has_status'] 651 ? [ 652 $status['triggers_count_enabled'], ' / ', 653 $status['triggers_count_disabled'], ' [', 654 (new CSpan($status['triggers_count_on']))->addClass(ZBX_STYLE_RED), ' / ', 655 (new CSpan($status['triggers_count_off']))->addClass(ZBX_STYLE_GREEN), ']' 656 ] 657 : '' 658 ]); 659 $table->addRow([_('Number of users (online)'), $status['has_status'] ? $status['users_count'] : '', 660 $status['has_status'] ? (new CSpan($status['users_online']))->addClass(ZBX_STYLE_GREEN) : '' 661 ]); 662 if (CWebUser::getType() == USER_TYPE_SUPER_ADMIN) { 663 $table->addRow([_('Required server performance, new values per second'), 664 ($status['has_status'] && array_key_exists('vps_total', $status)) ? round($status['vps_total'], 2) : '', '' 665 ]); 666 } 667 668 // Check requirements. 669 if (CWebUser::getType() == USER_TYPE_SUPER_ADMIN) { 670 $setup = new CFrontendSetup(); 671 $reqs = $setup->checkRequirements(); 672 $reqs[] = $setup->checkSslFiles(); 673 674 foreach ($reqs as $req) { 675 if ($req['result'] == CFrontendSetup::CHECK_FATAL) { 676 $table->addRow( 677 (new CRow([$req['name'], $req['current'], $req['error']]))->addClass(ZBX_STYLE_RED) 678 ); 679 } 680 } 681 682 $db = DB::getDbBackend(); 683 684 if (!$db->checkEncoding()) { 685 $table->addRow( 686 (new CRow((new CCol($db->getWarning()))->setAttribute('colspan', 3)))->addClass(ZBX_STYLE_RED) 687 ); 688 } 689 } 690 691 // Warn if database history tables have not been upgraded. 692 global $DB; 693 694 if (!$DB['DOUBLE_IEEE754']) { 695 $table->addRow([ 696 _('Database history tables upgraded'), 697 (new CSpan(_('No')))->addClass(ZBX_STYLE_RED), 698 '' 699 ]); 700 } 701 702 return $table; 703} 704 705/** 706 * Generate table for dashboard triggers popup. 707 * 708 * @see makeSystemStatus 709 * 710 * @param array $problems 711 * @param string $problems[]['objectid'] 712 * @param int $problems[]['clock'] 713 * @param int $problems[]['ns'] 714 * @param array $problems[]['acknowledged'] 715 * @param array $problems[]['severity'] 716 * @param array $problems[]['suppression_data'] 717 * @param array $problems[]['tags'] 718 * @param string $problems[]['tags'][]['tag'] 719 * @param string $problems[]['tags'][]['value'] 720 * @param array $triggers 721 * @param string $triggers[<triggerid>]['expression'] 722 * @param string $triggers[<triggerid>]['description'] 723 * @param array $triggers[<triggerid>]['hosts'] 724 * @param string $triggers[<triggerid>]['hosts'][]['name'] 725 * @param string $triggers[<triggerid>]['opdata'] 726 * @param array $actions 727 * @param array $config 728 * @param array $filter 729 * @param array $filter['show_suppressed'] (optional) 730 * @param array $filter['show_timeline'] (optional) 731 * @param array $filter['show_opdata'] (optional) 732 * 733 * @return CTableInfo 734 */ 735function makeProblemsPopup(array $problems, array $triggers, array $actions, array $config, array $filter) { 736 $url_details = (new CUrl('tr_events.php')) 737 ->setArgument('triggerid', '') 738 ->setArgument('eventid', ''); 739 740 $header_time = new CColHeader([_('Time'), (new CSpan())->addClass(ZBX_STYLE_ARROW_DOWN)]); 741 742 $show_timeline = (array_key_exists('show_timeline', $filter) && $filter['show_timeline']); 743 $show_opdata = (array_key_exists('show_opdata', $filter)) ? $filter['show_opdata'] : OPERATIONAL_DATA_SHOW_NONE; 744 745 if ($show_timeline) { 746 $header = [ 747 $header_time->addClass(ZBX_STYLE_RIGHT), 748 (new CColHeader())->addClass(ZBX_STYLE_TIMELINE_TH), 749 (new CColHeader())->addClass(ZBX_STYLE_TIMELINE_TH) 750 ]; 751 } 752 else { 753 $header = [$header_time]; 754 } 755 756 $table = (new CTableInfo()) 757 ->setHeader(array_merge($header, [ 758 _('Info'), 759 _('Host'), 760 _('Problem'), 761 ($show_opdata == OPERATIONAL_DATA_SHOW_SEPARATELY) ? _('Operational data') : null, 762 _('Duration'), 763 _('Ack'), 764 _('Actions'), 765 _('Tags') 766 ])); 767 768 $today = strtotime('today'); 769 $last_clock = 0; 770 771 // Unset triggers, which missing in problems array. 772 if ($problems) { 773 $objectids = []; 774 775 foreach ($problems as $problem) { 776 $objectids[$problem['objectid']] = true; 777 } 778 779 $triggers = array_intersect_key($triggers, $objectids); 780 } 781 782 $triggers_hosts = getTriggersHostsList($triggers); 783 $triggers_hosts = makeTriggersHostsList($triggers_hosts); 784 785 $tags = makeTags($problems); 786 787 if (array_key_exists('show_suppressed', $filter) && $filter['show_suppressed']) { 788 CScreenProblem::addMaintenanceNames($problems); 789 } 790 791 foreach ($problems as $problem) { 792 $trigger = $triggers[$problem['objectid']]; 793 794 $url_details 795 ->setArgument('triggerid', $problem['objectid']) 796 ->setArgument('eventid', $problem['eventid']); 797 798 $cell_clock = ($problem['clock'] >= $today) 799 ? zbx_date2str(TIME_FORMAT_SECONDS, $problem['clock']) 800 : zbx_date2str(DATE_TIME_FORMAT_SECONDS, $problem['clock']); 801 $cell_clock = new CCol(new CLink($cell_clock, $url_details)); 802 803 if ($show_timeline) { 804 if ($last_clock != 0) { 805 CScreenProblem::addTimelineBreakpoint($table, $last_clock, $problem['clock'], ZBX_SORT_DOWN); 806 } 807 $last_clock = $problem['clock']; 808 809 $row = [ 810 $cell_clock->addClass(ZBX_STYLE_TIMELINE_DATE), 811 (new CCol()) 812 ->addClass(ZBX_STYLE_TIMELINE_AXIS) 813 ->addClass(ZBX_STYLE_TIMELINE_DOT), 814 (new CCol())->addClass(ZBX_STYLE_TIMELINE_TD) 815 ]; 816 } 817 else { 818 $row = [ 819 $cell_clock 820 ->addClass(ZBX_STYLE_NOWRAP) 821 ->addClass(ZBX_STYLE_RIGHT) 822 ]; 823 } 824 825 $info_icons = []; 826 if (array_key_exists('suppression_data', $problem) && $problem['suppression_data']) { 827 $info_icons[] = makeSuppressedProblemIcon($problem['suppression_data']); 828 } 829 830 // operational data 831 $opdata = null; 832 if ($show_opdata != OPERATIONAL_DATA_SHOW_NONE) { 833 834 if ($trigger['opdata'] === '') { 835 if ($show_opdata == OPERATIONAL_DATA_SHOW_SEPARATELY) { 836 $opdata = (new CCol(CScreenProblem::getLatestValues($trigger['items'])))->addClass('latest-values'); 837 } 838 } 839 else { 840 $opdata = CMacrosResolverHelper::resolveTriggerOpdata( 841 [ 842 'triggerid' => $trigger['triggerid'], 843 'expression' => $trigger['expression'], 844 'opdata' => $trigger['opdata'], 845 'clock' => $problem['clock'], 846 'ns' => $problem['ns'] 847 ], 848 [ 849 'events' => true, 850 'html' => true 851 ] 852 ); 853 854 if ($show_opdata == OPERATIONAL_DATA_SHOW_SEPARATELY) { 855 $opdata = (new CCol($opdata)) 856 ->addClass('opdata') 857 ->addClass(ZBX_STYLE_WORDWRAP); 858 } 859 } 860 } 861 862 // Create acknowledge link. 863 $is_acknowledged = ($problem['acknowledged'] == EVENT_ACKNOWLEDGED); 864 $problem_update_link = (new CLink($is_acknowledged ? _('Yes') : _('No'))) 865 ->addClass($is_acknowledged ? ZBX_STYLE_GREEN : ZBX_STYLE_RED) 866 ->addClass(ZBX_STYLE_LINK_ALT) 867 ->onClick('acknowledgePopUp('.json_encode(['eventids' => [$problem['eventid']]]).', this);'); 868 869 $table->addRow(array_merge($row, [ 870 makeInformationList($info_icons), 871 $triggers_hosts[$trigger['triggerid']], 872 getSeverityCell($problem['severity'], null, 873 (($show_opdata == OPERATIONAL_DATA_SHOW_WITH_PROBLEM && $opdata) 874 ? [$problem['name'], ' (', $opdata, ')'] 875 : $problem['name'] 876 ) 877 ), 878 ($show_opdata == OPERATIONAL_DATA_SHOW_SEPARATELY) ? $opdata : null, 879 zbx_date2age($problem['clock']), 880 $problem_update_link, 881 makeEventActionsIcons($problem['eventid'], $actions['all_actions'], $actions['users'], $config), 882 $tags[$problem['eventid']] 883 ])); 884 } 885 886 return $table; 887} 888