diff --git a/alerts.php b/alerts.php index 690001808..eceb62f45 100755 --- a/alerts.php +++ b/alerts.php @@ -208,7 +208,7 @@ function ExtTransports($obj) { global $config; $tmp = false; //To keep scrutinizer from naging because it doesnt understand eval foreach( $config['alert']['transports'] as $transport=>$opts ) { - if( file_exists($config['install_dir']."/includes/alerts/transport.".$transport.".php") ) { + if( ($opts === true || !empty($opts)) && file_exists($config['install_dir']."/includes/alerts/transport.".$transport.".php") ) { echo $transport." => "; eval('$tmp = function($obj,$opts) { global $config; '.file_get_contents($config['install_dir']."/includes/alerts/transport.".$transport.".php").' };'); $tmp = $tmp($obj,$opts); @@ -292,7 +292,7 @@ function DescribeAlert($alert) { $i++; $obj['faults'][$i] = $incident; foreach( $incident as $k=>$v ) { - if( !empty($v) && $k != 'device_id' && (stristr($k,'id') || stristr($k,'desc')) && substr_count($k,'_') <= 1 ) { + if( !empty($v) && $k != 'device_id' && (stristr($k,'id') || stristr($k,'desc') || stristr($k,'msg')) && substr_count($k,'_') <= 1 ) { $obj['faults'][$i]['string'] .= $k.' => '.$v."; "; } } diff --git a/doc/Extensions/Alerting.md b/doc/Extensions/Alerting.md index 9e4d9eb02..1e5f7d08f 100644 --- a/doc/Extensions/Alerting.md +++ b/doc/Extensions/Alerting.md @@ -22,6 +22,11 @@ Table of Content: - [Ports](#entity-ports) - [Processors](#entity-processors) - [Storage](#entity-storage) +- [Macros](#macros) + - [Device](#macros-device) + - [Port](#macros-port) + - [Time](#macros-time) + # About @@ -316,3 +321,109 @@ __processors.processor_descr__ = The description of the processor. __storage.storage_descr__ = The description of the storage. __storage.storage_perc__ = The usage of the storage as a percentage. + +# Macros + +Macros are shorthands to either portion of rules or pure SQL enhanced with placeholders. +You can define your own macros in your `config.php`. + +Example macro-implementation of Debian-Devices +```php +$config['alert']['macros']['rule']['is_debian'] = '%devices.features ~ "@debian@"'; +``` +And in the Rule: +``` +... && %macros.is_debian = "1" && ... +``` + +This Example-macro is a Boolean-macro, it applies a form of filter to the set of results defined by the rule. +All macros that are not unary should return Boolean. + +You can only apply _Equal_ or _Not-Equal_ Operations on Bollean-macros where `True` is represented by `"1"` and `False` by `"0"`. + + +## Device (Boolean) + +Entity: `%macros.device` + +Description: Only select devices that aren't deleted, ignored or disabled. + +Source: `(%devices.disabled = "0" && %devices.ignore = "0")` + +### Device is up (Boolean) + +Entity: `%macros.device_up` + +Description: Only select devices that are up. + +Implies: %macros.device + +Source: `(%devices.status = "1" && %macros.device)` + +### Device is down (Boolean) + +Entity: `%macros.device_down` + +Description: Only select devices that are down. + +Implies: %macros.device + +Source: `(%devices.status = "0" && %macros.device)` + +## Port (Boolean) + +Entity: `%macros.port` + +Description: Only select ports that aren't deleted, ignored or disabled. + +Source: `(%ports.deleted = "0" && %ports.ignore = "0" && %ports.disabled = "0")` + +### Port is up (Boolean) + +Entity: `%macros.port_up` + +Description: Only select ports that are up and also should be up. + +Implies: %macros.port + +Source: `(%ports.ifOperStatus = "up" && %ports.ifAdminStatus = "up" && %macros.port)` + +### Port is down (Boolean) + +Entity: `%macros.port_down` + +Description: Only select ports that are down. + +Implies: %macros.port + +Source: `(%ports.ifOperStatus = "down" && %ports.ifAdminStatus != "down" && %macros.port)` + +### Port-Usage in Percent (Decimal) + +Entity: `%macros.port_usage_perc` + +Description: Return port-usage in percent. + +Source: `((%ports.ifInOctets_rate*8)/%ports.ifSpeed)*100` + +## Time + +### Now (Datetime) + +Entity: `%macros.now` + +Description: Alias of MySQL's NOW() + +Source: `NOW()` + +### Past N Minutes (Datetime) + +Entity: `%macros.last_$m` + +Description: Returns a MySQL Timestamp dated `$` Minutes in the past. `$` can only be a supported Resolution. + +Example: `%macros.last_5m` is Last 5 Minutes. + +Resolution: 5,10,15,30,60 + +Source: `DATE_SUB(NOW(),INTERVAL $ MINUTE)` diff --git a/html/ajax_rulesuggest.php b/html/ajax_rulesuggest.php index b4f410f4a..0515fcc65 100644 --- a/html/ajax_rulesuggest.php +++ b/html/ajax_rulesuggest.php @@ -67,10 +67,16 @@ if( isset($_GET['term'],$_GET['device_id']) ) { $chk = $memcache->get('rule-suggest_'.$term[0]); } if( !(sizeof($chk) > 0) || $chk === false ) { - $tmp = dbFetchRows('SHOW COLUMNS FROM '.$term[0]); - foreach( $tmp as $tst ) { - if( isset($tst['Field']) ) { - $chk[] = $term[0].'.'.$tst['Field']; + if( $term[0] == "macros" ) { + foreach( $config['alert']['macros']['rule'] as $macro=>$v ) { + $chk[] = "macros.".$macro; + } + } else { + $tmp = dbFetchRows('SHOW COLUMNS FROM '.$term[0]); + foreach( $tmp as $tst ) { + if( isset($tst['Field']) ) { + $chk[] = $term[0].'.'.$tst['Field']; + } } } } @@ -84,6 +90,7 @@ if( isset($_GET['term'],$_GET['device_id']) ) { foreach( $tmp as $tst ) { $chk[] = $tst['TABLE_NAME'].'.'; } + $chk[] = 'macros.'; } } if( sizeof($chk) > 0 ) { diff --git a/html/includes/modal/new_alert_rule.inc.php b/html/includes/modal/new_alert_rule.inc.php index d106f4ec3..5165ba28f 100644 --- a/html/includes/modal/new_alert_rule.inc.php +++ b/html/includes/modal/new_alert_rule.inc.php @@ -245,11 +245,13 @@ $('#and, #or').click('', function(e) { strategy: 'array', tagFieldName: 'rules[]' }); - if(entity.indexOf("%") >= 0) { - $('#response').data('tagmanager').populate([ entity+' '+condition+' '+value+' '+glue ]); - } else { - $('#response').data('tagmanager').populate([ '%'+entity+' '+condition+' "'+value+'" '+glue ]); + if(value.indexOf("%") < 0) { + value = '"'+value+'"'; } + if(entity.indexOf("%") < 0) { + entity = '%'+entity; + } + $('#response').data('tagmanager').populate([ entity+' '+condition+' '+value+' '+glue ]); } }); diff --git a/html/includes/print-alert-rules.php b/html/includes/print-alert-rules.php index 531b1684c..03b6707c4 100644 --- a/html/includes/print-alert-rules.php +++ b/html/includes/print-alert-rules.php @@ -12,12 +12,12 @@ $no_refresh = TRUE; '-1', 'rule' => '%devices.status != "1" && %devices.disabled = "0" && %devices.ignore = "0"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"-1","delay":"300"}', 'disabled' => 0, 'name' => 'Devices up/down'); - $default_rules[] = array('device_id' => '-1', 'rule' => '%devices.uptime < "300" && %devices.disabled = "0" && %devices.ignore = "0"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"1","delay":"300"}', 'disabled' => 0, 'name' => 'Device rebooted'); - $default_rules[] = array('device_id' => '-1', 'rule' => '%bgpPeers.bgpPeerState != "established"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"1","delay":"300"}', 'disabled' => 0, 'name' => 'BGP Session down'); + $default_rules[] = array('device_id' => '-1', 'rule' => '%macros.device_down = "1"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"-1","delay":"300"}', 'disabled' => 0, 'name' => 'Devices up/down'); + $default_rules[] = array('device_id' => '-1', 'rule' => '%devices.uptime < "300" && %macros.device = "1"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"1","delay":"300"}', 'disabled' => 0, 'name' => 'Device rebooted'); + $default_rules[] = array('device_id' => '-1', 'rule' => '%bgpPeers.bgpPeerState != "established" && %macros.device_up = "1"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"1","delay":"300"}', 'disabled' => 0, 'name' => 'BGP Session down'); $default_rules[] = array('device_id' => '-1', 'rule' => '%bgpPeers.bgpPeerFsmEstablishedTime < "300" && %bgpPeers.bgpPeerState = "established"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"1","delay":"300"}', 'disabled' => 0, 'name' => 'BGP Session establised'); - $default_rules[] = array('device_id' => '-1', 'rule' => '%ports.ifOperStatus != "up" && %ports.ifAdminStatus = "up" && %ports.deleted = "0" && %ports.ignore = "0" && %ports.disabled = "0"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"1","delay":"300"}', 'disabled' => 0, 'name' => 'Port status up/down'); - $default_rules[] = array('device_id' => '-1', 'rule' => '((%ports.ifInOctets_rate*8)/%ports.ifSpeed)*100 >= 80', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"-1","delay":"300"}', 'disabled' => 0, 'name' => 'Port utilisation over threshold'); + $default_rules[] = array('device_id' => '-1', 'rule' => '%macros.port_down = "1"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"1","delay":"300"}', 'disabled' => 0, 'name' => 'Port status up/down'); + $default_rules[] = array('device_id' => '-1', 'rule' => '%macros.port_usage_perc >= "80"', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"-1","delay":"300"}', 'disabled' => 0, 'name' => 'Port utilisation over threshold'); $default_rules[] = array('device_id' => '-1', 'rule' => '%sensors.sensor_current > %sensors.sensor_limit', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"-1","delay":"300"}', 'disabled' => 0, 'name' => 'Sensor over limit'); $default_rules[] = array('device_id' => '-1', 'rule' => '%sensors.sensor_current < %sensors.sensor_limit_low', 'severity' => 'critical', 'extra' => '{"mute":false,"count":"-1","delay":"300"}', 'disabled' => 0, 'name' => 'Sensor under limit'); foreach( $default_rules as $add_rule ) { diff --git a/html/includes/table/alerts.inc.php b/html/includes/table/alerts.inc.php index 46e5ddb0c..f6f2aa6ac 100644 --- a/html/includes/table/alerts.inc.php +++ b/html/includes/table/alerts.inc.php @@ -48,11 +48,11 @@ foreach (dbFetchRows($sql,$param) as $alert) { $fault_detail .= $tmp; } else { foreach ($tmp_alerts as $k=>$v) { - if (!empty($v) && $k != 'device_id' && (stristr($k,'id') || stristr($k,'desc')) && substr_count($k,'_') <= 1) { + if (!empty($v) && $k != 'device_id' && (stristr($k,'id') || stristr($k,'desc') || stristr($k,'msg')) && substr_count($k,'_') <= 1) { $fault_detail .= "$k => '$v', "; } - $fault_detail = rtrim($fault_detail,", "); } + $fault_detail = rtrim($fault_detail,", "); } $fault_detail .= "
"; } @@ -97,7 +97,7 @@ foreach (dbFetchRows($sql,$param) as $alert) { if ($_SESSION['userlevel'] >= '10') { $ack_ico = 'volume-up'; $ack_col = 'success'; - if(in_array($alert['state'],array(2,3,4))) { + if($alert['state'] == 2) { $ack_ico = 'volume-off'; $ack_col = 'danger'; } diff --git a/includes/alerts.inc.php b/includes/alerts.inc.php index 720629bad..dec531f8f 100644 --- a/includes/alerts.inc.php +++ b/includes/alerts.inc.php @@ -31,6 +31,7 @@ include_once($config['install_dir'].'/html/includes/authentication/'.$config['au * @return string */ function GenSQL($rule) { + $rule = RunMacros($rule); $tmp = explode(" ",$rule); $tables = array(); foreach( $tmp as $opt ) { @@ -55,6 +56,29 @@ function GenSQL($rule) { return $sql; } +/** + * Process Macros + * @param string $rule Rule to process + * @return string + */ +function RunMacros($rule,$x=1) { + global $config; + krsort($config['alert']['macros']['rule']); + foreach( $config['alert']['macros']['rule'] as $macro=>$value ) { + if( !strstr($macro," ") ) { + $rule = str_replace('%macros.'.$macro,$value,$rule); + } + } + if( strstr($rule,"%macros") ) { + if( ++$x < 30 ) { + $rule = RunMacros($rule,$x); + } else { + return false; + } + } + return $rule; +} + /** * Get Alert-Rules for Devices * @param int $device Device-ID @@ -158,8 +182,8 @@ function GetContacts($results) { if( sizeof($results) == 0 ) { return array(); } - if( $config['alerts']['email']['default_only'] ) { - return array($config['alerts']['email']['default'] => 'NOC'); + if( $config['alert']['default_only'] == true || $config['alerts']['email']['default_only'] == true ) { + return array(''.($config['alert']['default_mail'] ? $config['alert']['default_mail'] : $config['alerts']['email']['default']) => 'NOC'); } $users = get_userlist(); $contacts = array(); diff --git a/includes/defaults.inc.php b/includes/defaults.inc.php index b22b0957b..00b45e0a2 100644 --- a/includes/defaults.inc.php +++ b/includes/defaults.inc.php @@ -201,6 +201,44 @@ $config['email_smtp_password'] = NULL; // Password for SM // Alerting Settings +$config['alert'] = array( + 'macros' => array( //Macros: + 'rule' => array( // For Rules + //Time Macros + 'now' => 'NOW()', + 'past_5m' => 'DATE_SUB(NOW(),INTERVAL 5 MINUTE)', + 'past_10m' => 'DATE_SUB(NOW(),INTERVAL 10 MINUTE)', + 'past_15m' => 'DATE_SUB(NOW(),INTERVAL 15 MINUTE)', + 'past_30m' => 'DATE_SUB(NOW(),INTERVAL 30 MINUTE)', + 'past_60m' => 'DATE_SUB(NOW(),INTERVAL 60 MINUTE)', + + //Device Macros + 'device' => '(%devices.disabled = "0" && %devices.ignore = "0")', + 'device_up' => '(%devices.status = "1" && %macros.device)', + 'device_down' => '(%devices.status = "0" && %macros.device)', + + //Port Macros + 'port' => '(%ports.deleted = "0" && %ports.ignore = "0" && %ports.disabled = "0")', + 'port_up' => '(%ports.ifOperStatus = "up" && %ports.ifAdminStatus = "up" && %macros.port)', + 'port_down' => '(%ports.ifOperStatus = "down" && %ports.ifAdminStatus != "down" && %macros.port)', + 'port_usage_perc' => '((%ports.ifInOctets_rate*8)/%ports.ifSpeed)*100', + + //Misc Macros + ), + ), + 'transports' => array( //Transports: + 'dummy' => false, // Dummy alerting (debug) + 'mail' => false, // E-Mail alerting + 'irc' => false, // IRC Alerting + ), + 'globals' => false, //Issue to global-read users + 'admins' => false, //Issue to administrators + 'default_only' => false, //Only issue to default + 'default_mail' => '', //Default email +); + +//Legacy options + $config['alerts']['email']['default'] = NULL; // Default alert recipient $config['alerts']['email']['default_only'] = FALSE; // Only use default recipient $config['alerts']['email']['enable'] = TRUE; // Enable email alerts