From f8d7ccfe0dbe735b6caca13364fddf82c55aa16f Mon Sep 17 00:00:00 2001 From: Zmegolaz Date: Sat, 28 Oct 2017 05:59:25 +0200 Subject: [PATCH] feature: Support for up/down detection of ping only devices. * Added support for ping only devices. * Renamed sql-schema/206 to 207. * Discovery tried to detect the OS when it shouldn't. * Updated db_schema.yaml * Added ping icon. * Don't show unused graphs and tabs when SNMP is disabled. * Allow the user to specify OS of ping only devices. * Removing custom OS now changes it to 'ping'. * Removed unnecessary use of mres(). * UI select box for SNMP enable/disable. * Hide device_ping_perf if ping is disable on device. * Fixed SNMP settings update status messages. * Added functionality to add ping only devices via the web ui. * Added ping only option to addhost.php * Added ping only support to snmp-scan.py * Moved sql 208 to 211 * Fixed scrutinizer issues. * Fixed broken ossearch. * Added ping only support to the API. * Updated API doc. * Added (optional) to OS and hardware description. Hid Port Settings, Applications, Modules, Storage, Processors, Memory and Components from the edit menu * Style fix. * Updated ping icon. * clean() instead of mres(). More escaping. Better help in snmp-scan.py and addhost.php * Fixed scrutinizer issue. * Always try SNMP in snmp-scan.py, new option for it in addhost.php. Slice instead of chunk in ajax_ossuggest.php. Other minor style changes. * Updated sql modifications to insert the new column in the same place as in db_schema.yaml. --- addhost.php | 58 +++-- doc/API/index.md | 8 +- html/ajax_ossuggest.php | 69 ++++++ html/images/os/ping.svg | 1 + html/includes/api_functions.inc.php | 12 +- html/includes/device-header.inc.php | 4 +- html/includes/functions.inc.php | 4 +- html/pages/addhost.inc.php | 317 +++++++++++++++++---------- html/pages/device.inc.php | 14 +- html/pages/device/edit.inc.php | 20 +- html/pages/device/edit/snmp.inc.php | 225 +++++++++++++------ includes/common.php | 8 +- includes/definitions/ping.yaml | 5 + includes/discovery/functions.inc.php | 18 +- includes/functions.php | 24 +- includes/polling/functions.inc.php | 22 +- misc/db_schema.yaml | 1 + snmp-scan.py | 15 +- sql-schema/213.sql | 1 + tests/OSDiscoveryTest.php | 1 + 20 files changed, 578 insertions(+), 249 deletions(-) create mode 100644 html/ajax_ossuggest.php create mode 100644 html/images/os/ping.svg create mode 100644 includes/definitions/ping.yaml create mode 100644 sql-schema/213.sql diff --git a/addhost.php b/addhost.php index 14d29a4113..968a08b7ac 100755 --- a/addhost.php +++ b/addhost.php @@ -16,7 +16,7 @@ use LibreNMS\Exceptions\HostUnreachableException; $init_modules = array(); require __DIR__ . '/includes/init.php'; -$options = getopt('g:p:f::'); +$options = getopt('Pbg:p:f::'); if (isset($options['g']) && $options['g'] >= 0) { $cmd = array_shift($argv); @@ -55,6 +55,18 @@ if (isset($options['p'])) { array_unshift($argv, $cmd); } +if (isset($options['P'])) { + $cmd = array_shift($argv); + array_shift($argv); + array_unshift($argv, $cmd); +} + +if (isset($options['b'])) { + $cmd = array_shift($argv); + array_shift($argv); + array_unshift($argv, $cmd); +} + if (!empty($argv[1])) { $host = strtolower($argv[1]); $community = $argv[2]; @@ -63,7 +75,21 @@ if (!empty($argv[1])) { $port = 161; $transport = 'udp'; - if ($snmpver === 'v3') { + $additional = array(); + if (isset($options['b'])) { + $additional = array( + 'ping_fallback' => 1, + ); + } + if (isset($options['P'])) { + $community = ''; + $snmpver = 'v2c'; + $additional = array( + 'snmp_disable' => 1, + 'os' => $argv[2] ? mres($argv[2]) : "ping", + 'hardware' => $argv[3] ? mres($argv[3]) : '', + ); + } elseif ($snmpver === 'v3') { $seclevel = $community; // These values are the same as in defaults.inc.php @@ -163,7 +189,7 @@ if (!empty($argv[1])) { }//end if try { - $device_id = addHost($host, $snmpver, $port, $transport, $poller_group, $force_add, $port_assoc_mode); + $device_id = addHost($host, $snmpver, $port, $transport, $poller_group, $force_add, $port_assoc_mode, $additional); $device = device_by_id_cache($device_id); echo "Added device {$device['hostname']} ($device_id)\n"; exit(0); @@ -181,18 +207,22 @@ if (!empty($argv[1])) { c_echo( "\n".$config['project_name_version'].' Add Host Tool - Usage (SNMPv1/2c): ./addhost.php [-g ] [-f] [-p ] <%Whostname%n> [community] [v1|v2c] [port] ['.implode('|', $config['snmp']['transports']).'] - Usage (SNMPv3) : Config Defaults : ./addhost.php [-g ] [-f] [-p ] <%Whostname%n> any v3 [user] [port] ['.implode('|', $config['snmp']['transports']).'] - No Auth, No Priv : ./addhost.php [-g ] [-f] [-p ] <%Whostname%n> nanp v3 [user] [port] ['.implode('|', $config['snmp']['transports']).'] - Auth, No Priv : ./addhost.php [-g ] [-f] [-p ] <%Whostname%n> anp v3 [md5|sha] [port] ['.implode('|', $config['snmp']['transports']).'] - Auth, Priv : ./addhost.php [-g ] [-f] [-p ] <%Whostname%n> ap v3 [md5|sha] [aes|dsa] [port] ['.implode('|', $config['snmp']['transports']).'] + Usage (SNMPv1/2c) : ./addhost.php [-g ] [-f] [-b] [-p ] <%Whostname%n> [community] [v1|v2c] [port] ['.implode('|', $config['snmp']['transports']).'] + Usage (SNMPv3) : + Config Defaults : ./addhost.php [-g ] [-f] [-b] [-p ] <%Whostname%n> any v3 [user] [port] ['.implode('|', $config['snmp']['transports']).'] + No Auth, No Priv : ./addhost.php [-g ] [-f] [-b] [-p ] <%Whostname%n> nanp v3 [user] [port] ['.implode('|', $config['snmp']['transports']).'] + Auth, No Priv : ./addhost.php [-g ] [-f] [-b] [-p ] <%Whostname%n> anp v3 [md5|sha] [port] ['.implode('|', $config['snmp']['transports']).'] + Auth, Priv : ./addhost.php [-g ] [-f] [-b] [-p ] <%Whostname%n> ap v3 [md5|sha] [aes|dsa] [port] ['.implode('|', $config['snmp']['transports']).'] + Usage (ICMP only) : ./addhost.php [-g ] [-f] -P <%Whostname%n> [os] [hardware] - -g allows you to add a device to be pinned to a specific poller when using distributed polling. X can be any number associated with a poller group - -f forces the device to be added by skipping the icmp and snmp check against the host. - -p allow you to set a port association mode for this device. By default ports are associated by \'ifIndex\'. - For Linux/Unix based devices \'ifName\' or \'ifDescr\' might be useful for a stable iface mapping. - The default for this installation is \'' . $config['default_port_association_mode'] . '\' - Valid port assoc modes are: ' . join(', ', $valid_assoc_modes) . ' + -g allows you to add a device to be pinned to a specific poller when using distributed polling. X can be any number associated with a poller group + -f forces the device to be added by skipping the icmp and snmp check against the host. + -p allow you to set a port association mode for this device. By default ports are associated by \'ifIndex\'. + For Linux/Unix based devices \'ifName\' or \'ifDescr\' might be useful for a stable iface mapping. + The default for this installation is \'' . $config['default_port_association_mode'] . '\' + Valid port assoc modes are: ' . join(', ', $valid_assoc_modes) . ' + -b Add the host with SNMP if it replies to it, otherwise only ICMP. + -P Add the host with only ICMP, no SNMP or OS discovery. %rRemember to run discovery for the host afterwards.%n ' diff --git a/doc/API/index.md b/doc/API/index.md index d6c800e6fb..41971c39ae 100644 --- a/doc/API/index.md +++ b/doc/API/index.md @@ -744,6 +744,12 @@ For SNMP v3 - cryptopass: SNMP Crypto Password - cryptoalgo: SNMP Crypto algorithm (AES, DES) +For ICMP only + + - snmp_disable: Boolean, set to true for ICMP only. + - os: OS short name for the device (defaults to ping). + - hardware: Device hardware. + Example: ```curl curl -X POST -d '{"hostname":"localhost.localdomain","version":"v1","community":"public"}' -H 'X-Auth-Token: YOURAPITOKENHERE' https://librenms.org/api/v0/devices @@ -1975,4 +1981,4 @@ Output: } ] } -``` \ No newline at end of file +``` diff --git a/html/ajax_ossuggest.php b/html/ajax_ossuggest.php new file mode 100644 index 0000000000..159e5639a2 --- /dev/null +++ b/html/ajax_ossuggest.php @@ -0,0 +1,69 @@ + + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +session_start(); +if (!isset($_SESSION['authenticated'])) { + die('Unauthorized.'); +} + +$init_modules = array('web'); +require realpath(__DIR__ . '/..') . '/includes/init.php'; + +set_debug($_REQUEST['debug']); + +/** + * Levenshtein Sort + * @param string $base Comparison basis + * @param array $obj Object to sort + * @return array + */ +function levsortos($base, $obj, $keys) +{ + $ret = array(); + foreach ($obj as $elem) { + $lev = false; + foreach ($keys as $key) { + $levnew = levenshtein(strtolower($base), strtolower($elem[$key]), 1, 10, 10); + if ($lev === false || $levnew < $lev) { + $lev = $levnew; + } + } + while (isset($ret["$lev"])) { + $lev += 0.1; + } + + $ret["$lev"] = $elem; + } + + ksort($ret); + return $ret; +} + +header('Content-type: application/json'); +if (isset($_GET['term'])) { + $_GET['term'] = clean($_GET['term']); + $sortos = levsortos($_GET['term'], $config['os'], array("text", "os")); + $sortos = array_slice($sortos, 0, 20); + foreach ($sortos as $lev => $os) { + $ret[$lev] = array_intersect_key($os, array('os' => true, 'text' => true)); + } +} +if (!isset($ret)) { + $ret = array(array('Error: No suggestions found.')); +} + +die(json_encode($ret)); diff --git a/html/images/os/ping.svg b/html/images/os/ping.svg new file mode 100644 index 0000000000..dd893e28bb --- /dev/null +++ b/html/images/os/ping.svg @@ -0,0 +1 @@ + diff --git a/html/includes/api_functions.inc.php b/html/includes/api_functions.inc.php index 42bda11f25..277040e4b7 100644 --- a/html/includes/api_functions.inc.php +++ b/html/includes/api_functions.inc.php @@ -271,6 +271,7 @@ function add_device() // Default status & code to error and change it if we need to. $status = 'error'; $code = 500; + $additional = array(); // keep scrutinizer from complaining about snmpver not being set for all execution paths $snmpver = 'v2c'; if (empty($data)) { @@ -284,7 +285,14 @@ function add_device() $transport = $data['transport'] ? mres($data['transport']) : 'udp'; $poller_group = $data['poller_group'] ? mres($data['poller_group']) : 0; $force_add = $data['force_add'] ? true : false; - if ($data['version'] == 'v1' || $data['version'] == 'v2c') { + $snmp_disable = ($data['snmp_disable']); + if ($snmp_disable) { + $additional = array( + 'os' => $data['os'] ? mres($data['os']) : 'ping', + 'hardware' => $data['hardware'] ? mres($data['hardware']) : '', + 'snmp_disable' => 1, + ); + } elseif ($data['version'] == 'v1' || $data['version'] == 'v2c') { if ($data['community']) { $config['snmp']['community'] = array($data['community']); } @@ -309,7 +317,7 @@ function add_device() } if (empty($message)) { try { - $device_id = addHost($hostname, $snmpver, $port, $transport, $poller_group, $force_add); + $device_id = addHost($hostname, $snmpver, $port, $transport, $poller_group, $force_add, 'ifIndex', $additional); $code = 201; $status = 'ok'; $message = "Device $hostname ($device_id) has been added successfully"; diff --git a/html/includes/device-header.inc.php b/html/includes/device-header.inc.php index c118c8ab80..8aa5755042 100644 --- a/html/includes/device-header.inc.php +++ b/html/includes/device-header.inc.php @@ -16,7 +16,9 @@ echo ' //
'; -if (isset($config['os'][$device['os']]['over'])) { +if ($device['snmp_disable']) { + $graphs = $config['os']['ping']['over']; +} elseif (isset($config['os'][$device['os']]['over'])) { $graphs = $config['os'][$device['os']]['over']; } elseif (isset($device['os_group']) && isset($config['os'][$device['os_group']]['over'])) { $graphs = $config['os'][$device['os_group']]['over']; diff --git a/html/includes/functions.inc.php b/html/includes/functions.inc.php index 7050f48928..d285372faa 100644 --- a/html/includes/functions.inc.php +++ b/html/includes/functions.inc.php @@ -283,7 +283,9 @@ function generate_device_link($device, $text = null, $vars = array(), $start = 0 $text = format_hostname($device, $text); - if (isset($config['os'][$device['os']]['over'])) { + if ($device['snmp_disable']) { + $graphs = $config['os']['ping']['over']; + } elseif (isset($config['os'][$device['os']]['over'])) { $graphs = $config['os'][$device['os']]['over']; } elseif (isset($device['os_group']) && isset($config['os'][$device['os_group']]['over'])) { $graphs = $config['os'][$device['os_group']]['over']; diff --git a/html/pages/addhost.inc.php b/html/pages/addhost.inc.php index b744c80b72..0928581cd9 100644 --- a/html/pages/addhost.inc.php +++ b/html/pages/addhost.inc.php @@ -10,41 +10,51 @@ if ($_SESSION['userlevel'] < 10) { exit; } -if ($_POST['hostname']) { +$hostname = isset($_POST['hostname']) ? clean($_POST['hostname']) : false; +$snmp_enabled = isset($_POST['snmp']); + +if ($hostname !== false) { echo '
'; if ($_SESSION['userlevel'] > '5') { // Settings common to SNMPv2 & v3 - $hostname = mres($_POST['hostname']); if ($_POST['port']) { - $port = mres($_POST['port']); + $port = clean($_POST['port']); } else { $port = $config['snmp']['port']; } if ($_POST['transport']) { - $transport = mres($_POST['transport']); + $transport = clean($_POST['transport']); } else { $transport = 'udp'; } - if ($_POST['snmpver'] === 'v2c' or $_POST['snmpver'] === 'v1') { + $additional = array(); + if (!$snmp_enabled) { + $snmpver = 'v2c'; + $additional = array( + 'snmp_disable' => 1, + 'os' => $_POST['os'] ? mres($_POST['os_id']) : "ping", + 'hardware' => mres($_POST['hardware']), + ); + } elseif ($_POST['snmpver'] === 'v2c' || $_POST['snmpver'] === 'v1') { if ($_POST['community']) { - $config['snmp']['community'] = array($_POST['community']); + $config['snmp']['community'] = array(clean($_POST['community'])); } - $snmpver = mres($_POST['snmpver']); + $snmpver = clean($_POST['snmpver']); print_message("Adding host $hostname communit".(count($config['snmp']['community']) == 1 ? 'y' : 'ies').' '.implode(', ', $config['snmp']['community'])." port $port using $transport"); } elseif ($_POST['snmpver'] === 'v3') { $v3 = array( - 'authlevel' => mres($_POST['authlevel']), - 'authname' => mres($_POST['authname']), - 'authpass' => mres($_POST['authpass']), - 'authalgo' => mres($_POST['authalgo']), - 'cryptopass' => mres($_POST['cryptopass']), - 'cryptoalgo' => mres($_POST['cryptoalgo']), + 'authlevel' => clean($_POST['authlevel']), + 'authname' => clean($_POST['authname']), + 'authpass' => clean($_POST['authpass']), + 'authalgo' => clean($_POST['authalgo']), + 'cryptopass' => clean($_POST['cryptopass']), + 'cryptoalgo' => clean($_POST['cryptoalgo']), ); array_push($config['snmp']['v3'], $v3); @@ -54,12 +64,12 @@ if ($_POST['hostname']) { } else { print_error('Unsupported SNMP Version. There was a dropdown menu, how did you reach this error ?'); }//end if - $poller_group = $_POST['poller_group']; + $poller_group = clean($_POST['poller_group']); $force_add = ($_POST['force_add'] == 'on'); - $port_assoc_mode = $_POST['port_assoc_mode']; + $port_assoc_mode = clean($_POST['port_assoc_mode']); try { - $device_id = addHost($hostname, $snmpver, $port, $transport, $poller_group, $force_add, $port_assoc_mode); + $device_id = addHost($hostname, $snmpver, $port, $transport, $poller_group, $force_add, $port_assoc_mode, $additional); $link = generate_device_url(array('device_id' => $device_id)); print_message("Device added $hostname ($device_id)"); } catch (HostUnreachableException $e) { @@ -89,28 +99,50 @@ $pagetitle[] = 'Add host';

Add Device

-
Devices will be checked for Ping and SNMP reachability before being probed. Only devices with recognised OSes will be added.
+
Devices will be checked for Ping/SNMP reachability before being probed.
+
+ +
+ +
+
-
- -
- -
-
- -
-
- +
+
+
+ +
+ + +
+
+
+
+
+ +
+ +
+
+ +
+
+ -
-
-
- -
- +
+
+
+ +
+ -
-
-
-
-
- + +
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
-
- -
- -
-
-
-
-
-
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- + '; foreach (dbFetchRows('SELECT `id`,`group_name` FROM `poller_groups`') as $group) { @@ -224,9 +257,9 @@ if ($config['distributed_poller'] === true) { } echo ' - + +
-
'; }//endif ?> @@ -260,4 +293,62 @@ if ($config['distributed_poller'] === true) { } } $('#snmpv3').toggle(); + + function disableSnmp(e) { + if(e.checked) { + $('#snmp_conf').show(); + $('#snmp_override').hide(); + } else { + $('#snmp_conf').hide(); + $('#snmp_override').show(); + } + } + + var os_suggestions = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('text'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: "ajax_ossuggest.php?term=%QUERY", + filter: function (output) { + return $.map(output, function (item) { + return { + text: item.text, + os: item.os, + }; + }); + }, + wildcard: "%QUERY" + } + }); + os_suggestions.initialize(); + $('#os').typeahead({ + hint: true, + highlight: true, + minLength: 1, + classNames: { + menu: 'typeahead-left' + } + }, + { + source: os_suggestions.ttAdapter(), + async: true, + displayKey: 'text', + valueKey: 'os', + templates: { + suggestion: Handlebars.compile('

 {{text}}

') + }, + limit: 20 + }); + + $("#os").on("typeahead:selected typeahead:autocompleted", function(e,datum) { + $("#os_id").val(datum.os); + $("#os").html('' + datum.text + ''); + }); + + $("[name='snmp']").bootstrapSwitch('offColor','danger'); + diff --git a/html/pages/device.inc.php b/html/pages/device.inc.php index cb12f38265..0389de2e05 100644 --- a/html/pages/device.inc.php +++ b/html/pages/device.inc.php @@ -271,13 +271,13 @@ if (device_permitted($vars['device']) || $permitted_by_port) { '; } - - echo '
  • - - Neighbours - -
  • '; - + if (!$device['snmp_disable']) { + echo '
  • + + Neighbours + +
  • '; + } if (@dbFetchCell("SELECT 1 FROM stp WHERE device_id = '".$device['device_id']."'")) { echo '
  • diff --git a/html/pages/device/edit.inc.php b/html/pages/device/edit.inc.php index 70aac6ed35..c8d06c3ec2 100644 --- a/html/pages/device/edit.inc.php +++ b/html/pages/device/edit.inc.php @@ -12,16 +12,22 @@ if ($_SESSION['userlevel'] < '7') { } else { $panes['device'] = 'Device Settings'; $panes['snmp'] = 'SNMP'; - $panes['ports'] = 'Port Settings'; + if (!$device['snmp_disable']) { + $panes['ports'] = 'Port Settings'; + } if (count($config['os'][$device['os']]['icons'])) { $panes['icon'] = 'Icon'; } - $panes['apps'] = 'Applications'; + if (!$device['snmp_disable']) { + $panes['apps'] = 'Applications'; + } $panes['alerts'] = 'Alert Settings'; $panes['alert-rules'] = 'Alert Rules'; - $panes['modules'] = 'Modules'; + if (!$device['snmp_disable']) { + $panes['modules'] = 'Modules'; + } if ($config['show_services']) { $panes['services'] = 'Services'; @@ -37,9 +43,11 @@ if ($_SESSION['userlevel'] < '7') { $panes['wireless-sensors'] = 'Wireless Sensors'; } - $panes['storage'] = 'Storage'; - $panes['processors'] = 'Processors'; - $panes['mempools'] = 'Memory'; + if (!$device['snmp_disable']) { + $panes['storage'] = 'Storage'; + $panes['processors'] = 'Processors'; + $panes['mempools'] = 'Memory'; + } $panes['misc'] = 'Misc'; $panes['component'] = 'Components'; diff --git a/html/pages/device/edit/snmp.inc.php b/html/pages/device/edit/snmp.inc.php index 67fa327b51..9d42b890e5 100644 --- a/html/pages/device/edit/snmp.inc.php +++ b/html/pages/device/edit/snmp.inc.php @@ -2,95 +2,97 @@ if ($_POST['editing']) { if ($_SESSION['userlevel'] > '7') { - $no_checks = ($_POST['no_checks'] == 'on'); - $community = mres($_POST['community']); - $snmpver = mres($_POST['snmpver']); - $transport = $_POST['transport'] ? mres($_POST['transport']) : $transport = 'udp'; - $port = $_POST['port'] ? mres($_POST['port']) : $config['snmp']['port']; - $timeout = mres($_POST['timeout']); - $retries = mres($_POST['retries']); - $poller_group = isset($_POST['poller_group']) ? mres($_POST['poller_group']) : 0; - $port_assoc_mode = mres($_POST['port_assoc_mode']); - $max_repeaters = mres($_POST['max_repeaters']); - $max_oid = mres($_POST['max_oid']); - $v3 = array( - 'authlevel' => mres($_POST['authlevel']), - 'authname' => mres($_POST['authname']), - 'authpass' => mres($_POST['authpass']), - 'authalgo' => mres($_POST['authalgo']), - 'cryptopass' => mres($_POST['cryptopass']), - 'cryptoalgo' => mres($_POST['cryptoalgo']), - ); + $poller_group = isset($_POST['poller_group']) ? clean($_POST['poller_group']) : 0; + $snmp_enabled = ($_POST['snmp'] == 'on'); + if ($snmp_enabled) { + $no_checks = ($_POST['no_checks'] == 'on'); + $community = clean($_POST['community']); + $snmpver = clean($_POST['snmpver']); + $transport = $_POST['transport'] ? clean($_POST['transport']) : $transport = 'udp'; + $port = $_POST['port'] ? clean($_POST['port']) : $config['snmp']['port']; + $timeout = clean($_POST['timeout']); + $retries = clean($_POST['retries']); + $port_assoc_mode = clean($_POST['port_assoc_mode']); + $max_repeaters = clean($_POST['max_repeaters']); + $max_oid = clean($_POST['max_oid']); + $v3 = array( + 'authlevel' => clean($_POST['authlevel']), + 'authname' => clean($_POST['authname']), + 'authpass' => clean($_POST['authpass']), + 'authalgo' => clean($_POST['authalgo']), + 'cryptopass' => clean($_POST['cryptopass']), + 'cryptoalgo' => clean($_POST['cryptoalgo']), + ); - // FIXME needs better feedback - $update = array( - 'community' => $community, - 'snmpver' => $snmpver, - 'port' => $port, - 'transport' => $transport, - 'poller_group' => $poller_group, - 'port_association_mode' => $port_assoc_mode, - ); + // FIXME needs better feedback + $update = array( + 'community' => $community, + 'snmpver' => $snmpver, + 'port' => $port, + 'transport' => $transport, + 'poller_group' => $poller_group, + 'port_association_mode' => $port_assoc_mode, + 'snmp_disable' => 0, + ); - if ($_POST['timeout']) { - $update['timeout'] = $timeout; + if ($timeout) { + $update['timeout'] = $timeout; + } else { + $update['timeout'] = array('NULL'); + } + + if ($retries) { + $update['retries'] = $retries; + } else { + $update['retries'] = array('NULL'); + } + $update = array_merge($update, $v3); } else { - $update['timeout'] = array('NULL'); + $update['snmp_disable'] = 1; + $update['os'] = $_POST['os'] ? clean($_POST['os_id']) : "ping"; + $update['hardware'] = clean($_POST['hardware']); + $update['features'] = null; + $update['version'] = null; + $update['icon'] = null; } - if ($_POST['retries']) { - $update['retries'] = $retries; - } else { - $update['retries'] = array('NULL'); - } - - $update = array_merge($update, $v3); - $device_tmp = deviceArray($device['hostname'], $community, $snmpver, $port, $transport, $v3, $port_assoc_mode); - if ($no_checks === true || isSNMPable($device_tmp)) { + if ($no_checks === true || !$snmp_enabled || isSNMPable($device_tmp)) { $rows_updated = dbUpdate($update, 'devices', '`device_id` = ?', array($device['device_id'])); - $max_repeaters_set = false; - $max_oid_set = false; + $max_repeaters_set = 0; + $max_oid_set = 0; if (is_numeric($max_repeaters) && $max_repeaters != 0) { - set_dev_attrib($device, 'snmp_max_repeaters', $max_repeaters); - $max_repeaters_set = true; + $max_repeaters_set = set_dev_attrib($device, 'snmp_max_repeaters', $max_repeaters); } else { - del_dev_attrib($device, 'snmp_max_repeaters'); - $max_repeaters_set = true; + $max_repeaters_set = del_dev_attrib($device, 'snmp_max_repeaters'); } if (is_numeric($max_oid) && $max_oid != 0) { - set_dev_attrib($device, 'snmp_max_oid', $max_oid); - $max_oid_set = true; + $max_oid_set = set_dev_attrib($device, 'snmp_max_oid', $max_oid); } else { - del_dev_attrib($device, 'snmp_max_oid'); - $max_oid_set = true; + $max_oid_set = del_dev_attrib($device, 'snmp_max_oid'); } if ($rows_updated > 0) { - $update_message = $rows_updated.' Device record updated.'; - $updated = 1; - } elseif ($rows_updated = '-1') { - if ($max_repeaters_set === true || $max_repeaters_set === true) { - if ($max_repeaters_set === true) { - $update_message = 'SNMP Max repeaters updated, no other changes made'; - } - if ($max_oid_set === true) { - $update_message .= '
    SNMP Max OID updated, no other changes made'; - } - } else { - $update_message = 'Device record unchanged. No update necessary.'; - } - $updated = -1; - } else { - $update_message = 'Device record update error.'; - $updated = 0; + $update_message[] = $rows_updated.' Device record updated.'; + } + if ($max_repeaters_set) { + $update_message[] = 'SNMP Max repeaters updated.'; + } elseif ($max_repeaters_set === false) { + $update_failed_message[] = 'SNMP Max repeaters update failed.'; + } + if ($max_oid_set) { + $update_message[] = 'SNMP Max OID updated updated.'; + } elseif ($max_oid_set === false) { + $update_failed_message[] = 'SNMP Max OID updated failed.'; + } + if (!isset($update_message) && !isset($update_failed_message)) { + $update_message[] = 'Device record unchanged. No update necessary.'; } } else { - $update_message = 'Could not connect to device with new SNMP details'; - $updated = 0; + $update_failed_message[] = 'Could not connect to device with new SNMP details'; } }//end if }//end if @@ -98,10 +100,11 @@ if ($_POST['editing']) { $device = dbFetchRow('SELECT * FROM `devices` WHERE `device_id` = ?', array($device['device_id'])); $descr = $device['purpose']; -if ($updated && $update_message) { - print_message($update_message); -} elseif ($update_message) { - print_error($update_message); +if (isset($update_message)) { + print_message(join("
    ", $update_message)); +} +if (isset($update_failed_message)) { + print_error(join("
    ", $update_failed_message)); } $max_repeaters = get_dev_attrib($device, 'snmp_max_repeaters'); @@ -109,6 +112,28 @@ $max_oid = get_dev_attrib($device, 'snmp_max_oid'); echo " +
    + +
    + +
    +
    +
    +
    + +
    + +
    +
    +
    + +
    + + +
    +
    +
    +
    @@ -251,6 +276,7 @@ echo "
  • +
     {{text}}

    ') + }, + limit: 20 + }); + +$("#os").on("typeahead:selected typeahead:autocompleted", function(e,datum) { + $("#os_id").val(datum.os); + $("#os").html('' + datum.text + ''); +}); + +$("[name='snmp']").bootstrapSwitch('offColor','danger'); + $module_status) { $os_module_status = Config::getOsSetting($device['os'], "discovery_modules.$module"); diff --git a/includes/functions.php b/includes/functions.php index 8a84e4f026..206216d91e 100644 --- a/includes/functions.php +++ b/includes/functions.php @@ -459,6 +459,7 @@ function delete_device($id) * @param string $poller_group the poller group this device will belong to * @param boolean $force_add add even if the device isn't reachable * @param string $port_assoc_mode snmp field to use to determine unique ports + * @param array $additional an array with additional parameters to take into consideration when adding devices * * @return int returns the device_id of the added device * @@ -469,7 +470,7 @@ function delete_device($id) * @throws InvalidPortAssocModeException The given port association mode was invalid * @throws SnmpVersionUnsupportedException The given snmp version was invalid */ -function addHost($host, $snmp_version = '', $port = '161', $transport = 'udp', $poller_group = '0', $force_add = false, $port_assoc_mode = 'ifIndex') +function addHost($host, $snmp_version = '', $port = '161', $transport = 'udp', $poller_group = '0', $force_add = false, $port_assoc_mode = 'ifIndex', $additional = array()) { global $config; @@ -509,6 +510,9 @@ function addHost($host, $snmp_version = '', $port = '161', $transport = 'udp', $ $snmpvers = array($snmp_version); } + if (isset($additional['snmp_disable']) && $additional['snmp_disable'] == 1) { + return createHost($host, '', $snmp_version, $port, $transport, array(), $poller_group, 1, true, $additional); + } $host_unreachable_exception = new HostUnreachableException("Could not connect to $host, please check the snmp details and snmp reachability"); // try different snmp variables to add the device foreach ($snmpvers as $snmpver) { @@ -537,7 +541,11 @@ function addHost($host, $snmp_version = '', $port = '161', $transport = 'udp', $ throw new SnmpVersionUnsupportedException("Unsupported SNMP Version \"$snmpver\", must be v1, v2c, or v3"); } } - + if (isset($additional['ping_fallback']) && $additional['ping_fallback'] == 1) { + $additional['snmp_disable'] = 1; + $additional['os'] = "ping"; + return createHost($host, '', $snmp_version, $port, $transport, array(), $poller_group, 1, true, $additional); + } throw $host_unreachable_exception; } @@ -717,6 +725,7 @@ function getpollergroup($poller_group = '0') * @param int $poller_group distributed poller group to assign this host to * @param string $port_assoc_mode field to use to identify ports: ifIndex, ifName, ifDescr, ifAlias * @param bool $force_add Do not detect the host os + * @param array $additional an array with additional parameters to take into consideration when adding devices * @return int the id of the added host * @throws HostExistsException Throws this exception if the host already exists * @throws Exception Throws this exception if insertion into the database fails @@ -730,7 +739,8 @@ function createHost( $v3 = array(), $poller_group = 0, $port_assoc_mode = 'ifIndex', - $force_add = false + $force_add = false, + $additional = array() ) { $host = trim(strtolower($host)); @@ -745,7 +755,8 @@ function createHost( $device = array( 'hostname' => $host, 'sysName' => $host, - 'os' => 'generic', + 'os' => $additional['os'] ? $additional['os'] : 'generic', + 'hardware' => $additional['hardware'] ? $additional['hardware'] : null, 'community' => $community, 'port' => $port, 'transport' => $transport, @@ -754,6 +765,7 @@ function createHost( 'poller_group' => $poller_group, 'status_reason' => '', 'port_association_mode' => $port_assoc_mode, + 'snmp_disable' => $additional['snmp_disable'] ? $additional['snmp_disable'] : 0, ); $device = array_merge($device, $v3); // merge v3 settings @@ -2114,7 +2126,7 @@ function device_is_up($device, $record_perf = false) $response = array(); $response['ping_time'] = $ping_response['last_ping_timetaken']; if ($ping_response['result']) { - if (isSNMPable($device)) { + if ($device['snmp_disable'] || isSNMPable($device)) { $response['status'] = '1'; $response['status_reason'] = ''; } else { @@ -2128,7 +2140,7 @@ function device_is_up($device, $record_perf = false) $response['status_reason'] = 'icmp'; } - if ($device['status'] != $response['status']) { + if ($device['status'] != $response['status'] || $device['status_reason'] != $response['status_reason']) { dbUpdate( array('status' => $response['status'], 'status_reason' => $response['status_reason']), 'devices', diff --git a/includes/polling/functions.inc.php b/includes/polling/functions.inc.php index 7399bed584..055a07fac8 100644 --- a/includes/polling/functions.inc.php +++ b/includes/polling/functions.inc.php @@ -259,18 +259,22 @@ function poll_device($device, $options) $graphs = array(); $oldgraphs = array(); - // we always want the core module to be included - include 'includes/polling/core.inc.php'; - $force_module = false; - if ($options['m']) { - $config['poller_modules'] = array(); - foreach (explode(',', $options['m']) as $module) { - if (is_file('includes/polling/'.$module.'.inc.php')) { - $config['poller_modules'][$module] = 1; - $force_module = true; + if (!$device['snmp_disable']) { + // we always want the core module to be included + include 'includes/polling/core.inc.php'; + + if ($options['m']) { + $config['poller_modules'] = array(); + foreach (explode(',', $options['m']) as $module) { + if (is_file('includes/polling/'.$module.'.inc.php')) { + $config['poller_modules'][$module] = 1; + $force_module = true; + } } } + } else { + $config['poller_modules'] = array(); } foreach ($config['poller_modules'] as $module => $module_status) { $os_module_status = $config['os'][$device['os']]['poller_modules'][$module]; diff --git a/misc/db_schema.yaml b/misc/db_schema.yaml index 313ba02862..970463820b 100644 --- a/misc/db_schema.yaml +++ b/misc/db_schema.yaml @@ -402,6 +402,7 @@ devices: - { Field: transport, Type: varchar(16), 'Null': false, Extra: '', Default: udp } - { Field: timeout, Type: int(11), 'Null': true, Extra: '' } - { Field: retries, Type: int(11), 'Null': true, Extra: '' } + - { Field: snmp_disable, Type: tinyint(1), 'Null': false, Extra: '', Default: '0' } - { Field: bgpLocalAs, Type: varchar(16), 'Null': true, Extra: '' } - { Field: sysObjectID, Type: varchar(128), 'Null': true, Extra: '' } - { Field: sysDescr, Type: text, 'Null': true, Extra: '' } diff --git a/snmp-scan.py b/snmp-scan.py index 8ab3c3428a..c5f751669b 100755 --- a/snmp-scan.py +++ b/snmp-scan.py @@ -25,6 +25,7 @@ from __future__ import print_function from __future__ import unicode_literals import argparse +from argparse import RawTextHelpFormatter import json from collections import namedtuple from multiprocessing import Pool @@ -120,7 +121,7 @@ def scan_host(ip): pass try: - add_output = check_output(['/usr/bin/env', 'php', 'addhost.php', hostname or ip]) + add_output = check_output(['/usr/bin/env', 'php', 'addhost.php', args.ping, hostname or ip]) return Result(ip, hostname, Outcome.ADDED, add_output) except CalledProcessError as err: output = err.output.decode().rstrip() @@ -141,12 +142,14 @@ if __name__ == '__main__': ################### # Parse arguments # ################### - parser = argparse.ArgumentParser(description='Scan network for snmp hosts and add them to LibreNMS.') + parser = argparse.ArgumentParser(description='Scan network for snmp hosts and add them to LibreNMS.', formatter_class=RawTextHelpFormatter) parser.add_argument('network', action='append', nargs='*', type=str, help="""CIDR noted IP-Range to scan. Can be specified multiple times - This argument is only required if $config['nets'] is not set - Example: 192.168.0.0/24 - Example: 192.168.0.0/31 will be treated as an RFC3021 p-t-p network with two addresses, 192.168.0.0 and 192.168.0.1 - Example: 192.168.0.1/32 will be treated as a single host address""") +This argument is only required if $config['nets'] is not set +Example: 192.168.0.0/24 +Example: 192.168.0.0/31 will be treated as an RFC3021 p-t-p network with two addresses, 192.168.0.0 and 192.168.0.1 +Example: 192.168.0.1/32 will be treated as a single host address""") + parser.add_argument('-P', '--ping', action='store_const', const="-b", default="", help="""Add the device as an ICMP only device if it replies to ping but not SNMP. +Example: """ + __file__ + """ -P 192.168.0.0/24""") parser.add_argument('-t', dest='threads', type=int, help="How many IPs to scan at a time. More will increase the scan speed," + " but could overload your system. Default: {}".format(THREADS)) diff --git a/sql-schema/213.sql b/sql-schema/213.sql new file mode 100644 index 0000000000..983fa40865 --- /dev/null +++ b/sql-schema/213.sql @@ -0,0 +1 @@ +ALTER TABLE `devices` ADD `snmp_disable` TINYINT(1) NOT NULL DEFAULT 0 AFTER `retries`; diff --git a/tests/OSDiscoveryTest.php b/tests/OSDiscoveryTest.php index bbe83783e1..9d74c259f2 100644 --- a/tests/OSDiscoveryTest.php +++ b/tests/OSDiscoveryTest.php @@ -151,6 +151,7 @@ class OSDiscoveryTest extends \PHPUnit_Framework_TestCase $excluded_os = array( 'default', 'generic', + 'ping', ); $all_os = array_diff(array_keys($config['os']), $excluded_os);