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..d90bf4ff0 100644
--- a/doc/Extensions/Alerting.md
+++ b/doc/Extensions/Alerting.md
@@ -502,3 +502,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..43d11cffd 100644
--- a/doc/Support/Configuration.md
+++ b/doc/Support/Configuration.md
@@ -404,6 +404,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/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..24b43b99f 100644
--- a/includes/defaults.inc.php
+++ b/includes/defaults.inc.php
@@ -43,6 +43,7 @@ $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['fping6'] = "/usr/bin/fping6";
$config['snmpwalk'] = "/usr/bin/snmpwalk";
$config['snmpget'] = "/usr/bin/snmpget";
@@ -581,6 +582,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 725850897..f158067ca 100644
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -495,25 +495,21 @@ 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'];
+ }
+ //$status = shell_exec($config['fping'] . "$fping_params -e $hostname 2>/dev/null");
$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'] < 0 || $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);
}
@@ -1269,3 +1265,55 @@ function ip_exists($ip) {
}
return true;
}
+
+function fping($host,$params) {
+
+ global $config;
+
+// $handle = popen($config['fping'] . ' -e -q ' .$params . ' ' .$host.' 2>&1', 'r');
+// sleep(0.2);
+// $read = fread($handle, 8192);
+// pclose($handle);
+
+$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);
+
+if (is_resource($process)) {
+
+ fwrite($pipes[0], $in);
+ /* fwrite writes to stdin, 'cat' will immediately write the data from stdin
+ * to stdout and blocks, when the stdout buffer is full. Then it will not
+ * continue reading from stdin and php will block here.
+ */
+
+ fclose($pipes[0]);
+
+ while (!feof($pipes[1])) {
+ $read .= fgets($pipes[1], 1024);
+ }
+ fclose($pipes[1]);
+
+ $return_value = proc_close($process);
+}
+
+ system("echo 'YEAH $read END\n' >> /tmp/testing");
+ system("echo '". $config['fping'] ." -e -q $params $host' >> /tmp/testing");
+ 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,$max,$avg) = 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..03ec28b0a 100644
--- a/includes/polling/functions.inc.php
+++ b/includes/polling/functions.inc.php
@@ -132,6 +132,15 @@ 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();
diff --git a/sql-schema/056.sql b/sql-schema/056.sql
new file mode 100644
index 000000000..f8617ab98
--- /dev/null
+++ b/sql-schema/056.sql
@@ -0,0 +1,3 @@
+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);