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