Skip to content

Commit

Permalink
Alert Subsys to OOP and SNMPTraps trigger Alert Rules (#9765)
Browse files Browse the repository at this point in the history
* Changed snmptrap.php and handlers to allow for alerts.

* Moved RunRules() to Dispatcher.php

* Modified includes, passes pre-commit tests locally.

* Converted RunRules to OO, fixed formatting in files found by TravisCI

* rebase, covert alert subsy to oop

* Modified init.php to call new Alert subsystem

* fixed dependencies

* Resolved undefined functions

* Fixed PingCheck.php not triggering

* Changed runAlerts to __construct

* Fixes
Can't have Config inside empty()
incorrectly named function (my fault)
poller.php missing shebang
organize imports
  • Loading branch information
h-barnhart authored and murrant committed Jul 18, 2019
1 parent 6986711 commit 851994c
Show file tree
Hide file tree
Showing 10 changed files with 1,057 additions and 915 deletions.
118 changes: 118 additions & 0 deletions LibreNMS/Alert/AlertDB.php
@@ -0,0 +1,118 @@
<?php
/**
* AlertDB.php
*
* Extending the built in logging to add an event logger function
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
* Original code:
* @author Daniel Preussker <f0o@devilcode.org>
* @copyright 2014 f0o, LibreNMS
* @license GPL
*
* Modified by:
* @package LibreNMS
* @link http://librenms.org
* @copyright 2019 KanREN, Inc.
* @author Heath Barnhart <hbarnhart@kanren.net>
*/

namespace LibreNMS\Alert;

use App\Models\Device;
use LibreNMS\Alert\AlertUtil;
use LibreNMS\Alerting\QueryBuilderParser;

class AlertDB
{

/**
* @param $rule
* @param $query_builder
* @return bool|string
*/
public static function genSQL($rule, $query_builder = false)
{
if ($query_builder) {
return QueryBuilderParser::fromJson($query_builder)->toSql();
} else {
return self::genSQLOld($rule);
}
}

/**
* Generate SQL from Rule
* @param string $rule Rule to generate SQL for
* @return string|boolean
*/
public static function genSQLOld($rule)
{
$rule = AlertUtil::runMacros($rule);
if (empty($rule)) {
//Cannot resolve Macros due to recursion. Rule is invalid.
return false;
}
//Pretty-print rule to dissect easier
$pretty = array('&&' => ' && ', '||' => ' || ');
$rule = str_replace(array_keys($pretty), $pretty, $rule);
$tmp = explode(" ", $rule);
$tables = array();
foreach ($tmp as $opt) {
if (strstr($opt, '%') && strstr($opt, '.')) {
$tmpp = explode(".", $opt, 2);
$tmpp[0] = str_replace("%", "", $tmpp[0]);
$tables[] = mres(str_replace("(", "", $tmpp[0]));
$rule = str_replace($opt, $tmpp[0].'.'.$tmpp[1], $rule);
}
}
$tables = array_keys(array_flip($tables));
if (dbFetchCell('SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_NAME = ? && COLUMN_NAME = ?', array($tables[0],'device_id')) != 1) {
//Our first table has no valid glue, append the 'devices' table to it!
array_unshift($tables, 'devices');
}
$x = sizeof($tables)-1;
$i = 0;
$join = "";
while ($i < $x) {
if (isset($tables[$i+1])) {
$gtmp = ResolveGlues(array($tables[$i+1]), 'device_id');
if ($gtmp === false) {
//Cannot resolve glue-chain. Rule is invalid.
return false;
}
$last = "";
$qry = "";
foreach ($gtmp as $glue) {
if (empty($last)) {
list($tmp,$last) = explode('.', $glue);
$qry .= $glue.' = ';
} else {
list($tmp,$new) = explode('.', $glue);
$qry .= $tmp.'.'.$last.' && '.$tmp.'.'.$new.' = ';
$last = $new;
}
if (!in_array($tmp, $tables)) {
$tables[] = $tmp;
}
}
$join .= "( ".$qry.$tables[0].".device_id ) && ";
}
$i++;
}
$sql = "SELECT * FROM ".implode(",", $tables)." WHERE (".$join."".str_replace("(", "", $tables[0]).".device_id = ?) && (".str_replace(array("%","@","!~","~"), array("",".*","NOT REGEXP","REGEXP"), $rule).")";
return $sql;
}
}
129 changes: 129 additions & 0 deletions LibreNMS/Alert/AlertRules.php
@@ -0,0 +1,129 @@
<?php
/**
* AlertRules.php
*
* Extending the built in logging to add an event logger function
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Original Code:
* @author Daniel Preussker <f0o@devilcode.org>
* @copyright 2014 f0o, LibreNMS
* @license GPL
* @package LibreNMS
* @subpackage Alerts
*
* Modeified by:
* @package LibreNMS
* @link http://librenms.org
* @copyright 2019 KanREN, Inc.
* @author Heath Barnhart <hbarnhart@kanren.net>
*/

namespace LibreNMS\Alert;

use App\Models\Device;
use LibreNMS\Alert\AlertUtil;
use LibreNMS\Alert\AlertDB;

class AlertRules
{
public function runRules($device_id)
{

//Check to see if under maintenance
if (AlertUtil::isMaintenance($device_id) > 0) {
echo "Under Maintenance, Skipping alerts.\r\n";
return false;
}
//Checks each rule.
foreach (AlertUtil::getRules($device_id) as $rule) {
c_echo('Rule %p#'.$rule['id'].' (' . $rule['name'] . '):%n ');
$extra = json_decode($rule['extra'], true);
if (isset($extra['invert'])) {
$inv = (bool) $extra['invert'];
} else {
$inv = false;
}
d_echo(PHP_EOL);
if (empty($rule['query'])) {
$rule['query'] = AlertDB::genSQL($rule['rule'], $rule['builder']);
}
$sql = $rule['query'];
$qry = dbFetchRows($sql, array($device_id));
$cnt = count($qry);
for ($i = 0; $i < $cnt; $i++) {
if (isset($qry[$i]['ip'])) {
$qry[$i]['ip'] = inet6_ntop($qry[$i]['ip']);
}
}
$s = sizeof($qry);
if ($s == 0 && $inv === false) {
$doalert = false;
} elseif ($s > 0 && $inv === false) {
$doalert = true;
} elseif ($s == 0 && $inv === true) {
$doalert = true;
} else {
$doalert = false;
}

$current_state = dbFetchCell("SELECT state FROM alerts WHERE rule_id = ? AND device_id = ? ORDER BY id DESC LIMIT 1", [$rule['id'], $device_id]);
if ($doalert) {
if ($current_state == 2) {
c_echo('Status: %ySKIP');
} elseif ($current_state >= 1) {
c_echo('Status: %bNOCHG');
// NOCHG here doesn't mean no change full stop. It means no change to the alert state
// So we update the details column with any fresh changes to the alert output we might have.
$alert_log = dbFetchRow('SELECT alert_log.id, alert_log.details 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', array($device_id, $rule['id']));
$details = [];
if (!empty($alert_log['details'])) {
$details = json_decode(gzuncompress($alert_log['details']), true);
}
$details['contacts'] = AlertUtil::getContacts($qry);
$details['rule'] = $qry;
$details = gzcompress(json_encode($details), 9);
dbUpdate(array('details' => $details), 'alert_log', 'id = ?', array($alert_log['id']));
} else {
$extra = gzcompress(json_encode(array('contacts' => AlertUtil::getContacts($qry), 'rule'=>$qry)), 9);
if (dbInsert(['state' => 1, 'device_id' => $device_id, 'rule_id' => $rule['id'], 'details' => $extra], 'alert_log')) {
if (is_null($current_state)) {
dbInsert(array('state' => 1, 'device_id' => $device_id, 'rule_id' => $rule['id'], 'open' => 1,'alerted' => 0), 'alerts');
} else {
dbUpdate(['state' => 1, 'open' => 1], 'alerts', 'device_id = ? && rule_id = ?', [$device_id, $rule['id']]);
}
c_echo(PHP_EOL . 'Status: %rALERT');
}
}
} else {
if (!is_null($current_state) && $current_state == 0) {
c_echo('Status: %bNOCHG');
} else {
if (dbInsert(['state' => 0, 'device_id' => $device_id, 'rule_id' => $rule['id']], 'alert_log')) {
if (is_null($current_state)) {
dbInsert(['state' => 0, 'device_id' => $device_id, 'rule_id' => $rule['id'], 'open' => 1, 'alerted' => 0], 'alerts');
} else {
dbUpdate(['state' => 0, 'open' => 1, 'note' => ''], 'alerts', 'device_id = ? && rule_id = ?', [$device_id, $rule['id']]);
}

c_echo(PHP_EOL . 'Status: %gOK');
}
}
}
c_echo('%n' . PHP_EOL);
}
}
}

0 comments on commit 851994c

Please sign in to comment.