diff --git a/daily.php b/daily.php
index 03176a47d..abb525a0b 100644
--- a/daily.php
+++ b/daily.php
@@ -58,5 +58,13 @@ if ($options['f'] === 'perf_times') {
if ($options['f'] === 'callback') {
require_once "callback.php";
}
+if ($options['f'] === 'device_perf') {
+ if (is_numeric($config['device_perf_purge'])) {
+ if (dbDelete('device_perf', "timestamp < UNIX_TIMESTAMP(DATE_SUB(NOW(),INTERVAL ? DAY))", array($config['device_perf_purge'])) ) {
+ echo 'Device performance times cleared for entries over ' . $config['device_perf_purge'] . " days\n";
+ }
+ }
+}
+
?>
diff --git a/daily.sh b/daily.sh
index 4168d7344..91183d6d1 100755
--- a/daily.sh
+++ b/daily.sh
@@ -12,3 +12,4 @@ php daily.php -f eventlog
php daily.php -f authlog
php daily.php -f perf_times
php daily.php -f callback
+php daily.php -f device_perf
diff --git a/doc/Extensions/Alerting.md b/doc/Extensions/Alerting.md
index 9390e53af..253c90cc0 100644
--- a/doc/Extensions/Alerting.md
+++ b/doc/Extensions/Alerting.md
@@ -321,6 +321,8 @@ __devices.location__ = The devices location.
__devices.status__ = The status of the device, 1 = up, 0 = down.
+__devices.status_reason__ = The reason the device was detected as down (icmp or snmp).
+
__devices.ignore__ = If the device is ignored this will be set to 1.
__devices.disabled__ = If the device is disabled this will be set to 1.
@@ -502,3 +504,19 @@ Entity: `%macros.sensor`
Description: Only select sensors that aren't ignored.
Source: `(%sensors.sensor_alert = 1)`
+
+## Packet Loss (Boolean)
+
+Entity: `(%macros.packet_loss_5m)`
+
+Description: Packet loss % value for the device within the last 5 minutes.
+
+Example: `%macros.packet_loss_5m` > 50
+
+Entity: `(%macros.packet_loss_15m)`
+
+Description: Packet loss % value for the device within the last 15 minutes.
+
+Example: `%macros.packet_loss_15m` > 50
+
+
diff --git a/doc/Support/Configuration.md b/doc/Support/Configuration.md
index ae3ee9e19..68cb55251 100644
--- a/doc/Support/Configuration.md
+++ b/doc/Support/Configuration.md
@@ -33,6 +33,8 @@ $config['fping'] = "/usr/bin/fping";
$config['fping6'] = "/usr/bin/fping6";
$config['fping_options']['retries'] = 3;
$config['fping_options']['timeout'] = 500;
+$config['fping_options']['count'] = 3;
+$config['fping_options']['millisec'] = 5;
```
fping configuration options, this includes setting the timeout and retry options.
@@ -404,6 +406,7 @@ $config['syslog_purge'] = 30;
$config['eventlog_purge'] = 30;
$config['authlog_purge'] = 30;
$config['perf_times_purge'] = 30;
+$config['device_perf_purge'] = 30;
```
This option will ensure data within LibreNMS over 1 month old is automatically purged. You can alter these individually,
values are in days.
diff --git a/html/includes/table/devices.inc.php b/html/includes/table/devices.inc.php
index e89e0b6db..25cf21bd5 100644
--- a/html/includes/table/devices.inc.php
+++ b/html/includes/table/devices.inc.php
@@ -94,7 +94,7 @@ foreach (dbFetchRows($sql, $param) as $device) {
if ($device['status'] == '0') {
$extra = "danger";
- $msg = "down";
+ $msg = $device['status_reason'];
} else {
$extra = "success";
$msg = "up";
diff --git a/html/pages/device.inc.php b/html/pages/device.inc.php
index c72daf9bf..ff6a42775 100644
--- a/html/pages/device.inc.php
+++ b/html/pages/device.inc.php
@@ -382,6 +382,13 @@ if (device_permitted($vars['device']) || $check_device == $vars['device'])
');
}
+ echo('
+
+
Performance
+
+ ');
+
+
echo ('
');
diff --git a/html/pages/device/performance.inc.php b/html/pages/device/performance.inc.php
new file mode 100644
index 000000000..1c642daf4
--- /dev/null
+++ b/html/pages/device/performance.inc.php
@@ -0,0 +1,158 @@
+
+* 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. Please see LICENSE.txt at the top level of
+* the source code distribution for details.
+
+* Copyright (c) 2014 Neil Lathwood
+*
+* 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. Please see LICENSE.txt at the top level of
+* the source code distribution for details.
+
+*/
+
+
+if(!isset($vars['section'])) { $vars['section'] = "performance"; }
+
+if (is_admin() === true || is_read() === true) {
+ $query = "SELECT DATE_FORMAT(timestamp, '".$config['alert_graph_date_format']."') Date, xmt,rcv,loss,min,max,avg FROM `device_perf` WHERE `device_id` = ?";
+ $param = array($device['device_id']);
+} else {
+ $query = "SELECT DATE_FORMAT(timestamp, '".$config['alert_graph_date_format']."') Date, xmt,rcv,loss,min,max,avg FROM `device_perf`,`devices_perms` WHERE `device_id` = ? AND alert_log.device_id = devices_perms.device_id AND devices_perms.user_id = " . $_SESSION['user_id'];
+ $param = array($device['device_id']);
+}
+
+?>
+
+
+
+
+
diff --git a/includes/defaults.inc.php b/includes/defaults.inc.php
index a150c2daa..ba46626ee 100644
--- a/includes/defaults.inc.php
+++ b/includes/defaults.inc.php
@@ -43,6 +43,8 @@ $config['rrdtool'] = "/usr/bin/rrdtool";
$config['fping'] = "/usr/bin/fping";
$config['fping_options']['retries'] = 3;
$config['fping_options']['timeout'] = 500;
+$config['fping_options']['count'] = 3;
+$config['fping_options']['millisec'] = 20;
$config['fping6'] = "/usr/bin/fping6";
$config['snmpwalk'] = "/usr/bin/snmpwalk";
$config['snmpget'] = "/usr/bin/snmpget";
@@ -581,6 +583,7 @@ $config['syslog_purge'] = 30; # Number in days
$config['eventlog_purge'] = 30; # Number in days of how long to keep eventlog entries for.
$config['authlog_purge'] = 30; # Number in days of how long to keep authlog entries for.
$config['perf_times_purge'] = 30; # Number in days of how long to keep performace pooling stats entries for.
+$config['device_perf_purge'] = 30; // Number in days of how long to keep device performance data for.
# Date format for PHP date()s
$config['dateformat']['long'] = "r"; # RFC2822 style
diff --git a/includes/functions.php b/includes/functions.php
index 18937bb53..1d0fec1f7 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -499,25 +499,23 @@ function isPingable($hostname,$device_id = FALSE)
if(is_numeric($config['fping_options']['timeout']) || $config['fping_options']['timeout'] > 1) {
$fping_params .= ' -t ' . $config['fping_options']['timeout'];
}
- $status = shell_exec($config['fping'] . "$fping_params -e $hostname 2>/dev/null");
+ if(is_numeric($config['fping_options']['count']) || $config['fping_options']['count'] > 0) {
+ $fping_params .= ' -c ' . $config['fping_options']['count'];
+ }
+ if(is_numeric($config['fping_options']['millisec']) || $config['fping_options']['millisec'] > 0) {
+ $fping_params .= ' -p ' . $config['fping_options']['millisec'];
+ }
$response = array();
- if (strstr($status, "alive"))
- {
- $response['result'] = TRUE;
- } else {
- $status = shell_exec($config['fping6'] . "$fping_params -e $hostname 2>/dev/null");
- if (strstr($status, "alive"))
- {
- $response['result'] = TRUE;
- } else {
+ $status = fping($hostname,$fping_params);
+ if ($status['loss'] == 100) {
$response['result'] = FALSE;
- }
+ } else {
+ $response['result'] = TRUE;
}
- if(is_numeric($device_id) && !empty($device_id))
- {
- preg_match('/(\d+\.*\d*) (ms)/', $status, $time);
- $response['last_ping_timetaken'] = $time[1];
+ if (is_numeric($status['avg'])) {
+ $response['last_ping_timetaken'] = $status['avg'];
}
+ $response['db'] = $status;
return($response);
}
@@ -1273,3 +1271,42 @@ function ip_exists($ip) {
}
return true;
}
+
+function fping($host,$params) {
+
+ global $config;
+
+ $descriptorspec = array(
+ 0 => array("pipe", "r"),
+ 1 => array("pipe", "w"),
+ 2 => array("pipe", "w")
+ );
+
+ $process = proc_open($config['fping'] . ' -e -q ' .$params . ' ' .$host.' 2>&1', $descriptorspec, $pipes);
+ $read = '';
+
+ if (is_resource($process)) {
+
+ fclose($pipes[0]);
+
+ while (!feof($pipes[1])) {
+ $read .= fgets($pipes[1], 1024);
+ }
+ fclose($pipes[1]);
+ proc_close($process);
+ }
+
+ preg_match('/[0-9]+\/[0-9]+\/[0-9]+%/', $read, $loss_tmp);
+ preg_match('/[0-9\.]+\/[0-9\.]+\/[0-9\.]*$/', $read, $latency);
+ $loss = preg_replace("/%/","",$loss_tmp[0]);
+ list($xmt,$rcv,$loss) = preg_split("/\//", $loss);
+ list($min,$avg,$max) = preg_split("/\//", $latency[0]);
+ if ($loss < 0) {
+ $xmt = 1;
+ $rcv = 1;
+ $loss = 100;
+ }
+ $response = array('xmt'=>$xmt,'rcv'=>$rcv,'loss'=>$loss,'min'=>$min,'max'=>$max,'avg'=>$avg);
+ return $response;
+}
+
diff --git a/includes/polling/functions.inc.php b/includes/polling/functions.inc.php
index c705eccb8..6af89c3b9 100644
--- a/includes/polling/functions.inc.php
+++ b/includes/polling/functions.inc.php
@@ -132,24 +132,35 @@ function poll_device($device, $options)
if (!is_dir($host_rrd)) { mkdir($host_rrd); echo("Created directory : $host_rrd\n"); }
$ping_response = isPingable($device['hostname'],$device['device_id']);
+
+ $device_perf = $ping_response['db'];
+ $device_perf['device_id'] = $device['device_id'];
+ $device_perf['timestamp'] = array('NOW()');
+ if (is_array($device_perf)) {
+ dbInsert($device_perf, 'device_perf');
+ }
+
+
$device['pingable'] = $ping_response['result'];
$ping_time = $ping_response['last_ping_timetaken'];
$response = array();
+ $status_reason = '';
if ($device['pingable'])
{
$device['snmpable'] = isSNMPable($device);
if ($device['snmpable'])
{
$status = "1";
+ $response['status_reason'] = '';
} else {
echo("SNMP Unreachable");
$status = "0";
- $response['status'] = 'snmp';
+ $response['status_reason'] = 'snmp';
}
} else {
echo("Unpingable");
$status = "0";
- $response['status'] = 'icmp';
+ $response['status_reason'] = 'icmp';
}
if ($device['status'] != $status)
@@ -157,11 +168,11 @@ function poll_device($device, $options)
$poll_update .= $poll_separator . "`status` = '$status'";
$poll_separator = ", ";
- dbUpdate(array('status' => $status), 'devices', 'device_id=?', array($device['device_id']));
+ dbUpdate(array('status' => $status,'status_reason' => $response['status_reason']), 'devices', 'device_id=?', array($device['device_id']));
dbInsert(array('importance' => '0', 'device_id' => $device['device_id'], 'message' => "Device is " .($status == '1' ? 'up' : 'down')), 'alerts');
log_event('Device status changed to ' . ($status == '1' ? 'Up' : 'Down'), $device, ($status == '1' ? 'up' : 'down'));
- notify($device, "Device ".($status == '1' ? 'Up' : 'Down').": " . $device['hostname'], "Device ".($status == '1' ? 'up' : 'down').": " . $device['hostname'] . " " . $response['status']);
+ notify($device, "Device ".($status == '1' ? 'Up' : 'Down').": " . $device['hostname'], "Device ".($status == '1' ? 'up' : 'down').": " . $device['hostname'] . " " . $response['status_reason']);
}
if ($status == "1")
diff --git a/sql-schema/056.sql b/sql-schema/056.sql
new file mode 100644
index 000000000..a434a7d68
--- /dev/null
+++ b/sql-schema/056.sql
@@ -0,0 +1,5 @@
+CREATE TABLE IF NOT EXISTS `device_perf` ( `id` int(11) NOT NULL AUTO_INCREMENT, `device_id` int(11) NOT NULL, `timestamp` datetime NOT NULL, `xmt` float NOT NULL, `rcv` float NOT NULL, `loss` float NOT NULL, `min` float NOT NULL, `max` float NOT NULL, `avg` float NOT NULL, KEY `id` (`id`,`device_id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
+insert into config (config_name,config_value,config_default,config_descr,config_group,config_group_order,config_sub_group,config_sub_group_order,config_hidden,config_disabled) values ('alert.macros.rule.packet_loss_15m','(%macros.past_15m && %device_perf.loss)','(%macros.past_15m && %device_perf.loss)','Packet loss over the last 15 minutes','alerting',0,'macros',0,1,0);
+insert into config (config_name,config_value,config_default,config_descr,config_group,config_group_order,config_sub_group,config_sub_group_order,config_hidden,config_disabled) values ('alert.macros.rule.packet_loss_5m','(%macros.past_5m && %device_perf.loss)','(%macros.past_5m && %device_perf.loss)','Packet loss over the last 5 minutes','alerting',0,'macros',0,1,0);
+ALTER TABLE `devices` ADD `status_reason` VARCHAR( 50 ) NOT NULL AFTER `status` ;
+UPDATE `devices` SET `status_reason`='down' WHERE `status`=0;