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/forms.inc.php'; 24require_once dirname(__FILE__).'/include/correlation.inc.php'; 25 26$page['title'] = _('Event correlation rules'); 27$page['file'] = 'correlation.php'; 28$page['scripts'] = ['multiselect.js']; 29 30require_once dirname(__FILE__).'/include/page_header.php'; 31// VAR TYPE OPTIONAL FLAGS VALIDATION EXCEPTION 32$fields = [ 33 'correlationid' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, 'isset({form}) && {form} == "update"'], 34 'name' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, 'isset({add}) || isset({update})', 35 _('Name') 36 ], 37 'description' => [T_ZBX_STR, O_OPT, null, null, null], 38 'evaltype' => [T_ZBX_INT, O_OPT, null, 39 IN([CONDITION_EVAL_TYPE_AND_OR, CONDITION_EVAL_TYPE_AND, 40 CONDITION_EVAL_TYPE_OR, CONDITION_EVAL_TYPE_EXPRESSION 41 ]), 42 'isset({add}) || isset({update})' 43 ], 44 'formula' => [T_ZBX_STR, O_OPT, null, null, 'isset({add}) || isset({update})'], 45 'status' => [T_ZBX_INT, O_OPT, null, 46 IN([ZBX_CORRELATION_ENABLED, ZBX_CORRELATION_DISABLED]), 47 null 48 ], 49 'g_correlationid' => [T_ZBX_INT, O_OPT, null, DB_ID, 'isset({action})'], 50 'conditions' => [null, O_OPT, null, null, null], 51 'new_condition' => [null, O_OPT, null, null, 'isset({add_condition})'], 52 'operations' => [null, O_OPT, null, null, null], 53 'edit_operationid' => [T_ZBX_STR, O_OPT, P_ACT, null, null], 54 'new_operation' => [null, O_OPT, null, null, null], 55 // actions 56 'action' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, 57 IN('"correlation.massdelete","correlation.massdisable","correlation.massenable"'), 58 null 59 ], 60 'add_condition' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 61 'add_operation' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 62 'add' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 63 'update' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 64 'delete' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 65 'cancel' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 66 'form' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 67 'form_refresh' => [T_ZBX_INT, O_OPT, null, null, null], 68 // filter 69 'filter_set' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 70 'filter_rst' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 71 'filter_name' => [T_ZBX_STR, O_OPT, null, null, null], 72 'filter_status' => [T_ZBX_INT, O_OPT, null, 73 IN([-1, ZBX_CORRELATION_ENABLED, ZBX_CORRELATION_DISABLED]), 74 null 75 ], 76 // sort and sortorder 77 'sort' => [T_ZBX_STR, O_OPT, P_SYS, IN('"name","status"'), null], 78 'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null] 79]; 80 81check_fields($fields); 82 83$correlationid = getRequest('correlationid'); 84 85if ($correlationid !== null) { 86 $correlation = API::Correlation()->get([ 87 'output' => [], 88 'correlationids' => [$correlationid], 89 'editable' => true 90 ]); 91 92 if (!$correlation) { 93 access_deny(); 94 } 95} 96 97if (hasRequest('action')) { 98 $correlations = API::Correlation()->get([ 99 'output' => [], 100 'correlationids' => getRequest('g_correlationid'), 101 'editable' => true 102 ]); 103 104 if (count($correlations) != count(getRequest('g_correlationid'))) { 105 uncheckTableRows(null, zbx_objectValues($correlations, 'correlationid')); 106 } 107} 108 109if (hasRequest('add') || hasRequest('update')) { 110 $correlation = [ 111 'name' => getRequest('name'), 112 'description' => getRequest('description'), 113 'status' => getRequest('status', ZBX_CORRELATION_DISABLED), 114 'operations' => getRequest('operations', []) 115 ]; 116 117 $filter = [ 118 'conditions' => getRequest('conditions', []), 119 'evaltype' => getRequest('evaltype') 120 ]; 121 122 if ($filter['evaltype'] == CONDITION_EVAL_TYPE_EXPRESSION) { 123 if (count($filter['conditions']) > 1) { 124 $filter['formula'] = getRequest('formula'); 125 } 126 else { 127 // If only one or no conditions are left, reset the evaltype to "and/or" and clear the formula. 128 $filter['formula'] = ''; 129 $filter['evaltype'] = CONDITION_EVAL_TYPE_AND_OR; 130 } 131 } 132 $correlation['filter'] = $filter; 133 134 if (hasRequest('update')) { 135 $correlation['correlationid'] = $correlationid; 136 137 $result = API::Correlation()->update($correlation); 138 139 $messageSuccess = _('Correlation updated'); 140 $messageFailed = _('Cannot update correlation'); 141 } 142 else { 143 $result = API::Correlation()->create($correlation); 144 145 $messageSuccess = _('Correlation added'); 146 $messageFailed = _('Cannot add correlation'); 147 } 148 149 if ($result) { 150 uncheckTableRows(); 151 unset($_REQUEST['form']); 152 } 153 show_messages($result, $messageSuccess, $messageFailed); 154} 155elseif (hasRequest('delete') && hasRequest('correlationid')) { 156 $result = API::Correlation()->delete([getRequest('correlationid')]); 157 158 if ($result) { 159 unset($_REQUEST['form'], $_REQUEST['correlationid']); 160 uncheckTableRows(); 161 } 162 show_messages($result, _('Correlation deleted'), _('Cannot delete correlation')); 163} 164elseif (hasRequest('add_condition') && hasRequest('new_condition')) { 165 $new_condition = getRequest('new_condition'); 166 167 if ($new_condition) { 168 $conditions = getRequest('conditions', []); 169 170 // Add formulaid to new condition, so we can sort conditions. 171 $used_formulaids = zbx_objectValues($conditions, 'formulaid'); 172 $new_condition['formulaid'] = CConditionHelper::getNextFormulaId($used_formulaids); 173 $used_formulaids[] = $new_condition['formulaid']; 174 175 // Check existing conditions and remove duplicate condition values. 176 $valid_conditions = []; 177 foreach ($conditions as $condition) { 178 $valid_conditions[] = $condition; 179 180 // Check if still exists in loop and if type is the same. Remove same values. 181 if (isset($new_condition) && $new_condition['type'] == $condition['type']) { 182 switch ($new_condition['type']) { 183 case ZBX_CORR_CONDITION_OLD_EVENT_TAG: 184 case ZBX_CORR_CONDITION_NEW_EVENT_TAG: 185 if ($new_condition['tag'] === $condition['tag']) { 186 unset($new_condition); 187 } 188 break; 189 190 case ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP: 191 foreach ($new_condition['groupids'] as $i => $groupid) { 192 if ($condition['groupid'] == $groupid) { 193 unset($new_condition['groupids'][$i]); 194 } 195 } 196 197 // If no group IDs are left, remove condition (adding will be skipped). 198 if (!$new_condition['groupids']) { 199 unset($new_condition); 200 } 201 break; 202 203 case ZBX_CORR_CONDITION_EVENT_TAG_PAIR: 204 if ($new_condition['oldtag'] === $condition['oldtag'] 205 && $new_condition['newtag'] === $condition['newtag']) { 206 unset($new_condition); 207 } 208 break; 209 210 case ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE: 211 case ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE: 212 if ($new_condition['tag'] === $condition['tag'] 213 && $new_condition['value'] === $condition['value']) { 214 unset($new_condition); 215 } 216 break; 217 } 218 } 219 } 220 221 // Check if new condition is valid (tags cannot be empty) and host group IDs must be valid. 222 if (isset($new_condition)) { 223 switch ($new_condition['type']) { 224 case ZBX_CORR_CONDITION_OLD_EVENT_TAG: 225 case ZBX_CORR_CONDITION_NEW_EVENT_TAG: 226 if ($new_condition['tag'] === '') { 227 error(_s('Incorrect value for field "%1$s": %2$s.', 'tag', _('cannot be empty'))); 228 show_error_message(_('Cannot add correlation condition')); 229 } 230 else { 231 $valid_conditions[] = $new_condition; 232 } 233 break; 234 235 case ZBX_CORR_CONDITION_NEW_EVENT_HOSTGROUP: 236 if (!$new_condition['groupids']) { 237 error(_s('Incorrect value for field "%1$s": %2$s.', 'groupid', _('cannot be empty'))); 238 show_error_message(_('Cannot add correlation condition')); 239 } 240 else { 241 foreach ($new_condition['groupids'] as $groupid) { 242 if ($groupid == 0) { 243 error(_s('Incorrect value for field "%1$s": %2$s.', 'groupid', _('cannot be empty'))); 244 show_error_message(_('Cannot add correlation condition')); 245 } 246 else { 247 $valid_conditions[] = [ 248 'type' => $new_condition['type'], 249 'operator' => $new_condition['operator'], 250 'formulaid' => $new_condition['formulaid'], 251 'groupid' => $groupid 252 ]; 253 254 $new_condition['formulaid'] = CConditionHelper::getNextFormulaId($used_formulaids); 255 $used_formulaids[] = $new_condition['formulaid']; 256 } 257 } 258 } 259 break; 260 261 case ZBX_CORR_CONDITION_EVENT_TAG_PAIR: 262 if ($new_condition['oldtag'] === '') { 263 error(_s('Incorrect value for field "%1$s": %2$s.', 'oldtag', _('cannot be empty'))); 264 show_error_message(_('Cannot add correlation condition')); 265 } 266 elseif ($new_condition['newtag'] === '') { 267 error(_s('Incorrect value for field "%1$s": %2$s.', 'newtag', _('cannot be empty'))); 268 show_error_message(_('Cannot add correlation condition')); 269 } 270 else { 271 $valid_conditions[] = $new_condition; 272 unset($_REQUEST['new_condition']['oldtag']); 273 unset($_REQUEST['new_condition']['newtag']); 274 } 275 break; 276 277 case ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE: 278 case ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE: 279 if ($new_condition['tag'] === '') { 280 error(_s('Incorrect value for field "%1$s": %2$s.', 'tag', _('cannot be empty'))); 281 show_error_message(_('Cannot add correlation condition')); 282 } 283 else { 284 $valid_conditions[] = $new_condition; 285 unset($_REQUEST['new_condition']['value']); 286 } 287 break; 288 } 289 } 290 291 $_REQUEST['conditions'] = $valid_conditions; 292 } 293} 294elseif (hasRequest('add_operation') && hasRequest('new_operation')) { 295 $new_operation = getRequest('new_operation'); 296 $result = true; 297 298 $_REQUEST['operations'] = getRequest('operations', []); 299 300 $uniqOperations = [ 301 ZBX_CORR_OPERATION_CLOSE_OLD => 0, 302 ZBX_CORR_OPERATION_CLOSE_NEW => 0 303 ]; 304 305 if (array_key_exists($new_operation['type'], $uniqOperations)) { 306 $uniqOperations[$new_operation['type']]++; 307 308 foreach ($_REQUEST['operations'] as $operationId => $operation) { 309 if (array_key_exists($operation['type'], $uniqOperations) 310 && (!array_key_exists('id', $new_operation) || bccomp($new_operation['id'], $operationId) != 0)) { 311 $uniqOperations[$operation['type']]++; 312 } 313 } 314 315 if ($uniqOperations[$new_operation['type']] > 1) { 316 $result = false; 317 error(_s('Operation "%s" already exists.', corrOperationTypes($new_operation['type']))); 318 show_messages(); 319 } 320 } 321 322 if ($result) { 323 if (array_key_exists('id', $new_operation)) { 324 $_REQUEST['operations'][$new_operation['id']] = $new_operation; 325 } 326 else { 327 $_REQUEST['operations'][] = $new_operation; 328 } 329 330 CArrayHelper::sort($_REQUEST['operations'], ['type']); 331 } 332 333 unset($_REQUEST['new_operation']); 334} 335elseif (hasRequest('action') 336 && str_in_array(getRequest('action'), ['correlation.massenable', 'correlation.massdisable'])) { 337 338 $enable = (getRequest('action') === 'correlation.massenable'); 339 $status = $enable ? ZBX_CORRELATION_ENABLED : ZBX_CORRELATION_DISABLED; 340 341 $correlations_to_update = []; 342 foreach (getRequest('g_correlationid') as $g_correlationid) { 343 $correlations_to_update[] = [ 344 'correlationid' => $g_correlationid, 345 'status' => $status 346 ]; 347 } 348 349 $result = API::Correlation()->update($correlations_to_update); 350 $updated = 0; 351 352 if ($result) { 353 $updated = count($result['correlationids']); 354 } 355 356 $messageSuccess = $enable 357 ? _n('Correlation enabled', 'Correlations enabled', $updated) 358 : _n('Correlation disabled', 'Correlations disabled', $updated); 359 $messageFailed = $enable 360 ? _n('Cannot enable correlation', 'Cannot enable correlations', $updated) 361 : _n('Cannot disable correlation', 'Cannot disable correlations', $updated); 362 363 if ($result) { 364 uncheckTableRows(); 365 } 366 show_messages($result, $messageSuccess, $messageFailed); 367} 368elseif (hasRequest('action') && getRequest('action') === 'correlation.massdelete') { 369 $result = API::Correlation()->delete(getRequest('g_correlationid')); 370 371 if ($result) { 372 uncheckTableRows(); 373 } 374 show_messages($result, _('Selected correlations deleted'), _('Cannot delete selected correlations')); 375} 376 377/* 378 * Display 379 */ 380show_messages(); 381 382$config = select_config(); 383 384if (hasRequest('form')) { 385 $data = [ 386 'form' => getRequest('form'), 387 'correlationid' => $correlationid, 388 'new_condition' => getRequest('new_condition', []), 389 'new_operation' => getRequest('new_operation', ['type' => null]), 390 'config' => $config 391 ]; 392 393 if ($correlationid !== null) { 394 $data['correlation'] = API::Correlation()->get([ 395 'output' => ['correlationid', 'name', 'description', 'status'], 396 'correlationids' => [$correlationid], 397 'selectFilter' => ['formula', 'conditions', 'evaltype', 'eval_formula'], 398 'selectOperations' => ['type'], 399 'editable' => true 400 ]); 401 $data['correlation'] = reset($data['correlation']); 402 } 403 404 if (isset($data['correlation']['correlationid']) && !hasRequest('form_refresh')) { 405 CArrayHelper::sort($data['correlation']['operations'], ['type']); 406 } 407 else { 408 $data['correlation']['name'] = getRequest('name'); 409 $data['correlation']['description'] = getRequest('description'); 410 $data['correlation']['status'] = getRequest('status', hasRequest('form_refresh') ? 1 : 0); 411 $data['correlation']['operations'] = getRequest('operations', []); 412 $data['correlation']['filter']['evaltype'] = getRequest('evaltype'); 413 $data['correlation']['filter']['formula'] = getRequest('formula'); 414 $data['correlation']['filter']['conditions'] = getRequest('conditions', []); 415 } 416 417 $data['allowedConditions'] = corrConditionTypes(); 418 $data['allowedOperations'] = corrOperationTypes(); 419 420 if (!hasRequest('add_condition')) { 421 $data['correlation']['filter']['conditions'] = CConditionHelper::sortConditionsByFormulaId( 422 $data['correlation']['filter']['conditions'] 423 ); 424 } 425 426 if ($data['new_condition']) { 427 switch ($data['new_condition']['type']) { 428 case ZBX_CORR_CONDITION_EVENT_TAG_PAIR: 429 if (!array_key_exists('oldtag', $data['new_condition'])) { 430 $data['new_condition']['oldtag'] = ''; 431 } 432 433 if (!array_key_exists('newtag', $data['new_condition'])) { 434 $data['new_condition']['newtag'] = ''; 435 } 436 break; 437 438 case ZBX_CORR_CONDITION_OLD_EVENT_TAG_VALUE: 439 case ZBX_CORR_CONDITION_NEW_EVENT_TAG_VALUE: 440 if (!array_key_exists('value', $data['new_condition'])) { 441 $data['new_condition']['value'] = ''; 442 } 443 break; 444 } 445 } 446 else { 447 $data['new_condition'] = [ 448 'type' => ZBX_CORR_CONDITION_OLD_EVENT_TAG, 449 'operator' => CONDITION_OPERATOR_EQUAL, 450 'tag' => '' 451 ]; 452 } 453 454 // Render view. 455 $correlationView = new CView('configuration.correlation.edit', $data); 456 $correlationView->render(); 457 $correlationView->show(); 458} 459else { 460 $sortField = getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'name')); 461 $sortOrder = getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP)); 462 463 CProfile::update('web.'.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR); 464 CProfile::update('web.'.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR); 465 466 // filter 467 if (hasRequest('filter_set')) { 468 CProfile::update('web.correlation.filter_name', getRequest('filter_name', ''), PROFILE_TYPE_STR); 469 CProfile::update('web.correlation.filter_status', getRequest('filter_status', -1), PROFILE_TYPE_INT); 470 } 471 elseif (hasRequest('filter_rst')) { 472 CProfile::delete('web.correlation.filter_name'); 473 CProfile::delete('web.correlation.filter_status'); 474 } 475 476 $filter = [ 477 'name' => CProfile::get('web.correlation.filter_name', ''), 478 'status' => CProfile::get('web.correlation.filter_status', -1) 479 ]; 480 481 $data = [ 482 'sort' => $sortField, 483 'sortorder' => $sortOrder, 484 'filter' => $filter, 485 'config' => $config, 486 'profileIdx' => 'web.correlation.filter', 487 'active_tab' => CProfile::get('web.correlation.filter.active', 1) 488 ]; 489 490 $data['correlations'] = API::Correlation()->get([ 491 'output' => ['correlationid', 'name', 'description', 'status'], 492 'search' => [ 493 'name' => ($filter['name'] === '') ? null : $filter['name'] 494 ], 495 'filter' => [ 496 'status' => ($filter['status'] == -1) ? null : $filter['status'] 497 ], 498 'selectFilter' => ['formula', 'conditions', 'evaltype', 'eval_formula'], 499 'selectOperations' => ['type'], 500 'editable' => true, 501 'sortfield' => $sortField, 502 'limit' => $config['search_limit'] + 1 503 ]); 504 505 // sorting && paging 506 order_result($data['correlations'], $sortField, $sortOrder); 507 $data['paging'] = getPagingLine($data['correlations'], $sortOrder, new CUrl('correlation.php')); 508 509 // Render view. 510 $correlationView = new CView('configuration.correlation.list', $data); 511 $correlationView->render(); 512 $correlationView->show(); 513} 514 515require_once dirname(__FILE__).'/include/page_footer.php'; 516