diff --git a/AUTHORS.md b/AUTHORS.md index 694329cd8..6ff840e07 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -56,5 +56,7 @@ Contributors to LibreNMS: - Christophe Martinet (chrisgfx) - Nils Steinger (n-st) - Mark Nagel (ciscoqid) +- Sławomir Paszkiewicz (paszczus) +- Frederik Mogensen (mogensen) [1]: http://observium.org/ "Observium web site" diff --git a/doc/Extensions/Alerting.md b/doc/Extensions/Alerting.md index 92d264e20..bd0b1a786 100644 --- a/doc/Extensions/Alerting.md +++ b/doc/Extensions/Alerting.md @@ -66,7 +66,7 @@ __Conditions__ can be any of: __Values__ can be Entities or any single-quoted data. __Glues__ can be either `&&` for `AND` or `||` for `OR`. -__Note__: The difference between `Equals` and `Matches` (and it's negation) is that `Equals` does a strict comparison and `Matches` allows the usage of the placeholder `@`. The placeholder `@` is comparable with `.*` in RegExp. +__Note__: The difference between `Equals` and `Matches` (and it's negation) is that `Equals` does a strict comparison and `Matches` allows the usage of RegExp. Arithmetics are allowed as well. ## Examples diff --git a/doc/Extensions/Device-Groups.md b/doc/Extensions/Device-Groups.md index cc48cdaad..c38db06a8 100644 --- a/doc/Extensions/Device-Groups.md +++ b/doc/Extensions/Device-Groups.md @@ -8,12 +8,12 @@ Patterns work in the same was as Entities within the alerting system, the format as __tablename.columnname__. If you are ensure of what the entity is you want then have a browse around inside MySQL using `show tables` and `desc `. As a working example and a common question, let's assume you want to group devices by hostname. If you hostname format is dcX.[devicetype].example.com. You would use the pattern -devices.hostname. Select the condition which in this case would Like and then enter dc1.@.example.com. This would then match dc1.sw01.example.com, dc1.rtr01.example.com but not +devices.hostname. Select the condition which in this case would Like and then enter `dc1\..*\.example.com`. This would then match dc1.sw01.example.com, dc1.rtr01.example.com but not dc2.sw01.example.com. #### Wildcards -As used in the example above, wildcards are represented by the @ symbol. I.e @.example.com would match any hostnames under example.com. +As with alerts, the `Like` operation allows RegExp. A list of common entities is maintained in our [Alerting docs](http://docs.librenms.org/Extensions/Alerting/#entities). diff --git a/doc/Extensions/Distributed-Poller.md b/doc/Extensions/Distributed-Poller.md index 86163b644..e6c3e2c9d 100644 --- a/doc/Extensions/Distributed-Poller.md +++ b/doc/Extensions/Distributed-Poller.md @@ -5,7 +5,11 @@ These machines can be in a different physical location and therefore minimize ne Devices can also be groupped together into a `poller_group` to pin these devices to a single or a group of designated pollers. -All pollers need to share their RRD-folder, for example via NFS or a combination of NFS and rrdcached. +~~All pollers need to share their RRD-folder, for example via NFS or a combination of NFS and rrdcached.~~ + +> This is no longer a strict requirement with the use of rrdtool 1.5 and above. If you are NOT running 1.5 then you will still +need to share the RRD-folder. + It is also required that all pollers can access the central memcached to communicate with eachother. In order to enable distributed polling, set `$config['distributed_poller'] = true` and your memcached details into `$config['distributed_poller_memcached_host']` and `$config['distributed_poller_memcached_port']`. @@ -49,7 +53,14 @@ The pollers, web and API layers should all be able to access the database server ####RRD Storage Central storage should be provided so all RRD files can be read from and written to in one location. As suggested above, it's recommended that RRD Cached is configured and used. -For this example, we are running RRDCached to allow all pollers and web/api servers to read/write to the rrd iles with the rrd directory also exported by NFS for simple access and maintenance. +For this example, we are running RRDCached to allow all pollers and web/api servers to read/write to the rrd iles ~~with the rrd directory also exported by NFS for simple access and maintenance.~~ + +Sharing rrd files via something like NFS is no longer required if you run rrdtool 1.5 or greater. If you don't - please share your rrd folder as before. If you run rrdtool +1.5 or greater then add this config to your pollers: + +```php +$config['rrdtool_version'] = 1.5; +``` ####Memcache Memcache is required for the distributed pollers to be able to register to a central location and record what devices are polled. Memcache can run from any of the kit so long as it is accessable by all pollers. @@ -88,4 +99,10 @@ $config['rrd_dir'] = "/opt/librenms/rrd"; $config['rrdcached_dir'] = ""; ``` +For rrdtool 1.5 or greater then you can enable support for rrdcached to create the rrd files: + +```php +$config['rrdtool_version'] = 1.5; +``` + $config['rrdcached_dir'] Is only needed if you are using tcp connections for rrd cached and needs only to be set if you want to store rrd files within a sub directory of your rrdcached base directory. diff --git a/doc/Extensions/Memcached.md b/doc/Extensions/Memcached.md new file mode 100644 index 000000000..ae55ff342 --- /dev/null +++ b/doc/Extensions/Memcached.md @@ -0,0 +1,16 @@ +# Memcached + +LibreNMS can store SQL results in memcached to achieve performance advantages of in-memory value storage and removing work load for frequent queries off the MySQL backend. + +To enable memcached in your install you need to have `memcached` installed and the PHP extension `php5-memcached` or `php-memcached` and add the following lines to your `config.php`: + +```php +$config['memcached']['enable'] = true; +$config['memcached']['host'] = "localhost"; +$config['memcached']['port'] = 11211; +``` + +By default values are kept for 4 Minutes inside the memcached, you can adjust this retention time by modifying the `$config['memcached']['ttl']` value to any desired amount of seconds. +It's strongly discouraged to set this above `300` (5 Minutes) to avoid interferences with the polling, discovery and alerting processes. + +If you use the Distributed Poller, you can point this to the same memcached instance. However a local memcached will perform better in any case. diff --git a/doc/Extensions/RRDCached.md b/doc/Extensions/RRDCached.md index 772fb6215..b1afff24b 100644 --- a/doc/Extensions/RRDCached.md +++ b/doc/Extensions/RRDCached.md @@ -2,6 +2,13 @@ This document will explain how to setup RRDCached for LibreNMS. +> If you are using rrdtool / rrdcached version 1.5 or above then this now supports creating rrd files over rrdcached. To +enable this set the following config: + +```php +$config['rrdtool_version'] = 1.5; +``` + ### RRDCached installation CentOS 6 This example is based on a fresh LibreNMS install, on a minimimal CentOS 6 installation. In this example, we'll use the Repoforge repository. @@ -34,7 +41,7 @@ service rrdcached start ``` - Edit /opt/librenms/config.php to include: -```ssh +```php $config['rrdcached'] = "unix:/var/run/rrdcached/rrdcached.sock"; ``` ### RRDCached installation CentOS 7 diff --git a/doc/Support/Configuration.md b/doc/Support/Configuration.md index 1694a4fb9..865b00b9b 100644 --- a/doc/Support/Configuration.md +++ b/doc/Support/Configuration.md @@ -46,7 +46,7 @@ $config['rrdtool'] = "/usr/bin/rrdtool"; ```php $config['fping'] = "/usr/bin/fping"; -$config['fping6'] = "/usr/bin/fping6"; +$config['fping6'] = "fping6"; $config['fping_options']['retries'] = 3; $config['fping_options']['timeout'] = 500; $config['fping_options']['count'] = 3; diff --git a/html/ajax_dash.php b/html/ajax_dash.php index ec88e68ec..893de7a1c 100644 --- a/html/ajax_dash.php +++ b/html/ajax_dash.php @@ -28,7 +28,7 @@ if (!$_SESSION['authenticated']) { $type = mres($_POST['type']); if ($type == 'placeholder') { - $output = 'Please add a Widget to get started'; + $output = "

Click on Edit-Dashboard to add Widgets


Remember: You can only move & rezise widgets when you're in Edit-Mode.

"; $status = 'ok'; $title = 'Placeholder'; } diff --git a/html/ajax_rulesuggest.php b/html/ajax_rulesuggest.php index bb49ecab2..cee773a2f 100644 --- a/html/ajax_rulesuggest.php +++ b/html/ajax_rulesuggest.php @@ -72,22 +72,16 @@ if (isset($_GET['term'],$_GET['device_id'])) { $_GET['device_id'] = mres($_GET['device_id']); if (strstr($_GET['term'], '.')) { $term = explode('.', $_GET['term']); - if ($config['memcached']['enable']) { - $chk = $memcache->get('rule-suggest_'.$term[0]); - } - - if (!(sizeof($chk) > 0) || $chk === false) { - if ($term[0] == 'macros') { - foreach ($config['alert']['macros']['rule'] as $macro => $v) { - $chk[] = 'macros.'.$macro; - } + 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']; - } + } + else { + $tmp = dbFetchRows('SHOW COLUMNS FROM '.$term[0]); + foreach ($tmp as $tst) { + if (isset($tst['Field'])) { + $chk[] = $term[0].'.'.$tst['Field']; } } } @@ -95,29 +89,18 @@ if (isset($_GET['term'],$_GET['device_id'])) { $current = true; } else { - if ($config['memcached']['enable']) { - $chk = $memcache->get('rule-suggest-toplvl'); + $tmp = dbFetchRows("SELECT TABLE_NAME FROM information_schema.COLUMNS WHERE COLUMN_NAME = 'device_id'"); + foreach ($tmp as $tst) { + $chk[] = $tst['TABLE_NAME'].'.'; } - if (!(sizeof($chk) > 0) || $chk === false) { - $tmp = dbFetchRows("SELECT TABLE_NAME FROM information_schema.COLUMNS WHERE COLUMN_NAME = 'device_id'"); - foreach ($tmp as $tst) { - $chk[] = $tst['TABLE_NAME'].'.'; - } - - $chk[] = 'macros.'; - $chk[] = 'bills.'; - } + $chk[] = 'macros.'; + $chk[] = 'bills.'; } if (sizeof($chk) > 0) { - if ($config['memcached']['enable']) { - $memcache->set('rule-suggest-'.$oterm, $chk, 86400); - // Cache for 24h - } - - $obj = levsort($_GET['term'], $chk); - $obj = array_chunk($obj, 20, true); - $obj = $obj[0]; + $obj = levsort($_GET['term'], $chk); + $obj = array_chunk($obj, 20, true); + $obj = $obj[0]; $flds = array(); if ($current === true) { foreach ($obj as $fld) { diff --git a/html/ajax_search.php b/html/ajax_search.php index 7d82a1ac9..fd6675682 100644 --- a/html/ajax_search.php +++ b/html/ajax_search.php @@ -318,6 +318,17 @@ if (isset($_REQUEST['search'])) { $json = json_encode($device); die($json); + } + else if ($_REQUEST['type'] == 'bill') { + // Device search + if (is_admin() === true || is_read() === true) { + $results = dbFetchRows("SELECT `bills`.bill_id, `bills`.bill_name FROM `bills` WHERE `bill_name` LIKE '%".$search."%' OR `bill_notes` LIKE '%".$search."%' LIMIT 8"); + } + else { + $results = dbFetchRows("SELECT `bills`.bill_id, `bills`.bill_name FROM `bills` INNER JOIN `bill_perms` ON `bills`.bill_id = `bill_perms`.bill_id WHERE `bill_perms`.user_id = ? AND (`bill_name` LIKE '%".$search."%' OR `bill_notes` LIKE '%".$search."%') LIMIT 8", array($_SESSION['user_id'])); + } + $json = json_encode($results); + die($json); }//end if }//end if }//end if diff --git a/html/css/styles.css b/html/css/styles.css index eb646b8b9..c7e55d4e6 100644 --- a/html/css/styles.css +++ b/html/css/styles.css @@ -683,7 +683,6 @@ table.simple { padding: 10px; } -table { border-collapse: collapse; margin: 0px; background-color:#ffffff } form { margin: 0px; padding: 0px; } hr { border: 0px; height: 1px; background-color: #ccc; color: #ccc; } p { margin: 0px; padding: 0px; } diff --git a/html/includes/authentication/mysql.inc.php b/html/includes/authentication/mysql.inc.php index 84e119cea..c16e716f8 100644 --- a/html/includes/authentication/mysql.inc.php +++ b/html/includes/authentication/mysql.inc.php @@ -3,7 +3,7 @@ function authenticate($username, $password) { $encrypted_old = md5($password); - $row = dbFetchRow('SELECT username,password FROM `users` WHERE `username`= ?', array($username)); + $row = dbFetchRow('SELECT username,password FROM `users` WHERE `username`= ?', array($username), true); if ($row['username'] && $row['username'] == $username) { // Migrate from old, unhashed password if ($row['password'] == $encrypted_old) { @@ -36,7 +36,7 @@ function authenticate($username, $password) { function reauthenticate($sess_id, $token) { list($uname,$hash) = explode('|', $token); - $session = dbFetchRow("SELECT * FROM `session` WHERE `session_username` = '$uname' AND session_value='$sess_id'"); + $session = dbFetchRow("SELECT * FROM `session` WHERE `session_username` = '$uname' AND session_value='$sess_id'", array(), true); $hasher = new PasswordHash(8, false); if ($hasher->CheckPassword($uname.$session['session_token'], $hash)) { $_SESSION['username'] = $uname; @@ -59,7 +59,7 @@ function passwordscanchange($username='') { return 1; } else { - return dbFetchCell('SELECT can_modify_passwd FROM users WHERE username = ?', array($username)); + return dbFetchCell('SELECT can_modify_passwd FROM users WHERE username = ?', array($username), true); } }//end passwordscanchange() @@ -114,20 +114,20 @@ function adduser($username, $password, $level, $email='', $realname='', $can_mod function user_exists($username) { - $return = @dbFetchCell('SELECT COUNT(*) FROM users WHERE username = ?', array($username)); + $return = @dbFetchCell('SELECT COUNT(*) FROM users WHERE username = ?', array($username), true); return $return; }//end user_exists() function get_userlevel($username) { - return dbFetchCell('SELECT `level` FROM `users` WHERE `username` = ?', array($username)); + return dbFetchCell('SELECT `level` FROM `users` WHERE `username` = ?', array($username), true); }//end get_userlevel() function get_userid($username) { - return dbFetchCell('SELECT `user_id` FROM `users` WHERE `username` = ?', array($username)); + return dbFetchCell('SELECT `user_id` FROM `users` WHERE `username` = ?', array($username), true); }//end get_userid() @@ -158,7 +158,7 @@ function can_update_users() { function get_user($user_id) { - return dbFetchRow('SELECT * FROM `users` WHERE `user_id` = ?', array($user_id)); + return dbFetchRow('SELECT * FROM `users` WHERE `user_id` = ?', array($user_id), true); }//end get_user() diff --git a/html/includes/common/generic-graph.inc.php b/html/includes/common/generic-graph.inc.php index e7cf4b9ed..87bb88373 100644 --- a/html/includes/common/generic-graph.inc.php +++ b/html/includes/common/generic-graph.inc.php @@ -66,6 +66,8 @@ if( defined('show_settings') || empty($widget_settings) ) { + +
@@ -140,6 +142,14 @@ if( defined('show_settings') || empty($widget_settings) ) { $common_output[] = '
+
+
+ +
+
+ +
+
@@ -301,6 +311,40 @@ function '.$unique_id.'() { } }); + var '.$unique_id.'_bill = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace("munin"), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: "ajax_search.php?search=%QUERY&type=bill", + filter: function (output) { + return $.map(output, function (item) { + return { + name: item.bill_name, + bill_id: item.bill_id, + }; + }); + }, + wildcard: "%QUERY" + } + }); + '.$unique_id.'_bill.initialize(); + $(".input_'.$unique_id.'_bill").typeahead({ + hint: true, + highlight: true, + minLength: 1, + classNames: { + menu: "typeahead-left" + } + }, + { + source: '.$unique_id.'_bill.ttAdapter(), + async: false, + templates: { + header: "
 Bill
", + suggestion: Handlebars.compile(\'

{{name}}

\') + } + }); + switch_'.$unique_id.'($("#select_'.$unique_id.'").val()); } function switch_'.$unique_id.'(data) { diff --git a/html/includes/functions.inc.php b/html/includes/functions.inc.php index 67d062b4c..df3ad76e4 100644 --- a/html/includes/functions.inc.php +++ b/html/includes/functions.inc.php @@ -222,7 +222,7 @@ function generate_device_link($device, $text=null, $vars=array(), $start=0, $end $url = generate_device_url($device, $vars); // beginning of overlib box contains large hostname followed by hardware & OS details - $contents = '
'.$device['hostname'].''; + $contents = '
'.$device['hostname'].''; if ($device['hardware']) { $contents .= ' - '.$device['hardware']; } @@ -248,8 +248,8 @@ function generate_device_link($device, $text=null, $vars=array(), $start=0, $end foreach ($graphs as $entry) { $graph = $entry['graph']; $graphhead = $entry['text']; - $contents .= '
'; - $contents .= ''.$graphhead.'
'; + $contents .= '
'; + $contents .= ''.$graphhead.'
'; $contents .= generate_minigraph_image($device, $start, $end, $graph); $contents .= generate_minigraph_image($device, $config['time']['week'], $end, $graph); $contents .= '
'; @@ -279,6 +279,7 @@ function generate_device_link($device, $text=null, $vars=array(), $start=0, $end function overlib_link($url, $text, $contents, $class) { global $config; + $contents = "
".$contents.'
'; $contents = str_replace('"', "\'", $contents); $output = '
  • Overview