RunAlerts::runAlerts()   F
last analyzed

Complexity

Conditions 37
Paths 12301

Size

Total Lines 99
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 59
dl 0
loc 99
rs 0
c 0
b 0
f 0
cc 37
nc 12301
nop 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * RunAlerts.php
4
 *
5
 * Copyright (C) 2014 Daniel Preussker <[email protected]>
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 3 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, see <https://www.gnu.org/licenses/>.
18
 *
19
 * Original Code:
20
 * @author Daniel Preussker <[email protected]>
21
 * @copyright 2014 f0o, LibreNMS
22
 * @license GPL
23
 * @package LibreNMS
24
 * @subpackage Alerts
25
 *
26
 * Modified by:
27
 * @author Heath Barnhart <[email protected]>
28
 *
29
 */
30
31
namespace LibreNMS\Alert;
32
33
use App\Models\DevicePerf;
34
use LibreNMS\Config;
35
use LibreNMS\Enum\Alert;
36
use LibreNMS\Enum\AlertState;
37
use LibreNMS\Util\Time;
38
use Log;
39
40
class RunAlerts
41
{
42
    /**
43
     * Populate variables
44
     *
45
     * @param  string  $txt  Text with variables
46
     * @param  bool  $wrap  Wrap variable for text-usage (default: true)
47
     * @return string
48
     */
49
    public function populate($txt, $wrap = true)
50
    {
51
        preg_match_all('/%([\w\.]+)/', $txt, $m);
52
        foreach ($m[1] as $tmp) {
53
            $orig = $tmp;
54
            $rep = false;
55
            if ($tmp == 'key' || $tmp == 'value') {
56
                $rep = '$' . $tmp;
57
            } else {
58
                if (strstr($tmp, '.')) {
59
                    $tmp = explode('.', $tmp, 2);
60
                    $pre = '$' . $tmp[0];
61
                    $tmp = $tmp[1];
62
                } else {
63
                    $pre = '$obj';
64
                }
65
66
                $rep = $pre . "['" . str_replace('.', "']['", $tmp) . "']";
67
                if ($wrap) {
68
                    $rep = '{' . $rep . '}';
69
                }
70
            }
71
72
            $txt = str_replace('%' . $orig, $rep, $txt);
73
        }
74
75
        return $txt;
76
    }
77
78
    /**
79
     * Describe Alert
80
     *
81
     * @param  array  $alert  Alert-Result from DB
82
     * @return array|bool|string
83
     */
84
    public function describeAlert($alert)
85
    {
86
        $obj = [];
87
        $i = 0;
88
        $device = dbFetchRow('SELECT hostname, sysName, sysDescr, sysContact, os, type, ip, hardware, version, serial, features, purpose, notes, uptime, status, status_reason, locations.location FROM devices LEFT JOIN locations ON locations.id = devices.location_id WHERE device_id = ?', [$alert['device_id']]);
89
        $attribs = get_dev_attribs($alert['device_id']);
90
91
        $obj['hostname'] = $device['hostname'];
92
        $obj['sysName'] = $device['sysName'];
93
        $obj['sysDescr'] = $device['sysDescr'];
94
        $obj['sysContact'] = $device['sysContact'];
95
        $obj['os'] = $device['os'];
96
        $obj['type'] = $device['type'];
97
        $obj['ip'] = inet6_ntop($device['ip']);
98
        $obj['hardware'] = $device['hardware'];
99
        $obj['version'] = $device['version'];
100
        $obj['serial'] = $device['serial'];
101
        $obj['features'] = $device['features'];
102
        $obj['location'] = $device['location'];
103
        $obj['uptime'] = $device['uptime'];
104
        $obj['uptime_short'] = Time::formatInterval($device['uptime'], 'short');
105
        $obj['uptime_long'] = Time::formatInterval($device['uptime']);
106
        $obj['description'] = $device['purpose'];
107
        $obj['notes'] = $device['notes'];
108
        $obj['alert_notes'] = $alert['note'];
109
        $obj['device_id'] = $alert['device_id'];
110
        $obj['rule_id'] = $alert['rule_id'];
111
        $obj['id'] = $alert['id'];
112
        $obj['proc'] = $alert['proc'];
113
        $obj['status'] = $device['status'];
114
        $obj['status_reason'] = $device['status_reason'];
115
        if (can_ping_device($attribs)) {
116
            $ping_stats = DevicePerf::where('device_id', $alert['device_id'])->latest('timestamp')->first();
117
            $obj['ping_timestamp'] = $ping_stats->timestamp;
118
            $obj['ping_loss'] = $ping_stats->loss;
119
            $obj['ping_min'] = $ping_stats->min;
120
            $obj['ping_max'] = $ping_stats->max;
121
            $obj['ping_avg'] = $ping_stats->avg;
122
            $obj['debug'] = json_decode($ping_stats->debug, true);
123
        }
124
        $extra = $alert['details'];
125
126
        $tpl = new Template;
127
        $template = $tpl->getTemplate($obj);
128
129
        if ($alert['state'] >= AlertState::ACTIVE) {
130
            $obj['title'] = $template->title ?: 'Alert for device ' . $device['hostname'] . ' - ' . ($alert['name'] ? $alert['name'] : $alert['rule']);
131
            if ($alert['state'] == AlertState::ACKNOWLEDGED) {
132
                $obj['title'] .= ' got acknowledged';
133
            } elseif ($alert['state'] == AlertState::WORSE) {
134
                $obj['title'] .= ' got worse';
135
            } elseif ($alert['state'] == AlertState::BETTER) {
136
                $obj['title'] .= ' got better';
137
            }
138
139
            foreach ($extra['rule'] as $incident) {
140
                $i++;
141
                $obj['faults'][$i] = $incident;
142
                $obj['faults'][$i]['string'] = null;
143
                foreach ($incident as $k => $v) {
144
                    if (! empty($v) && $k != 'device_id' && (stristr($k, 'id') || stristr($k, 'desc') || stristr($k, 'msg')) && substr_count($k, '_') <= 1) {
145
                        $obj['faults'][$i]['string'] .= $k . ' = ' . $v . '; ';
146
                    }
147
                }
148
            }
149
            $obj['elapsed'] = $this->timeFormat(time() - strtotime($alert['time_logged']));
150
            if (! empty($extra['diff'])) {
151
                $obj['diff'] = $extra['diff'];
152
            }
153
        } elseif ($alert['state'] == AlertState::RECOVERED) {
154
            // Alert is now cleared
155
            $id = dbFetchRow('SELECT alert_log.id,alert_log.time_logged,alert_log.details FROM alert_log WHERE alert_log.state != ? && alert_log.state != ? && alert_log.rule_id = ? && alert_log.device_id = ? && alert_log.id < ? ORDER BY id DESC LIMIT 1', [AlertState::ACKNOWLEDGED, AlertState::RECOVERED, $alert['rule_id'], $alert['device_id'], $alert['id']]);
156
            if (empty($id['id'])) {
157
                return false;
158
            }
159
160
            $extra = [];
161
            if (! empty($id['details'])) {
162
                $extra = json_decode(gzuncompress($id['details']), true);
163
            }
164
165
            // Reset count to 0 so alerts will continue
166
            $extra['count'] = 0;
167
            dbUpdate(['details' => gzcompress(json_encode($id['details']), 9)], 'alert_log', 'id = ?', [$alert['id']]);
168
169
            $obj['title'] = $template->title_rec ?: 'Device ' . $device['hostname'] . ' recovered from ' . ($alert['name'] ? $alert['name'] : $alert['rule']);
170
            $obj['elapsed'] = $this->timeFormat(strtotime($alert['time_logged']) - strtotime($id['time_logged']));
171
            $obj['id'] = $id['id'];
172
            foreach ($extra['rule'] as $incident) {
173
                $i++;
174
                $obj['faults'][$i] = $incident;
175
                foreach ($incident as $k => $v) {
176
                    if (! empty($v) && $k != 'device_id' && (stristr($k, 'id') || stristr($k, 'desc') || stristr($k, 'msg')) && substr_count($k, '_') <= 1) {
177
                        $obj['faults'][$i]['string'] .= $k . ' => ' . $v . '; ';
178
                    }
179
                }
180
            }
181
        } else {
182
            return 'Unknown State';
183
        }//end if
184
        $obj['builder'] = $alert['builder'];
185
        $obj['uid'] = $alert['id'];
186
        $obj['alert_id'] = $alert['alert_id'];
187
        $obj['severity'] = $alert['severity'];
188
        $obj['rule'] = $alert['rule'];
189
        $obj['name'] = $alert['name'];
190
        $obj['timestamp'] = $alert['time_logged'];
191
        $obj['contacts'] = $extra['contacts'];
192
        $obj['state'] = $alert['state'];
193
        $obj['alerted'] = $alert['alerted'];
194
        $obj['template'] = $template;
195
196
        return $obj;
197
    }
198
199
    /**
200
     * Format Elapsed Time
201
     *
202
     * @param  int  $secs  Seconds elapsed
203
     * @return string
204
     */
205
    public function timeFormat($secs)
206
    {
207
        $bit = [
208
            'y' => $secs / 31556926 % 12,
209
            'w' => $secs / 604800 % 52,
210
            'd' => $secs / 86400 % 7,
211
            'h' => $secs / 3600 % 24,
212
            'm' => $secs / 60 % 60,
213
            's' => $secs % 60,
214
        ];
215
        $ret = [];
216
        foreach ($bit as $k => $v) {
217
            if ($v > 0) {
218
                $ret[] = $v . $k;
219
            }
220
        }
221
222
        if (empty($ret)) {
223
            return 'none';
224
        }
225
226
        return join(' ', $ret);
227
    }
228
229
    public function clearStaleAlerts()
230
    {
231
        $sql = 'SELECT `alerts`.`id` AS `alert_id`, `devices`.`hostname` AS `hostname` FROM `alerts` LEFT JOIN `devices` ON `alerts`.`device_id`=`devices`.`device_id`  RIGHT JOIN `alert_rules` ON `alerts`.`rule_id`=`alert_rules`.`id` WHERE `alerts`.`state`!=' . AlertState::CLEAR . ' AND `devices`.`hostname` IS NULL';
232
        foreach (dbFetchRows($sql) as $alert) {
233
            if (empty($alert['hostname']) && isset($alert['alert_id'])) {
234
                dbDelete('alerts', '`id` = ?', [$alert['alert_id']]);
235
                echo "Stale-alert: #{$alert['alert_id']}" . PHP_EOL;
236
            }
237
        }
238
    }
239
240
    /**
241
     * Re-Validate Rule-Mappings
242
     *
243
     * @param  int  $device_id  Device-ID
244
     * @param  int  $rule  Rule-ID
245
     * @return bool
246
     */
247
    public function isRuleValid($device_id, $rule)
248
    {
249
        global $rulescache;
250
        if (empty($rulescache[$device_id]) || ! isset($rulescache[$device_id])) {
251
            foreach (AlertUtil::getRules($device_id) as $chk) {
252
                $rulescache[$device_id][$chk['id']] = true;
253
            }
254
        }
255
256
        if ($rulescache[$device_id][$rule] === true) {
257
            return true;
258
        }
259
260
        return false;
261
    }
262
263
    /**
264
     * Issue Alert-Object
265
     *
266
     * @param  array  $alert
267
     * @return bool
268
     */
269
    public function issueAlert($alert)
270
    {
271
        if (Config::get('alert.fixed-contacts') == false) {
272
            if (empty($alert['query'])) {
273
                $alert['query'] = AlertDB::genSQL($alert['rule'], $alert['builder']);
274
            }
275
            $sql = $alert['query'];
276
            $qry = dbFetchRows($sql, [$alert['device_id']]);
277
            $alert['details']['contacts'] = AlertUtil::getContacts($qry);
278
        }
279
280
        $obj = $this->describeAlert($alert);
281
        if (is_array($obj)) {
282
            echo 'Issuing Alert-UID #' . $alert['id'] . '/' . $alert['state'] . ':' . PHP_EOL;
283
            $this->extTransports($obj);
284
285
            echo "\r\n";
286
        }
287
288
        return true;
289
    }
290
291
    /**
292
     * Issue ACK notification
293
     *
294
     * @return void
295
     */
296
    public function runAcks()
297
    {
298
        foreach ($this->loadAlerts('alerts.state = ' . AlertState::ACKNOWLEDGED . ' && alerts.open = ' . AlertState::ACTIVE) as $alert) {
299
            $this->issueAlert($alert);
300
            dbUpdate(['open' => AlertState::CLEAR], 'alerts', 'rule_id = ? && device_id = ?', [$alert['rule_id'], $alert['device_id']]);
301
        }
302
    }
303
304
    /**
305
     * Run Follow-Up alerts
306
     *
307
     * @return void
308
     */
309
    public function runFollowUp()
310
    {
311
        foreach ($this->loadAlerts('alerts.state > ' . AlertState::CLEAR . ' && alerts.open = 0') as $alert) {
312
            if ($alert['state'] != AlertState::ACKNOWLEDGED || ($alert['info']['until_clear'] === false)) {
313
                $rextra = json_decode($alert['extra'], true);
314
                if ($rextra['invert']) {
315
                    continue;
316
                }
317
318
                if (empty($alert['query'])) {
319
                    $alert['query'] = AlertDB::genSQL($alert['rule'], $alert['builder']);
320
                }
321
                $chk = dbFetchRows($alert['query'], [$alert['device_id']]);
322
                //make sure we can json_encode all the datas later
323
                $cnt = count($chk);
324
                for ($i = 0; $i < $cnt; $i++) {
325
                    if (isset($chk[$i]['ip'])) {
326
                        $chk[$i]['ip'] = inet6_ntop($chk[$i]['ip']);
327
                    }
328
                }
329
                $o = sizeof($alert['details']['rule']);
330
                $n = sizeof($chk);
331
                $ret = 'Alert #' . $alert['id'];
332
                $state = AlertState::CLEAR;
333
                if ($n > $o) {
334
                    $ret .= ' Worsens';
335
                    $state = AlertState::WORSE;
336
                    $alert['details']['diff'] = array_diff($chk, $alert['details']['rule']);
337
                } elseif ($n < $o) {
338
                    $ret .= ' Betters';
339
                    $state = AlertState::BETTER;
340
                    $alert['details']['diff'] = array_diff($alert['details']['rule'], $chk);
341
                }
342
343
                if ($state > AlertState::CLEAR && $n > 0) {
344
                    $alert['details']['rule'] = $chk;
345
                    if (dbInsert([
346
                        'state' => $state,
347
                        'device_id' => $alert['device_id'],
348
                        'rule_id' => $alert['rule_id'],
349
                        'details' => gzcompress(json_encode($alert['details']), 9),
350
                    ], 'alert_log')) {
351
                        dbUpdate(['state' => $state, 'open' => 1, 'alerted' => 1], 'alerts', 'rule_id = ? && device_id = ?', [$alert['rule_id'], $alert['device_id']]);
352
                    }
353
354
                    echo $ret . ' (' . $o . '/' . $n . ")\r\n";
355
                }
356
            }
357
        }
358
    }
359
360
    public function loadAlerts($where)
361
    {
362
        $alerts = [];
363
        foreach (dbFetchRows("SELECT alerts.id, alerts.alerted, alerts.device_id, alerts.rule_id, alerts.state, alerts.note, alerts.info FROM alerts WHERE $where") as $alert_status) {
364
            $alert = dbFetchRow(
365
                'SELECT alert_log.id,alert_log.rule_id,alert_log.device_id,alert_log.state,alert_log.details,alert_log.time_logged,alert_rules.rule,alert_rules.severity,alert_rules.extra,alert_rules.name,alert_rules.query,alert_rules.builder,alert_rules.proc FROM alert_log,alert_rules WHERE alert_log.rule_id = alert_rules.id && alert_log.device_id = ? && alert_log.rule_id = ? && alert_rules.disabled = 0 ORDER BY alert_log.id DESC LIMIT 1',
366
                [$alert_status['device_id'], $alert_status['rule_id']]
367
            );
368
369
            if (empty($alert['rule_id']) || ! $this->isRuleValid($alert_status['device_id'], $alert_status['rule_id'])) {
370
                echo 'Stale-Rule: #' . $alert_status['rule_id'] . '/' . $alert_status['device_id'] . "\r\n";
371
                // Alert-Rule does not exist anymore, let's remove the alert-state.
372
                dbDelete('alerts', 'rule_id = ? && device_id = ?', [$alert_status['rule_id'], $alert_status['device_id']]);
373
            } else {
374
                $alert['alert_id'] = $alert_status['id'];
375
                $alert['state'] = $alert_status['state'];
376
                $alert['alerted'] = $alert_status['alerted'];
377
                $alert['note'] = $alert_status['note'];
378
                if (! empty($alert['details'])) {
379
                    $alert['details'] = json_decode(gzuncompress($alert['details']), true);
380
                }
381
                $alert['info'] = json_decode($alert_status['info'], true);
382
                $alerts[] = $alert;
383
            }
384
        }
385
386
        return $alerts;
387
    }
388
389
    /**
390
     * Run all alerts
391
     *
392
     * @return void
393
     */
394
    public function runAlerts()
395
    {
396
        foreach ($this->loadAlerts('alerts.state != ' . AlertState::ACKNOWLEDGED . ' && alerts.open = 1') as $alert) {
397
            $noiss = false;
398
            $noacc = false;
399
            $updet = false;
400
            $rextra = json_decode($alert['extra'], true);
401
            if (! isset($rextra['recovery'])) {
402
                // backwards compatibility check
403
                $rextra['recovery'] = true;
404
            }
405
406
            $chk = dbFetchRow('SELECT alerts.alerted,devices.ignore,devices.disabled FROM alerts,devices WHERE alerts.device_id = ? && devices.device_id = alerts.device_id && alerts.rule_id = ?', [$alert['device_id'], $alert['rule_id']]);
407
408
            if ($chk['alerted'] == $alert['state']) {
409
                $noiss = true;
410
            }
411
412
            $tolerence_window = Config::get('alert.tolerance_window');
413
            if (! empty($rextra['count']) && empty($rextra['interval'])) {
414
                // This check below is for compat-reasons
415
                if (! empty($rextra['delay']) && $alert['state'] != AlertState::RECOVERED) {
416
                    if ((time() - strtotime($alert['time_logged']) + $tolerence_window) < $rextra['delay'] || (! empty($alert['details']['delay']) && (time() - $alert['details']['delay'] + $tolerence_window) < $rextra['delay'])) {
417
                        continue;
418
                    } else {
419
                        $alert['details']['delay'] = time();
420
                        $updet = true;
421
                    }
422
                }
423
424
                if ($alert['state'] == AlertState::ACTIVE && ! empty($rextra['count']) && ($rextra['count'] == -1 || $alert['details']['count']++ < $rextra['count'])) {
425
                    if ($alert['details']['count'] < $rextra['count']) {
426
                        $noacc = true;
427
                    }
428
429
                    $updet = true;
430
                    $noiss = false;
431
                }
432
            } else {
433
                // This is the new way
434
                if (! empty($rextra['delay']) && (time() - strtotime($alert['time_logged']) + $tolerence_window) < $rextra['delay'] && $alert['state'] != AlertState::RECOVERED) {
435
                    continue;
436
                }
437
438
                if (! empty($rextra['interval'])) {
439
                    if (! empty($alert['details']['interval']) && (time() - $alert['details']['interval'] + $tolerence_window) < $rextra['interval']) {
440
                        continue;
441
                    } else {
442
                        $alert['details']['interval'] = time();
443
                        $updet = true;
444
                    }
445
                }
446
447
                if (in_array($alert['state'], [AlertState::ACTIVE, AlertState::WORSE, AlertState::BETTER]) && ! empty($rextra['count']) && ($rextra['count'] == -1 || $alert['details']['count']++ < $rextra['count'])) {
448
                    if ($alert['details']['count'] < $rextra['count']) {
449
                        $noacc = true;
450
                    }
451
452
                    $updet = true;
453
                    $noiss = false;
454
                }
455
            }
456
            if ($chk['ignore'] == 1 || $chk['disabled'] == 1) {
457
                $noiss = true;
458
                $updet = false;
459
                $noacc = false;
460
            }
461
462
            if (AlertUtil::isMaintenance($alert['device_id'])) {
463
                $noiss = true;
464
                $noacc = true;
465
            }
466
467
            if ($updet) {
468
                dbUpdate(['details' => gzcompress(json_encode($alert['details']), 9)], 'alert_log', 'id = ?', [$alert['id']]);
469
            }
470
471
            if (! empty($rextra['mute'])) {
472
                echo 'Muted Alert-UID #' . $alert['id'] . "\r\n";
473
                $noiss = true;
474
            }
475
476
            if ($this->isParentDown($alert['device_id'])) {
477
                $noiss = true;
478
                Log::event('Skipped alerts because all parent devices are down', $alert['device_id'], 'alert', 1);
479
            }
480
481
            if ($alert['state'] == AlertState::RECOVERED && $rextra['recovery'] == false) {
482
                // Rule is set to not send a recovery alert
483
                $noiss = true;
484
            }
485
486
            if (! $noiss) {
487
                $this->issueAlert($alert);
488
                dbUpdate(['alerted' => $alert['state']], 'alerts', 'rule_id = ? && device_id = ?', [$alert['rule_id'], $alert['device_id']]);
489
            }
490
491
            if (! $noacc) {
492
                dbUpdate(['open' => 0], 'alerts', 'rule_id = ? && device_id = ?', [$alert['rule_id'], $alert['device_id']]);
493
            }
494
        }
495
    }
496
497
    /**
498
     * Run external transports
499
     *
500
     * @param  array  $obj  Alert-Array
501
     * @return void
502
     */
503
    public function extTransports($obj)
504
    {
505
        $type = new Template;
506
507
        // If alert transport mapping exists, override the default transports
508
        $transport_maps = AlertUtil::getAlertTransports($obj['alert_id']);
509
510
        if (! $transport_maps) {
511
            $transport_maps = AlertUtil::getDefaultAlertTransports();
512
        }
513
514
        // alerting for default contacts, etc
515
        if (Config::get('alert.transports.mail') === true && ! empty($obj['contacts'])) {
516
            $transport_maps[] = [
517
                'transport_id' => null,
518
                'transport_type' => 'mail',
519
                'opts' => $obj,
520
            ];
521
        }
522
523
        foreach ($transport_maps as $item) {
524
            $class = 'LibreNMS\\Alert\\Transport\\' . ucfirst($item['transport_type']);
525
            if (class_exists($class)) {
526
                //FIXME remove Deprecated transport
527
                $transport_title = "Transport {$item['transport_type']}";
528
                $obj['transport'] = $item['transport_type'];
529
                $obj['transport_name'] = $item['transport_name'];
530
                $obj['alert'] = new AlertData($obj);
531
                $obj['title'] = $type->getTitle($obj);
532
                $obj['alert']['title'] = $obj['title'];
533
                $obj['msg'] = $type->getBody($obj);
534
                c_echo(" :: $transport_title => ");
535
                try {
536
                    $instance = new $class($item['transport_id']);
537
                    $tmp = $instance->deliverAlert($obj, $item['opts']);
538
                    $this->alertLog($tmp, $obj, $obj['transport']);
539
                } catch (\Exception $e) {
540
                    $this->alertLog($e, $obj, $obj['transport']);
541
                }
542
                unset($instance);
543
                echo PHP_EOL;
544
            }
545
        }
546
547
        if (count($transport_maps) === 0) {
548
            echo 'No configured transports';
549
        }
550
    }
551
552
    // Log alert event
553
    public function alertLog($result, $obj, $transport)
554
    {
555
        $prefix = [
556
            AlertState::RECOVERED => 'recovery',
557
            AlertState::ACTIVE => $obj['severity'] . ' alert',
558
            AlertState::ACKNOWLEDGED => 'acknowledgment',
559
        ];
560
        $prefix[3] = &$prefix[0];
561
        $prefix[4] = &$prefix[0];
562
563
        if ($obj['state'] == AlertState::RECOVERED) {
564
            $severity = Alert::OK;
565
        } elseif ($obj['state'] == AlertState::ACTIVE) {
566
            $severity = Alert::SEVERITIES[$obj['severity']] ?? Alert::UNKNOWN;
567
        } elseif ($obj['state'] == AlertState::ACKNOWLEDGED) {
568
            $severity = Alert::NOTICE;
569
        } else {
570
            $severity = Alert::UNKNOWN;
571
        }
572
573
        if ($result === true) {
574
            echo 'OK';
575
            Log::event('Issued ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "'", $obj['device_id'], 'alert', $severity);
576
        } elseif ($result === false) {
577
            echo 'ERROR';
578
            Log::event('Could not issue ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "'", $obj['device_id'], null, Alert::ERROR);
579
        } else {
580
            echo "ERROR: $result\r\n";
581
            Log::event('Could not issue ' . $prefix[$obj['state']] . " for rule '" . $obj['name'] . "' to transport '" . $transport . "' Error: " . $result, $obj['device_id'], 'error', Alert::ERROR);
582
        }
583
    }
584
585
    /**
586
     * Check if a device's all parent are down
587
     * Returns true if all parents are down
588
     *
589
     * @param  int  $device  Device-ID
590
     * @return bool
591
     */
592
    public function isParentDown($device)
593
    {
594
        $parent_count = dbFetchCell('SELECT count(*) from `device_relationships` WHERE `child_device_id` = ?', [$device]);
595
        if (! $parent_count) {
596
            return false;
597
        }
598
599
        $down_parent_count = dbFetchCell("SELECT count(*) from devices as d LEFT JOIN devices_attribs as a ON d.device_id=a.device_id LEFT JOIN device_relationships as r ON d.device_id=r.parent_device_id WHERE d.status=0 AND d.ignore=0 AND d.disabled=0 AND r.child_device_id=? AND (d.status_reason='icmp' OR (a.attrib_type='override_icmp_disable' AND a.attrib_value=true))", [$device]);
600
        if ($down_parent_count == $parent_count) {
601
            return true;
602
        }
603
604
        return false;
605
    }
606
}
607