Skip to content

Commit 33dbd98

Browse files
Calvariolaf
andauthored
Fix Alert diff, Add new AlertStatus changed, Fix AlertLog UI not showing the correct details (librenms#16313)
* Fix alert diff and add new status changed * Fix for styleci * Fix for styleci2 * Fix PHP Static Analysis * Fix for styleci3 * Fix PHP Static 2 * Typo * Fix the UI as it wouldn't fetch the correct details * Fix the UI for clear/resolved * Ignore PHPStan as dbFetchRows is already in use in this file and some improvements for the eventlog UI variable names * Typo * A more robust method of comparison * Fix styleci * Fix rename missing * Fix PHP Static Analysis * Fix typo when AlertState Better * Add failsafe and improve diffBetweenFaults * Fix style & PHP Static Analysis * Add back the default $state * Remove the else as we need to let go a "not changed" * Change UI "title" for the modifications * Fix doc * Fix merge conflict * Fake rollback for conflict * Update again for conflict * Update again for conflict * Rename "got changed" to "changed" * Some language changes --------- Co-authored-by: Neil Lathwood <[email protected]>
1 parent fdc3036 commit 33dbd98

File tree

15 files changed

+313
-158
lines changed

15 files changed

+313
-158
lines changed

LibreNMS/Alert/RunAlerts.php

Lines changed: 104 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ public function describeAlert($alert)
141141
$obj['title'] .= ' Has worsened';
142142
} elseif ($alert['state'] == AlertState::BETTER) {
143143
$obj['title'] .= ' Has improved';
144+
} elseif ($alert['state'] == AlertState::CHANGED) {
145+
$obj['title'] .= ' changed';
144146
}
145147

146148
foreach ($extra['rule'] as $incident) {
@@ -308,28 +310,45 @@ public function runFollowUp()
308310
}
309311
$chk = dbFetchRows($alert['query'], [$alert['device_id']]);
310312
//make sure we can json_encode all the datas later
311-
$cnt = count($chk);
312-
for ($i = 0; $i < $cnt; $i++) {
313+
$current_alert_count = count($chk);
314+
for ($i = 0; $i < $current_alert_count; $i++) {
313315
if (isset($chk[$i]['ip'])) {
314316
$chk[$i]['ip'] = inet6_ntop($chk[$i]['ip']);
315317
}
316318
}
317319
$alert['details']['rule'] ??= []; // if details.rule is missing, set it to an empty array
318-
$o = count($alert['details']['rule']);
319-
$n = count($chk);
320320
$ret = 'Alert #' . $alert['id'];
321321
$state = AlertState::CLEAR;
322-
if ($n > $o) {
323-
$ret .= ' Worsens';
322+
323+
// Get the added and resolved items
324+
[$added_diff, $resolved_diff] = $this->diffBetweenFaults($alert['details']['rule'], $chk);
325+
$previous_alert_count = count($alert['details']['rule']);
326+
327+
if (! empty($added_diff) && ! empty($resolved_diff)) {
328+
$ret .= ' Changed';
329+
$state = AlertState::CHANGED;
330+
$alert['details']['diff'] = ['added' => $added_diff, 'resolved' => $resolved_diff];
331+
} elseif (! empty($added_diff)) {
332+
$ret .= ' Worse';
324333
$state = AlertState::WORSE;
325-
$alert['details']['diff'] = array_diff($chk, $alert['details']['rule']);
326-
} elseif ($n < $o) {
327-
$ret .= ' Betters';
334+
$alert['details']['diff'] = ['added' => $added_diff];
335+
} elseif (! empty($resolved_diff)) {
336+
$ret .= ' Better';
328337
$state = AlertState::BETTER;
329-
$alert['details']['diff'] = array_diff($alert['details']['rule'], $chk);
338+
$alert['details']['diff'] = ['resolved' => $resolved_diff];
339+
// Failsafe if the diff didn't return any results
340+
} elseif ($current_alert_count > $previous_alert_count) {
341+
$ret .= ' Worse';
342+
$state = AlertState::WORSE;
343+
Eventlog::log('Alert got worse but the diff was not, ensure that a "id" or "_id" field is available for rule ' . $alert['name'], $alert['device_id'], 'alert', Severity::Warning);
344+
// Failsafe if the diff didn't return any results
345+
} elseif ($current_alert_count < $previous_alert_count) {
346+
$ret .= ' Better';
347+
$state = AlertState::BETTER;
348+
Eventlog::log('Alert got better but the diff was not, ensure that a "id" or "_id" field is available for rule ' . $alert['name'], $alert['device_id'], 'alert', Severity::Warning);
330349
}
331350

332-
if ($state > AlertState::CLEAR && $n > 0) {
351+
if ($state > AlertState::CLEAR && $current_alert_count > 0) {
333352
$alert['details']['rule'] = $chk;
334353
if (dbInsert([
335354
'state' => $state,
@@ -340,12 +359,83 @@ public function runFollowUp()
340359
dbUpdate(['state' => $state, 'open' => 1, 'alerted' => 1], 'alerts', 'rule_id = ? && device_id = ?', [$alert['rule_id'], $alert['device_id']]);
341360
}
342361

343-
echo $ret . ' (' . $o . '/' . $n . ")\r\n";
362+
echo $ret . ' (' . $previous_alert_count . '/' . $current_alert_count . ")\r\n";
344363
}
345364
}
346365
}
347366
}
348367

368+
/**
369+
* Extract the fields that are used to identify the elements in the array of a "fault"
370+
*
371+
* @param array $element
372+
* @return array
373+
*/
374+
private function extractIdFieldsForFault($element)
375+
{
376+
return array_filter(array_keys($element), function ($key) {
377+
// Exclude location_id as it is not relevant for the comparison
378+
return ($key === 'id' || strpos($key, '_id')) !== false && $key !== 'location_id';
379+
});
380+
}
381+
382+
/**
383+
* Generate a comparison key for an element based on the fields that identify it for a "fault"
384+
*
385+
* @param array $element
386+
* @param array $idFields
387+
* @return string
388+
*/
389+
private function generateComparisonKeyForFault($element, $idFields)
390+
{
391+
$keyParts = [];
392+
foreach ($idFields as $field) {
393+
$keyParts[] = isset($element[$field]) ? $element[$field] : '';
394+
}
395+
396+
return implode('|', $keyParts);
397+
}
398+
399+
/**
400+
* Find new elements in the array for faults
401+
* PHP array_diff is not working well for it
402+
*
403+
* @param array $array1
404+
* @param array $array2
405+
* @return array [$added, $removed]
406+
*/
407+
private function diffBetweenFaults($array1, $array2)
408+
{
409+
$array1_keys = [];
410+
$added_elements = [];
411+
$removed_elements = [];
412+
413+
// Create associative array for quick lookup of $array1 elements
414+
foreach ($array1 as $element1) {
415+
$element1_ids = $this->extractIdFieldsForFault($element1);
416+
$element1_key = $this->generateComparisonKeyForFault($element1, $element1_ids);
417+
$array1_keys[$element1_key] = $element1;
418+
}
419+
420+
// Iterate through $array2 and determine added elements
421+
foreach ($array2 as $element2) {
422+
$element2_ids = $this->extractIdFieldsForFault($element2);
423+
$element2_key = $this->generateComparisonKeyForFault($element2, $element2_ids);
424+
425+
if (! isset($array1_keys[$element2_key])) {
426+
$added_elements [] = $element2;
427+
} else {
428+
// Remove matched elements
429+
unset($array1_keys[$element2_key]);
430+
}
431+
}
432+
433+
// Remaining elements in $array1_keys are the removed elements
434+
$removed_elements = array_values($array1_keys);
435+
436+
return [$added_elements, $removed_elements];
437+
}
438+
349439
public function loadAlerts($where)
350440
{
351441
$alerts = [];
@@ -439,7 +529,7 @@ public function runAlerts()
439529
}
440530
}
441531

442-
if (in_array($alert['state'], [AlertState::ACTIVE, AlertState::WORSE, AlertState::BETTER]) && ! empty($rextra['count']) && ($rextra['count'] == -1 || $alert['details']['count']++ < $rextra['count'])) {
532+
if (in_array($alert['state'], [AlertState::ACTIVE, AlertState::WORSE, AlertState::BETTER, AlertState::CHANGED]) && ! empty($rextra['count']) && ($rextra['count'] == -1 || $alert['details']['count']++ < $rextra['count'])) {
443533
// We don't want -1 alert rule count alarms to get muted because of the current alert count
444534
if ($alert['details']['count'] < $rextra['count'] || $rextra['count'] == -1) {
445535
$noacc = true;
@@ -556,6 +646,7 @@ public function alertLog($result, $obj, $transport)
556646
AlertState::ACKNOWLEDGED => 'acknowledgment',
557647
AlertState::WORSE => 'worsened',
558648
AlertState::BETTER => 'improved',
649+
AlertState::CHANGED => 'changed',
559650
];
560651

561652
$severity = match ($obj['state']) {

LibreNMS/Alert/Transport.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ public static function getColorForState($state)
9292
AlertState::ACKNOWLEDGED => Config::get('alert_colour.acknowledged'),
9393
AlertState::WORSE => Config::get('alert_colour.worse'),
9494
AlertState::BETTER => Config::get('alert_colour.better'),
95+
AlertState::CHANGED => Config::get('alert_colour.changed'),
9596
];
9697

9798
return isset($colors[$state]) ? $colors[$state] : '#337AB7';

LibreNMS/Enum/AlertState.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ abstract class AlertState
3232
const ACKNOWLEDGED = 2;
3333
const WORSE = 3;
3434
const BETTER = 4;
35+
const CHANGED = 5;
3536
const RECOVERED = 0;
3637
}

app/Http/Controllers/Widgets/AlertsController.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public function getSettingsView(Request $request)
7070
'acknowledged' => '2',
7171
'worse' => '3',
7272
'better' => '4',
73+
'changed' => '5',
7374
];
7475

7576
return view('widgets.settings.alerts', $data);

doc/API/Alerts.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ Route: `/api/v0/alerts/:id`
4545

4646
- id is the alert id, you can obtain a list of alert ids from [`list_alerts`](#list_alerts).
4747
- note is the note to add to the alert
48-
- until_clear is a boolean and if set to false, the alert will re-alert if it worsens/betters.
48+
- until_clear is a boolean and if set to false, the alert will re-alert if it gets worse/better or changes.
4949

5050
Input:
5151

doc/Alerting/Transports.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ websrv08.dc4.eu.corp.example.net gets shortened to websrv08.dc4.eu.cen).
870870
- Sensu will reject rules with special characters - the Transport will attempt
871871
to fix up rule names, but it's best to stick to letters, numbers and spaces
872872
- The transport only deals in absolutes - it ignores the got worse/got better
873-
states
873+
/changed states
874874
- The agent will buffer alerts, but LibreNMS will not - if your agent is
875875
offline, alerts will be dropped
876876
- There is no backchannel between Sensu and LibreNMS - if you make changes in

doc/Alerting/index.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ alerts. Click this icon to acknowledge the alert.
3636
until the alert clears. Click this icon to un-acknowledge the alert.
3737

3838
![unack alert until fault worsens](img/nunack.png) This alert is
39-
currently acknowledged until the alert worsens or gets
40-
better, at which stage it will be automatically unacknowledged and
39+
currently acknowledged until the alert worsens, gets
40+
better or changes, at which stage it will be automatically unacknowledged and
4141
alerts will resume. Click this icon to un-acknowledge the alert.
4242

4343
### Notes

includes/html/common/alert-log.inc.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
// 'Acknowledged' => 2,
2626
'Worse' => 3,
2727
'Better' => 4,
28+
'Changed' => 5,
2829
];
2930

3031
$alert_severities = [
@@ -124,6 +125,7 @@
124125
<option value="1">Alert</option> \
125126
<option value="3">Worse</option> \
126127
<option value="4">Better</option> \
128+
<option value="5">Changed</option> \
127129
</select> \
128130
</div> \
129131
<div class="form-group"> \

includes/html/common/alerts.inc.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'acknowledged' => 2,
2323
'worse' => 3,
2424
'better' => 4,
25+
'changed' => 5,
2526
];
2627

2728
$alert_severities = [

0 commit comments

Comments
 (0)