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/** 23 * Returns the names of supported event sources. 24 * 25 * If the $source parameter is passed, returns the name of the specific source, otherwise - returns an array of all 26 * supported sources. 27 * 28 * @param int $source 29 * 30 * @return array|string 31 */ 32function eventSource($source = null) { 33 $sources = [ 34 EVENT_SOURCE_TRIGGERS => _('trigger'), 35 EVENT_SOURCE_DISCOVERY => _('discovery'), 36 EVENT_SOURCE_AUTO_REGISTRATION => _('auto registration'), 37 EVENT_SOURCE_INTERNAL => _x('internal', 'event source') 38 ]; 39 40 if ($source === null) { 41 return $sources; 42 } 43 elseif (isset($sources[$source])) { 44 return $sources[$source]; 45 } 46 else { 47 return _('Unknown'); 48 } 49} 50 51/** 52 * Returns the names of supported event objects. 53 * 54 * If the $source parameter is passed, returns the name of the specific object, otherwise - returns an array of all 55 * supported objects. 56 * 57 * @param int $object 58 * 59 * @return array|string 60 */ 61function eventObject($object = null) { 62 $objects = [ 63 EVENT_OBJECT_TRIGGER => _('trigger'), 64 EVENT_OBJECT_DHOST => _('discovered host'), 65 EVENT_OBJECT_DSERVICE => _('discovered service'), 66 EVENT_OBJECT_AUTOREGHOST => _('auto-registered host'), 67 EVENT_OBJECT_ITEM => _('item'), 68 EVENT_OBJECT_LLDRULE => _('low-level discovery rule') 69 ]; 70 71 if ($object === null) { 72 return $objects; 73 } 74 elseif (isset($objects[$object])) { 75 return $objects[$object]; 76 } 77 else { 78 return _('Unknown'); 79 } 80} 81 82/** 83 * Returns all supported event source-object pairs. 84 * 85 * @return array 86 */ 87function eventSourceObjects() { 88 return [ 89 ['source' => EVENT_SOURCE_TRIGGERS, 'object' => EVENT_OBJECT_TRIGGER], 90 ['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DHOST], 91 ['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DSERVICE], 92 ['source' => EVENT_SOURCE_AUTO_REGISTRATION, 'object' => EVENT_OBJECT_AUTOREGHOST], 93 ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_TRIGGER], 94 ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_ITEM], 95 ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_LLDRULE] 96 ]; 97} 98 99function get_events_unacknowledged($db_element, $value_trigger = null, $value_event = null, $ack = false) { 100 $elements = ['hosts' => [], 'hosts_groups' => [], 'triggers' => []]; 101 get_map_elements($db_element, $elements); 102 103 if (empty($elements['hosts_groups']) && empty($elements['hosts']) && empty($elements['triggers'])) { 104 return 0; 105 } 106 107 $config = select_config(); 108 $options = [ 109 'output' => ['triggerid'], 110 'monitored' => 1, 111 'skipDependent' => 1, 112 'limit' => $config['search_limit'] + 1 113 ]; 114 if (!is_null($value_trigger)) { 115 $options['filter'] = ['value' => $value_trigger]; 116 } 117 if (!empty($elements['hosts_groups'])) { 118 $options['groupids'] = array_unique($elements['hosts_groups']); 119 } 120 if (!empty($elements['hosts'])) { 121 $options['hostids'] = array_unique($elements['hosts']); 122 } 123 if (!empty($elements['triggers'])) { 124 $options['triggerids'] = array_unique($elements['triggers']); 125 } 126 $triggerids = API::Trigger()->get($options); 127 128 return API::Event()->get([ 129 'source' => EVENT_SOURCE_TRIGGERS, 130 'object' => EVENT_OBJECT_TRIGGER, 131 'countOutput' => true, 132 'objectids' => zbx_objectValues($triggerids, 'triggerid'), 133 'filter' => [ 134 'value' => $value_event, 135 'acknowledged' => $ack ? 1 : 0 136 ] 137 ]); 138} 139 140function get_next_event($currentEvent, array $eventList = []) { 141 $nextEvent = false; 142 143 foreach ($eventList as $event) { 144 // check only the events belonging to the same object 145 // find the event with the smallest eventid but greater than the current event id 146 if ($event['object'] == $currentEvent['object'] && bccomp($event['objectid'], $currentEvent['objectid']) == 0 147 && (bccomp($event['eventid'], $currentEvent['eventid']) === 1 148 && (!$nextEvent || bccomp($event['eventid'], $nextEvent['eventid']) === -1))) { 149 $nextEvent = $event; 150 } 151 } 152 if ($nextEvent) { 153 return $nextEvent; 154 } 155 156 $sql = 'SELECT e.*'. 157 ' FROM events e'. 158 ' WHERE e.source='.zbx_dbstr($currentEvent['source']). 159 ' AND e.object='.zbx_dbstr($currentEvent['object']). 160 ' AND e.objectid='.zbx_dbstr($currentEvent['objectid']). 161 ' AND e.clock>='.zbx_dbstr($currentEvent['clock']). 162 ' AND ((e.clock='.zbx_dbstr($currentEvent['clock']).' AND e.ns>'.$currentEvent['ns'].')'. 163 ' OR e.clock>'.zbx_dbstr($currentEvent['clock']).')'. 164 ' ORDER BY e.clock,e.eventid'; 165 return DBfetch(DBselect($sql, 1)); 166} 167 168function make_event_details($event, $trigger, $backurl) { 169 $config = select_config(); 170 $table = (new CTableInfo()) 171 ->addRow([ 172 _('Event'), 173 CMacrosResolverHelper::resolveEventDescription(array_merge($trigger, $event)) 174 ]) 175 ->addRow([ 176 _('Time'), 177 zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']) 178 ]); 179 180 if ($config['event_ack_enable']) { 181 // to make resulting link not have hint with acknowledges 182 $event['acknowledges'] = count($event['acknowledges']); 183 $table->addRow([_('Acknowledged'), getEventAckState($event, $backurl)]); 184 } 185 186 return $table; 187} 188 189function make_small_eventlist($startEvent, $backurl) { 190 $config = select_config(); 191 192 $table = (new CTableInfo()) 193 ->setHeader([ 194 _('Time'), 195 _('Status'), 196 _('Duration'), 197 _('Age'), 198 $config['event_ack_enable'] ? _('Ack') : null, // if we need to chow acks 199 _('Actions') 200 ]); 201 202 $clock = $startEvent['clock']; 203 204 $events = API::Event()->get([ 205 'source' => EVENT_SOURCE_TRIGGERS, 206 'object' => EVENT_OBJECT_TRIGGER, 207 'objectids' => $startEvent['objectid'], 208 'eventid_till' => $startEvent['eventid'], 209 'output' => API_OUTPUT_EXTEND, 210 'select_acknowledges' => API_OUTPUT_COUNT, 211 'sortfield' => ['clock', 'eventid'], 212 'sortorder' => ZBX_SORT_DOWN, 213 'limit' => 20 214 ]); 215 216 $sortFields = [ 217 ['field' => 'clock', 'order' => ZBX_SORT_DOWN], 218 ['field' => 'eventid', 'order' => ZBX_SORT_DOWN] 219 ]; 220 CArrayHelper::sort($events, $sortFields); 221 222 $actions = makeEventsActions(zbx_objectValues($events, 'eventid')); 223 224 foreach ($events as $event) { 225 $lclock = $clock; 226 $duration = zbx_date2age($lclock, $event['clock']); 227 $clock = $event['clock']; 228 229 if (bccomp($startEvent['eventid'],$event['eventid']) == 0 && $nextevent = get_next_event($event, $events)) { 230 $duration = zbx_date2age($nextevent['clock'], $clock); 231 } 232 elseif (bccomp($startEvent['eventid'], $event['eventid']) == 0) { 233 $duration = zbx_date2age($clock); 234 } 235 236 $eventStatusSpan = new CSpan(trigger_value2str($event['value'])); 237 238 // add colors and blinking to span depending on configuration and trigger parameters 239 addTriggerValueStyle( 240 $eventStatusSpan, 241 $event['value'], 242 $event['clock'], 243 $event['acknowledged'] 244 ); 245 246 $table->addRow([ 247 (new CLink( 248 zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']), 249 'tr_events.php?triggerid='.$event['objectid'].'&eventid='.$event['eventid'])) 250 ->addClass('action'), 251 $eventStatusSpan, 252 $duration, 253 zbx_date2age($event['clock']), 254 $config['event_ack_enable'] ? getEventAckState($event, $backurl) : null, 255 (new CCol(isset($actions[$event['eventid']]) ? $actions[$event['eventid']] : '')) 256 ->addClass(ZBX_STYLE_NOWRAP) 257 ]); 258 } 259 260 return $table; 261} 262 263/** 264 * Create table with trigger description and events. 265 * 266 * @param array $trigger An array of trigger data. 267 * @param string $trigger['triggerid'] Trigger ID to select events. 268 * @param string $trigger['description'] Trigger description. 269 * @param string $trigger['url'] Trigger URL. 270 * @param string $trigger['lastEvent']['eventid'] Last event ID 271 * @param string $backurl URL to return to. 272 * 273 * @return CDiv 274 */ 275function make_popup_eventlist($trigger, $backurl) { 276 // Show trigger description and URL. 277 $div = (new CDiv()); 278 279 if ($trigger['comments'] !== '') { 280 $div->addItem( 281 (new CDiv()) 282 ->addItem(zbx_str2links($trigger['comments'])) 283 ->addClass(ZBX_STYLE_OVERLAY_DESCR) 284 ); 285 } 286 287 if ($trigger['url'] !== '') { 288 $trigger_url = CHtmlUrlValidator::validate($trigger['url'], false) 289 ? $trigger['url'] 290 : 'javascript: alert(\''._s('Provided URL "%1$s" is invalid.', zbx_jsvalue($trigger['url'], false, false)). 291 '\');'; 292 293 $div->addItem( 294 (new CDiv()) 295 ->addItem(new CLink(CHTML::encode($trigger['url']), $trigger_url)) 296 ->addClass(ZBX_STYLE_OVERLAY_DESCR_URL) 297 ); 298 } 299 300 // Select and show events. 301 $config = select_config(); 302 303 $table = new CTableInfo(); 304 305 // If acknowledges are turned on, we show 'ack' column. 306 if ($config['event_ack_enable']) { 307 $table->setHeader([_('Time'), _('Status'), _('Duration'), _('Age'), _('Ack')]); 308 } 309 else { 310 $table->setHeader([_('Time'), _('Status'), _('Duration'), _('Age')]); 311 } 312 313 if ($trigger['lastEvent']) { 314 $events = API::Event()->get([ 315 'source' => EVENT_SOURCE_TRIGGERS, 316 'object' => EVENT_OBJECT_TRIGGER, 317 'output' => API_OUTPUT_EXTEND, 318 'objectids' => [$trigger['triggerid']], 319 'eventid_till' => $trigger['lastEvent']['eventid'], 320 'select_acknowledges' => API_OUTPUT_COUNT, 321 'sortfield' => ['clock', 'eventid'], 322 'sortorder' => ZBX_SORT_DOWN, 323 'limit' => ZBX_WIDGET_ROWS 324 ]); 325 326 $lclock = time(); 327 328 foreach ($events as $event) { 329 $duration = zbx_date2age($lclock, $event['clock']); 330 $lclock = $event['clock']; 331 332 $eventStatusSpan = new CSpan(trigger_value2str($event['value'])); 333 334 // add colors and blinking to span depending on configuration and trigger parameters 335 addTriggerValueStyle($eventStatusSpan, $event['value'], $event['clock'], $event['acknowledged']); 336 337 $table->addRow([ 338 zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']), 339 $eventStatusSpan, 340 $duration, 341 zbx_date2age($event['clock']), 342 $config['event_ack_enable'] ? getEventAckState($event, $backurl) : null 343 ]); 344 } 345 } 346 347 $div->addItem($table); 348 349 return $div; 350} 351 352/** 353 * Create element with event acknowledges info. 354 * If $event has subarray 'acknowledges', returned link will have hint with acknowledges. 355 * 356 * @param array $event event data 357 * @param int $event['acknowledged'] 358 * @param int $event['eventid'] 359 * @param int $event['objectid'] 360 * @param mixed $event['acknowledges'] 361 * @param string $backurl add url param to link with current page file name 362 * 363 * @return CLink 364 */ 365function getEventAckState($event, $backurl) { 366 if ($event['acknowledged'] == EVENT_ACKNOWLEDGED) { 367 $acknowledges_num = is_array($event['acknowledges']) ? count($event['acknowledges']) : $event['acknowledges']; 368 } 369 370 $link = 'zabbix.php?action=acknowledge.edit&eventids[]='.$event['eventid'].'&backurl='.urlencode($backurl); 371 372 if ($event['acknowledged'] == EVENT_ACKNOWLEDGED) { 373 $ack = (new CLink(_('Yes'), $link)) 374 ->addClass(ZBX_STYLE_LINK_ALT) 375 ->addClass(ZBX_STYLE_GREEN); 376 if (is_array($event['acknowledges'])) { 377 $ack->setHint(makeAckTab(array_slice($event['acknowledges'], 0, ZBX_WIDGET_ROWS)), '', false); 378 } 379 $ack = [$ack, CViewHelper::showNum($acknowledges_num)]; 380 } 381 else { 382 $ack = (new CLink(_('No'), $link)) 383 ->addClass(ZBX_STYLE_LINK_ALT) 384 ->addClass(ZBX_STYLE_RED); 385 } 386 387 return $ack; 388} 389 390function getLastEvents($options) { 391 if (!isset($options['limit'])) { 392 $options['limit'] = 15; 393 } 394 395 $triggerOptions = [ 396 'filter' => [], 397 'skipDependent' => 1, 398 'selectHosts' => ['hostid', 'name'], 399 'output' => API_OUTPUT_EXTEND, 400 'sortfield' => 'lastchange', 401 'sortorder' => ZBX_SORT_DOWN, 402 'limit' => $options['triggerLimit'] 403 ]; 404 405 $eventOptions = [ 406 'source' => EVENT_SOURCE_TRIGGERS, 407 'object' => EVENT_OBJECT_TRIGGER, 408 'output' => API_OUTPUT_EXTEND, 409 'sortfield' => ['clock', 'eventid'], 410 'sortorder' => ZBX_SORT_DOWN 411 ]; 412 413 if (isset($options['eventLimit'])) { 414 $eventOptions['limit'] = $options['eventLimit']; 415 } 416 417 if (isset($options['priority'])) { 418 $triggerOptions['filter']['priority'] = $options['priority']; 419 } 420 if (isset($options['monitored'])) { 421 $triggerOptions['monitored'] = $options['monitored']; 422 } 423 if (isset($options['lastChangeSince'])) { 424 $triggerOptions['lastChangeSince'] = $options['lastChangeSince']; 425 $eventOptions['time_from'] = $options['lastChangeSince']; 426 } 427 if (isset($options['value'])) { 428 $triggerOptions['filter']['value'] = $options['value']; 429 $eventOptions['value'] = $options['value']; 430 } 431 432 // triggers 433 $triggers = API::Trigger()->get($triggerOptions); 434 $triggers = zbx_toHash($triggers, 'triggerid'); 435 436 // events 437 $eventOptions['objectids'] = zbx_objectValues($triggers, 'triggerid'); 438 $events = API::Event()->get($eventOptions); 439 440 $sortClock = []; 441 $sortEvent = []; 442 foreach ($events as $enum => $event) { 443 if (!isset($triggers[$event['objectid']])) { 444 continue; 445 } 446 447 $events[$enum]['trigger'] = $triggers[$event['objectid']]; 448 $events[$enum]['host'] = reset($events[$enum]['trigger']['hosts']); 449 $sortClock[$enum] = $event['clock']; 450 $sortEvent[$enum] = $event['eventid']; 451 452 //expanding description for the state where event was 453 $merged_event = array_merge($event, $triggers[$event['objectid']]); 454 $events[$enum]['trigger']['description'] = CMacrosResolverHelper::resolveEventDescription($merged_event); 455 } 456 array_multisort($sortClock, SORT_DESC, $sortEvent, SORT_DESC, $events); 457 458 return $events; 459} 460