Merge pull request #1273 from paulgear/mib-based-polling

MIB-based polling
This commit is contained in:
Daniel Preussker
2015-06-17 13:33:22 +00:00
25 changed files with 719 additions and 101 deletions
+1
View File
@@ -5,6 +5,7 @@ find . \
-path ./html/includes/geshi -prune -o \
-path ./html/includes/jpgraph -prune -o \
-path ./html/js/jqplot -prune -o \
-path ./junk -prune -o \
-path ./logs -prune -o \
-path ./mibs -prune -o \
-path ./rrd -prune -o \
+102
View File
@@ -0,0 +1,102 @@
## WARNING ##
MIB-based polling is experimental. It might overload your LibreNMS server,
destroy your data, set your routers on fire, and kick your cat. It has been
tested against a very limited set of devices (namely Ruckus ZD1000 wireless
controllers, and Net-SNMP on Linux). It may fail badly on other hardware.
The approach taken is fairly basic and I claim no special expertise in
understanding MIBs. Most of my understanding of SNMP comes from reading
net-snmp man pages, and reverse engineering the output of snmptranslate and
snmpwalk and trying to make devices work with LibreNMS. I may have made
false assumptions and probably use wrong terminology in many places. Feel
free to offer corrections/suggestions via pull requests or email.
Paul Gear <paul@librenms.org>
## Overview ##
MIB-based polling is disabled by default; you must set
`$config['poller_modules']['mib'] = 1;`
in config.php to enable it.
The components involved in of MIB-based support are:
### Discovery ###
- MIB-based detection is not involved; any work done here would have to be
duplicated by the poller and thus would only increase load.
### Polling ###
- The file `includes/snmp.inc.php` now contains code which can parse MIBs
using `snmptranslate` and use the data returned to populate an array
which guides the poller in what to store. At the moment, only OIDs with
Unsigned32 and Counter64 data types are parsed.
- `includes/polling/mib.inc.php` looks for a MIB matching sysObjectID in
the MIB directory; if one is found, it:
- parses it
- walks that MIB on the device
- stores any numeric results in individual RRD files
- updates/adds graph definitions in the previously-unused graph_types
database table
- Individual OSes (`includes/polling/os/*.inc.php`) can poll extra MIBs
for a given OS by calling poll_mib(). At the moment, this actually
happens before the general MIB polling.
- Devices may be excluded from MIB polling by changing the setting in the
device edit screen (`/device/device=ID/tab=edit/section=modules/`)
### Graphing ###
- For each graph type defined in the database, a graph will appear in:
Device -> Graphs -> MIB
- MIB graphs are generated generically by
`html/includes/graphs/device/mib.inc.php`
- At the moment, all units are placed in the same graph. This is probably
non-optimal for, e.g., wifi controllers with hundreds of APs attached.
## Adding/testing other device types ##
One of the goals of this work is to help take out the heavy lifting of
adding new device types. Even if you want fully customised graphs or
tables, you can use the automatic collection of MIBs to make it easy to
gather the data you want.
### How to add a new device MIB ###
1. Ensure the manufacturer's MIB is present in the mibs directory. If you
plan to submit your work to LibreNMS, make sure you attribute the source
of the MIB, including the exact download URL.
2. Check that `snmptranslate -Ts -M mibs -m MODULE | grep mibName` produces
a named list of OIDs. See the comments for `snmp_mib_walk()` in
`includes/snmp.inc.php` for an example.
3. Check that `snmptranslate -Td -On -M mibs -m MODULE MODULE::mibName`
produces a parsed description of the OID values. An example can be
found in the comments for `snmp_mib_parse()` in `includes/snmp.inc.php`.
4. Get the `sysObjectID` from a device, for example:```
snmpget -v2c -c public -OUsb -m SNMPv2-MIB -M /opt/librenms/mibs -t 30 hostname sysObjectID.0
```
5. Ensure `snmptranslate -m all -M /opt/librenms/mibs OID 2>/dev/null`
(where OID is the value returned for sysObjectID above) results in a
valid name for the MIB. See the comments for `snmp_translate()` in
`includes/snmp.inc.php` for an example. If this step fails, it means
there is something wrong with the MIB and net-snmp cannot parse it.
6. Add any additional MIBs you wish to poll for specific device types to
`includes/polling/os/OSNAME.inc.php` by calling `poll_mibs()` with the
MIB module and name. See includes/polling/os/ruckuswireless.inc.php for
an example.
7. That should be all you need to see MIB graphs!
## TODO ##
- Save the most recent MIB data in the database (including string types
which cannot be graphed). Display it in the appropriate places.
- Parse and save integer and timetick data types.
- Filter MIBs/OIDs from being polled and/or saved.
- Move graphs from the MIB section to elsewhere. e.g. There is already
specific support for wireless APs - this should be utilised, but isn't
yet.
- Combine multiple MIB values into graphs automatically on a predefined or
user-defined basis.
- Include MIB types in stats submissions.
+2 -1
View File
@@ -103,7 +103,8 @@ To go into a bit more detail, the following are usually needed:
**includes/definitions.inc.php**
Update this file to include the required definitions for the new OS.
**includes/discovery/os/ciscowlc.inc.php**
This file just sets the $os variable, done by checking the sysDescr snmp value for a particular value that matches the OS you are adding.
This file just sets the $os variable, done by checking the SNMP tree for a particular value that matches the OS you are adding. Typically, this will come from the presence of specific values in
sysObjectID or sysDescr, or the existence of a particular enterprise tree.
**includes/polling/os/ciscowlc.inc.php**
This file will usually set the variables for $version and $hardware gained from an snmp lookup.
**html/images/os/$os.png**
+5 -2
View File
@@ -70,6 +70,7 @@ $config['poller_modules']['aruba-controller'] = 1;
$config['poller_modules']['entity-physical'] = 1;
$config['poller_modules']['applications'] = 1;
$config['poller_modules']['cisco-asa-firewall'] = 1;
$config['poller_modules']['mib'] = 0;
```
#### Poller modules
@@ -112,7 +113,7 @@ $config['poller_modules']['cisco-asa-firewall'] = 1;
`ospf`: OSPF Support.
`cisco-ipsec-flow-monitor': IPSec statistics support.
`cisco-ipsec-flow-monitor`: IPSec statistics support.
`cisco-remote-access-monitor`: Cisco remote access support.
@@ -138,6 +139,8 @@ $config['poller_modules']['cisco-asa-firewall'] = 1;
`cisco-asa-firewall`: Cisco ASA firewall support.
`mib`: Support for generic MIB parsing.
#### Running
Here are some examples of running poller from within your install directory.
@@ -176,4 +179,4 @@ DB Updates
RRD Updates
SNMP Response
SNMP Response
+1
View File
@@ -436,6 +436,7 @@ function get_graphs() {
$router = $app->router()->getCurrentRoute()->getParams();
$hostname = $router['hostname'];
// FIXME: this has some overlap with html/pages/device/graphs.inc.php
// use hostname as device_id if it's all digits
$device_id = ctype_digit($hostname) ? $hostname : getidbyname($hostname);
$graphs = array();
+1
View File
@@ -2,6 +2,7 @@
/*
* LibreNMS front page graphs
*
* Author: Paul Gear
* Copyright (c) 2013 Gear Consulting Pty Ltd <http://libertysys.com.au/>
*
* This program is free software: you can redistribute it and/or modify it
@@ -3,6 +3,7 @@
* LibreNMS front page top devices graph
* - Find most utilised devices that have been polled in the last N minutes
*
* Author: Paul Gear
* Copyright (c) 2013 Gear Consulting Pty Ltd <http://libertysys.com.au/>
*
* This program is free software: you can redistribute it and/or modify it
+1
View File
@@ -3,6 +3,7 @@
* LibreNMS front page top ports graph
* - Find most utilised ports that have been polled in the last N minutes
*
* Author: Paul Gear
* Copyright (c) 2013 Gear Consulting Pty Ltd <http://libertysys.com.au/>
*
* This program is free software: you can redistribute it and/or modify it
+3 -3
View File
@@ -2,9 +2,9 @@
if ($auth || device_permitted($device['device_id']))
{
$title = generate_device_link($device);
$graph_title = $device['hostname'];
$auth = TRUE;
$title = generate_device_link($device);
$graph_title = $device['hostname'];
$auth = true;
}
?>
+36
View File
@@ -0,0 +1,36 @@
<?php
/*
* LibreNMS MIB-based polling
*
* Author: Paul Gear
* Copyright (c) 2015 Gear Consulting Pty Ltd <github@libertysys.com.au>
*
* 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.
*/
$rrd_list = array();
$prefix = rrd_name($device['hostname'], array($subtype, ""), "");
foreach (glob($prefix."*.rrd") as $filename) {
// find out what * expanded to
$globpart = str_replace($prefix, '', $filename); // take off the prefix
$instance = substr($globpart, 0, -4); // take off ".rrd"
$ds = array();
$mibparts = explode("-", $subtype);
$mibvar = end($mibparts);
$ds['ds'] = name_shorten($mibvar);
$ds['descr'] = "$mibvar-$instance";
$ds['filename'] = $filename;
$rrd_list[] = $ds;
}
$colours = 'mixed';
$scale_min = "0";
$nototal = 0;
$simple_rrd = true;
include("includes/graphs/generic_multi_line.inc.php");
+7 -28
View File
@@ -38,37 +38,16 @@ $graphfile = $config['temp_dir'] . "/" . strgen() . ".png";
$type = $graphtype['type'];
$subtype = $graphtype['subtype'];
if (is_file($config['install_dir'] . "/html/includes/graphs/$type/$subtype.inc.php"))
{
$auth = is_client_authorized($_SERVER['REMOTE_ADDR']);
include($config['install_dir'] . "/html/includes/graphs/$type/auth.inc.php");
if (isset($config['allow_unauth_graphs']) && $config['allow_unauth_graphs'])
{
$auth = "1"; // hardcode auth for all with config function
}
if (isset($config['allow_unauth_graphs_cidr']) && count($config['allow_unauth_graphs_cidr']) > 0)
{
foreach ($config['allow_unauth_graphs_cidr'] as $range)
{
if (Net_IPv4::ipInNetwork($_SERVER['REMOTE_ADDR'], $range))
{
$auth = "1";
if ($debug) { echo("matched $range"); }
break;
}
}
}
include($config['install_dir'] . "/html/includes/graphs/$type/auth.inc.php");
if (isset($auth) && $auth)
{
if ($auth === true && is_file($config['install_dir'] . "/html/includes/graphs/$type/$subtype.inc.php")) {
include($config['install_dir'] . "/html/includes/graphs/$type/$subtype.inc.php");
}
}
else
{
elseif ($auth === true && is_mib_graph($type, $subtype)) {
include($config['install_dir'] . "/html/includes/graphs/$type/mib.inc.php");
}
else {
graph_error("$type*$subtype ");//Graph Template Missing");
}
+5 -6
View File
@@ -1,6 +1,5 @@
<?php
// Sections are printed in the order they exist in $config['graph_sections']
// Graphs are printed in the order they exist in $config['graph_types']
$link_array = array('page' => 'device',
@@ -15,19 +14,19 @@ print_optionbar_start();
echo("<span style='font-weight: bold;'>Graphs</span> &#187; ");
$sep = "";
foreach (dbFetchRows("SELECT * FROM device_graphs WHERE device_id = ? ORDER BY graph", array($device['device_id'])) as $graph)
{
$section = $config['graph_types']['device'][$graph['graph']]['section'];
$graph_enable[$section][$graph['graph']] = $graph['graph'];
if ($section != "") {
$graph_enable[$section][$graph['graph']] = $graph['graph'];
}
}
// These are standard graphs we should have for all systems
$graph_enable['poller']['poller_perf'] = 'device_poller_perf';
$graph_enable['poller']['ping_perf'] = 'device_ping_perf';
#foreach ($config['graph_sections'] as $section)
$sep = "";
foreach ($graph_enable as $section => $nothing)
{
if (isset($graph_enable) && is_array($graph_enable[$section]))
@@ -47,8 +46,8 @@ foreach ($graph_enable as $section => $nothing)
$sep = " | ";
}
}
unset ($sep);
print_optionbar_end();
$graph_enable = $graph_enable[$vars['group']];
-1
View File
@@ -1,6 +1,5 @@
<?php
// Sections are printed in the order they exist in $config['graph_sections']
// Graphs are printed in the order they exist in $config['graph_types']
$link_array = array('page' => 'device',
+6 -17
View File
@@ -45,20 +45,6 @@ if (!$auth)
$title .= " :: ".ucfirst($subtype);
}
# Load our list of available graphtypes for this object
// FIXME not all of these are going to be valid
if ($handle = opendir($config['install_dir'] . "/html/includes/graphs/".$type."/"))
{
while (false !== ($file = readdir($handle)))
{
if ($file != "." && $file != ".." && $file != "auth.inc.php" &&strstr($file, ".inc.php"))
{
$types[] = str_replace(".inc.php", "", $file);
}
}
closedir($handle);
}
$graph_array = $vars;
$graph_array['height'] = "60";
$graph_array['width'] = $thumb_width;
@@ -75,11 +61,14 @@ if (!$auth)
onchange="window.open(this.options[this.selectedIndex].value,'_top')" >
<?php
foreach ($types as $avail_type)
foreach (get_graph_subtypes($type) as $avail_type)
{
echo("<option value='".generate_url($vars, array('type' => $type."_".$avail_type, 'page' => "graphs"))."'");
if ($avail_type == $subtype) { echo(" selected"); }
echo(">".nicecase($avail_type)."</option>");
if ($avail_type == $subtype) {
echo(" selected");
}
$display_type = is_mib_graph($type, $avail_type) ? $avail_type : nicecase($avail_type);
echo(">$display_type</option>");
}
?>
</select>
+157 -2
View File
@@ -1,6 +1,20 @@
<?php
// Common Functions
/*
* LibreNMS - Common Functions
*
* Original Observium version by: Adam Armstrong, Tom Laermans
* Copyright (c) 2009-2012 Adam Armstrong.
*
* Additions for LibreNMS by: Neil Lathwood, Paul Gear, Tim DuFrane
* Copyright (c) 2014-2015 Neil Lathwood <http://www.lathwood.co.uk>
* Copyright (c) 2014-2015 Gear Consulting Pty Ltd <http://libertysys.com.au/>
*
* 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.
*/
function format_number_short($number, $sf)
{
@@ -476,6 +490,19 @@ function get_dev_attrib($device, $attrib_type)
}
}
function is_dev_attrib_enabled($device, $attrib, $default = true)
{
$val = get_dev_attrib($device, $attrib);
if ($val != NULL) {
// attribute is set
return ($val != 0);
}
else {
// attribute not set
return $default;
}
}
function del_dev_attrib($device, $attrib_type)
{
return dbDelete('devices_attribs', "`device_id` = ? AND `attrib_type` = ?", array($device['device_id'], $attrib_type));
@@ -592,4 +619,132 @@ function edit_service($service, $descr, $service_ip, $service_param = "", $servi
}
/*
* convenience function - please use this instead of 'if ($debug) { echo ...; }'
*/
function d_echo($text, $no_debug_text = null)
{
global $debug;
if ($debug) {
echo "$text";
}
elseif ($no_debug_text) {
echo "$no_debug_text";
}
}
/*
* convenience function - please use this instead of 'if ($debug) { print_r ...; }'
*/
function d_print_r($var, $no_debug_text = null)
{
global $debug;
if ($debug) {
print_r($var);
}
elseif ($no_debug_text) {
echo "$no_debug_text";
}
}
/*
* Shorten the name so it works as an RRD data source name (limited to 19 chars by default).
* Substitute for $subst if necessary.
* @return the shortened name
*/
function name_shorten($name, $common = null, $subst = "mibval", $len = 19)
{
if ($common !== null) {
// remove common from the beginning of the string, if present
if (strlen($name) > $len && strpos($name, $common) >= 0) {
$newname = str_replace($common, '', $name);
$name = $newname;
}
}
if (strlen($name) > $len) {
$name = $subst;
}
return $name;
}
/*
* @return the name of the rrd file for $host's $extra component
* @param host Host name
* @param extra Components of RRD filename - will be separated with "-"
*/
function rrd_name($host, $extra, $exten = ".rrd")
{
global $config;
$filename = safename(is_array($extra) ? implode("-", $extra) : $extra);
return implode("/", array($config['rrd_dir'], $host, $filename.$exten));
}
/*
* @return true if the given graph type is a dynamic MIB graph
*/
function is_mib_graph($type, $subtype)
{
global $config;
return $config['graph_types'][$type][$subtype]['section'] == 'mib';
}
/*
* @return true if client IP address is authorized to access graphs
*/
function is_client_authorized($clientip)
{
global $config;
if (isset($config['allow_unauth_graphs']) && $config['allow_unauth_graphs']) {
d_echo("Unauthorized graphs allowed\n");
return true;
}
if (isset($config['allow_unauth_graphs_cidr'])) {
foreach ($config['allow_unauth_graphs_cidr'] as $range) {
if (Net_IPv4::ipInNetwork($clientip, $range)) {
d_echo("Unauthorized graphs allowed from $range\n");
return true;
}
}
}
return false;
}
/*
* @return an array of all graph subtypes for the given type
* FIXME not all of these are going to be valid
*/
function get_graph_subtypes($type)
{
global $config;
$types = array();
// find the subtypes defined in files
if ($handle = opendir($config['install_dir'] . "/html/includes/graphs/$type/")) {
while (false !== ($file = readdir($handle))) {
if ($file != "." && $file != ".." && $file != "auth.inc.php" && strstr($file, ".inc.php")) {
$types[] = str_replace(".inc.php", "", $file);
}
}
closedir($handle);
}
// find the MIB subtypes
foreach ($config['graph_types'] as $type => $unused1) {
print_r($type);
foreach ($config['graph_types'][$type] as $subtype => $unused2) {
print_r($subtype);
if (is_mib_graph($type, $subtype)) {
$types[] = $subtype;
}
}
}
sort($types);
return $types;
}
?>
+1
View File
@@ -533,6 +533,7 @@ $config['poller_modules']['aruba-controller'] = 1;
$config['poller_modules']['entity-physical'] = 1;
$config['poller_modules']['applications'] = 1;
$config['poller_modules']['cisco-asa-firewall'] = 1;
$config['poller_modules']['mib'] = 0;
// List of discovery modules. Need to be in this array to be
// considered for execution.
+1 -1
View File
@@ -1176,7 +1176,7 @@ foreach ($config['os'] as $this_os => $blah)
// Graph Types
$config['graph_sections'] = array('general', 'system', 'firewall', 'netstats', 'wireless', 'storage', 'vpdn', 'load balancer');
include_once($config['install_dir'] . "/includes/load_db_graph_types.inc.php");
// Device - Wireless - AirMAX
-20
View File
@@ -1247,23 +1247,3 @@ function ip_exists($ip) {
}
return true;
}
/*
* convenience function - please use this instead of 'if ($debug) { echo ...; }'
*/
function d_echo($text) {
global $debug;
if ($debug) {
echo "$text\n";
}
}
/*
* convenience function - please use this instead of 'if ($debug) { print_r ...; }'
*/
function d_print_r($var) {
global $debug;
if ($debug) {
print_r($var);
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
/*
* LibreNMS dynamic graph types
*
* Author: Paul Gear
* Copyright (c) 2015 Gear Consulting Pty Ltd <github@libertysys.com.au>
*
* 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.
*/
// load graph types from the database
foreach (dbFetchRows('select * from graph_types') as $graph) {
// remove leading 'graph_' from column names
foreach ($graph as $k => $v) {
$key = strpos($k, 'graph_') == 0 ? str_replace('graph_', '', $k) : $k;
$g[$key] = $v;
}
$config['graph_types'][$g['type']][$g['subtype']] = $g;
}
+28 -4
View File
@@ -183,6 +183,7 @@ function poll_device($device, $options)
{
if ($attribs['poll_'.$module] || ( $module_status && !isset($attribs['poll_'.$module])))
{
// TODO per-module polling stats
include('includes/polling/'.$module.'.inc.php');
} elseif (isset($attribs['poll_'.$module]) && $attribs['poll_'.$module] == "0") {
echo("Module [ $module ] disabled on host.\n");
@@ -201,11 +202,11 @@ function poll_device($device, $options)
foreach (dbFetch("SELECT `graph` FROM `device_graphs` WHERE `device_id` = ?", array($device['device_id'])) as $graph)
{
if (!isset($graphs[$graph["graph"]]))
if (isset($graphs[$graph["graph"]]))
{
dbDelete('device_graphs', "`device_id` = ? AND `graph` = ?", array($device['device_id'], $graph["graph"]));
} else {
$oldgraphs[$graph["graph"]] = TRUE;
} else {
dbDelete('device_graphs', "`device_id` = ? AND `graph` = ?", array($device['device_id'], $graph["graph"]));
}
}
@@ -222,6 +223,7 @@ function poll_device($device, $options)
$device_end = utime(); $device_run = $device_end - $device_start; $device_time = substr($device_run, 0, 5);
// TODO: These should be easy converts to rrd_create_update()
// Poller performance rrd
$poller_rrd = $config['rrd_dir'] . "/" . $device['hostname'] . "/poller-perf.rrd";
if (!is_file($poller_rrd))
@@ -266,7 +268,7 @@ function poll_mib_def($device, $mib_name_table, $mib_subdir, $mib_oids, $mib_gra
global $config;
echo("This is mag_poll_mib_def Processing\n");
echo("This is poll_mib_def Processing\n");
$mib = NULL;
if (stristr($mib_name_table, "UBNT")) {
@@ -346,4 +348,26 @@ function poll_mib_def($device, $mib_name_table, $mib_subdir, $mib_oids, $mib_gra
return TRUE;
}
/*
* Please use this instead of creating & updating RRD files manually.
* @param device Device object - only 'hostname' is used at present
* @param name Array of rrdname components
* @param def Array of data definitions
* @param val Array of value definitions
*
*/
function rrd_create_update($device, $name, $def, $val, $step = 300)
{
global $config;
$rrd = rrd_name($device['hostname'], $name);
if (!is_file($rrd) && $def != null) {
// add the --step and the rra definitions to the array
$newdef = "--step $step ".implode(' ', $def).$config['rrd_rra'];
rrdtool_create($rrd, $newdef);
}
rrdtool_update($rrd, $val);
}
?>
+16
View File
@@ -0,0 +1,16 @@
<?php
/*
* LibreNMS MIB-based polling
*
* Author: Paul Gear
* Copyright (c) 2015 Gear Consulting Pty Ltd <github@libertysys.com.au>
*
* 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.
*/
$devicemib = array($device['sysObjectID'] => "all");
poll_mibs($devicemib, $device, $graphs);
@@ -2,8 +2,12 @@
/*
* LibreNMS Ruckus Wireless OS information module
*
* Originally by:
* Copyright (c) 2015 Søren Friis Rosiak <sorenrosiak@gmail.com>
*
* Updates by Paul Gear:
* Copyright (c) 2015 Gear Consulting Pty Ltd <github@libertysys.com.au>
*
* 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
@@ -39,4 +43,12 @@ $ruckuscountry = first_oid_match($device, $ruckuscountries);
if (isset($ruckuscountry) && $ruckuscountry != "") {
$version .= " ($ruckuscountry)";
}
$ruckus_mibs = array(
"ruckusZDSystemStats" => "RUCKUS-ZD-SYSTEM-MIB",
"ruckusZDWLANTable" => "RUCKUS-ZD-WLAN-MIB",
"ruckusZDWLANAPTable" => "RUCKUS-ZD-WLAN-MIB",
);
poll_mibs($ruckus_mibs, $device, $graphs);
?>
+6
View File
@@ -113,6 +113,12 @@
log_event("Contact -> ".$poll_device['sysContact'], $device, 'system');
}
if ($poll_device['sysObjectID'] && $poll_device['sysObjectID'] != $device['sysObjectID'])
{
$update_array['sysObjectID'] = $poll_device['sysObjectID'];
log_event("ObjectID -> ".$poll_device['sysObjectID'], $device, 'system');
}
if ($poll_device['sysName'] && $poll_device['sysName'] != $device['sysName'])
{
$update_array['sysName'] = $poll_device['sysName'];
+300 -16
View File
@@ -322,9 +322,9 @@ function snmp_cache_ifIndex($device)
return $array;
}
function snmpwalk_cache_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL)
function snmpwalk_cache_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL, $snmpflags = "-OQUs")
{
$data = snmp_walk($device, $oid, "-OQUs", $mib, $mibdir);
$data = snmp_walk($device, $oid, $snmpflags, $mib, $mibdir);
foreach (explode("\n", $data) as $entry)
{
list($oid,$value) = explode("=", $entry, 2);
@@ -345,20 +345,7 @@ function snmpwalk_cache_oid($device, $oid, $array, $mib = NULL, $mibdir = NULL)
// to be the same.
function snmpwalk_cache_oid_num($device, $oid, $array, $mib = NULL, $mibdir = NULL)
{
$data = snmp_walk($device, $oid, "-OQUn", $mib, $mibdir);
foreach (explode("\n", $data) as $entry)
{
list($oid,$value) = explode("=", $entry, 2);
$oid = trim($oid); $value = trim($value);
list($oid, $index) = explode(".", $oid, 2);
if (!strstr($value, "at this OID") && isset($oid) && isset($index))
{
$array[$index][$oid] = $value;
}
}
return $array;
return snmpwalk_cache_oid($device, $oid, $array, $mib, $mibdir, $snmpflags = "-OQUn");
}
@@ -818,4 +805,301 @@ function snmp_gen_auth (&$device)
return $cmd;
}
/*
* Translate the given MIB into a vaguely useful PHP array. Each keyword becomes an array index.
*
* Example:
* snmptranslate -Td -On -M mibs -m RUCKUS-ZD-SYSTEM-MIB RUCKUS-ZD-SYSTEM-MIB::ruckusZDSystemStatsNumSta
* .1.3.6.1.4.1.25053.1.2.1.1.1.15.30
* ruckusZDSystemStatsAllNumSta OBJECT-TYPE
* -- FROM RUCKUS-ZD-SYSTEM-MIB
* SYNTAX Unsigned32
* MAX-ACCESS read-only
* STATUS current
* DESCRIPTION "Number of All client devices"
* ::= { iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) ruckusRootMIB(25053) ruckusObjects(1) ruckusZD(2) ruckusZDSystemModule(1) ruckusZDSystemMIB(1) ruckusZDSystemObjects(1)
* ruckusZDSystemStats(15) 30 }
*/
function snmp_mib_parse($oid, $mib, $module, $mibdir = null)
{
global $debug;
$fulloid = explode(".", $oid);
$lastpart = end($fulloid);
$cmd = "snmptranslate -Td -On";
$cmd .= mibdir($mibdir);
$cmd .= " -m ".$module." ".$module."::";
$cmd .= $lastpart;
$result = array();
$lines = preg_split('/\n+/', trim(shell_exec($cmd)));
foreach ($lines as $l) {
$f = preg_split('/\s+/', trim($l));
// first line is all numeric
if (preg_match('/^[\d.]+$/', $f[0])) {
$result['oid'] = $f[0];
continue;
}
// then the name of the object type
if ($f[1] && $f[1] == "OBJECT-TYPE") {
$result['object_type'] = $f[0];
$result['shortname'] = str_replace($mib, '', $f[0]);
$result['dsname'] = name_shorten($f[0], $mib);
continue;
}
// then the other data elements
if ($f[0] == "--" && $f[1] == "FROM") {
$result['module'] = $f[2];
continue;
}
if ($f[0] == "MAX-ACCESS") {
$result['max_access'] = $f[1];
continue;
}
if ($f[0] == "STATUS") {
$result[strtolower($f[0])] = $f[1];
continue;
}
if ($f[0] == "SYNTAX") {
$result[strtolower($f[0])] = $f[1];
continue;
}
if ($f[0] == "DESCRIPTION") {
$desc = explode('"', $l);
if ($desc[1]) {
$str = preg_replace('/^[\s.]*/', '', $desc[1]);
$str = preg_replace('/[\s.]*$/', '', $str);
$result[strtolower($f[0])] = $str;
}
continue;
}
}
// The main mib entry doesn't have any useful data in it - only return items that have the syntax specified.
if (isset($result['syntax']) && isset($result['object_type'])) {
$result['mib'] = $mib;
return $result;
}
else {
return null;
}
}
/*
* Walks through the given MIB module, looking for the given MIB.
* NOTE: different from snmp walk - this doesn't touch the device.
* NOTE: There's probably a better way to do this with snmptranslate.
*
* Example:
* snmptranslate -Ts -M mibs -m RUCKUS-ZD-SYSTEM-MIB | grep ruckusZDSystemStats
* .iso.org.dod.internet.private.enterprises.ruckusRootMIB.ruckusObjects.ruckusZD.ruckusZDSystemModule.ruckusZDSystemMIB.ruckusZDSystemObjects.ruckusZDSystemStats
* .iso.org.dod.internet.private.enterprises.ruckusRootMIB.ruckusObjects.ruckusZD.ruckusZDSystemModule.ruckusZDSystemMIB.ruckusZDSystemObjects.ruckusZDSystemStats.ruckusZDSystemStatsNumAP
* .iso.org.dod.internet.private.enterprises.ruckusRootMIB.ruckusObjects.ruckusZD.ruckusZDSystemModule.ruckusZDSystemMIB.ruckusZDSystemObjects.ruckusZDSystemStats.ruckusZDSystemStatsNumSta
* ...
*/
function snmp_mib_walk($mib, $module, $mibdir = null)
{
$cmd = "snmptranslate -Ts";
$cmd .= mibdir($mibdir);
$cmd .= " -m ".$module;
$result = array();
$data = preg_split('/\n+/', shell_exec($cmd));
foreach ($data as $oid) {
// only include oids which are part of this mib
if (strstr($oid, $mib)) {
$obj = snmp_mib_parse($oid, $mib, $module, $mibdir);
if ($obj) {
$result[] = $obj;
}
}
}
return $result;
}
/*
* @return an array containing all of the mib objects, keyed by object-type;
* returns an empty array if something goes wrong.
*/
function snmp_mib_load($mib, $module, $mibdir = null)
{
$mibs = array();
foreach (snmp_mib_walk($mib, $module, $mibdir) as $obj) {
$mibs[$obj['object_type']] = $obj;
}
return $mibs;
}
/*
* Turn the given oid (name or numeric value) into a MODULE::mib name.
* @return an array consisting of the module and mib names, or null if no matching MIB is found.
* Example:
* snmptranslate -m all -M mibs .1.3.6.1.4.1.8072.3.2.10 2>/dev/null
* NET-SNMP-TC::linux
*/
function snmp_translate($oid, $module, $mibdir = null)
{
if ($module !== "all") {
$oid = "$module::$oid";
}
$cmd = "snmptranslate" . mibdir($mibdir);
$cmd .= " -m $module $oid"; // load all the MIBs looking for our object
$cmd .= " 2>/dev/null"; // ignore invalid MIBs
$lines = preg_split('/\n+/', external_exec($cmd));
if (empty($lines)) {
d_echo("No results from snmptranslate\n");
return null;
}
$matches = array();
if (!preg_match('/(.*)::(.*)/', $lines[0], $matches)) {
d_echo("This doesn't look like a MIB: $lines[0]\n");
return null;
}
d_echo("SNMP translated: $module::$oid -> $matches[1]::$matches[2]\n");
return array($matches[1], $matches[2]);
}
/*
* check if the type of the oid is a numeric type, and if so,
* @return the name of RRD type that is best suited to saving it
*/
function oid_rrd_type($oid, $mibdef) {
if (!isset($mibdef[$oid])) {
return false;
}
switch ($mibdef[$oid]['syntax']) {
case 'OCTET':
case 'IpAddress':
return false;
case 'TimeTicks':
// Need to find a way to flag that this should be parsed
//return 'COUNTER';
return false;
case 'Counter64':
return 'COUNTER:600:0:U';
case 'Unsigned32':
return 'GAUGE:600:U:U';
}
return false;
}
/*
* Construct a graph names for use in the database.
* Tag each as in use on this device in &$graphs.
* Update the database with graph definitions as needed.
* We don't include the index in the graph name - that is handled at display time.
*/
function tag_graphs($mibname, $oids, $mibdef, &$graphs)
{
foreach ($oids as $index => $array) {
foreach ($array as $oid => $val) {
$graphname = $mibname."-".$mibdef[$oid]['shortname'];
$graphs[$graphname] = true;
}
}
}
/*
* Ensure a graph_type definition exists in the database for the entities in this MIB
*/
function update_mib_graph_types($mibname, $oids, $mibdef, $graphs)
{
$seengraphs = array();
// Get the list of graphs currently in the database
// FIXME: there's probably a more efficient way to do this
foreach (dbFetch("SELECT DISTINCT `graph_subtype` FROM `graph_types` WHERE `graph_subtype` LIKE ?", array("$mibname-%")) as $graph) {
$seengraphs[$graph['graph_subtype']] = true;
}
foreach ($oids as $index => $array) {
$i = 1;
foreach ($array as $oid => $val) {
$graphname = "$mibname-".$mibdef[$oid]['shortname'];
// add the graph if it's not in the database already
if ($graphs[$graphname] && !$seengraphs[$graphname]) {
// construct a graph definition based on the MIB definition
$graphdef = array();
$graphdef['graph_type'] = "device";
$graphdef['graph_subtype'] = $graphname;
$graphdef['graph_section'] = "mib";
$graphdef['graph_descr'] = $mibdef[$oid]['description'];
$graphdef['graph_order'] = $i++;
// TODO: add colours, unit_text, and ds
// add graph to the database
dbInsert($graphdef, 'graph_types');
}
}
}
}
/*
* Save all of the measurable oids for the device in their own RRDs.
*/
function save_mibs($device, $mibname, $oids, $mibdef, &$graphs)
{
$usedoids = array();
foreach ($oids as $index => $array) {
foreach ($array as $oid => $val) {
$type = oid_rrd_type($oid, $mibdef);
if ($type === false) {
continue;
}
$usedoids[$index][$oid] = $val;
rrd_create_update(
$device,
array($mibname, $mibdef[$oid]['shortname'], $index),
array("DS:".$mibdef[$oid]['dsname'].":$type"),
"N:$val"
);
}
}
tag_graphs($mibname, $usedoids, $mibdef, $graphs);
update_mib_graph_types($mibname, $usedoids, $mibdef, $graphs);
}
/*
* Take a list of MIB name => module pairs.
* Validate MIBs and poll based on the results.
*/
function poll_mibs($list, $device, &$graphs)
{
if (!is_dev_attrib_enabled($device, "poll_mib")) {
d_echo("MIB module disabled for ".$device['hostname']."\n");
return;
}
$mibdefs = array();
echo("MIB-based polling:");
d_echo("\n");
foreach ($list as $name => $module) {
$translated = snmp_translate($name, $module);
if ($translated) {
echo(" $module::$name");
d_echo("\n");
$mod = $translated[0];
$nam = $translated[1];
$mibdefs[$nam] = snmp_mib_load($nam, $mod);
$oids = snmpwalk_cache_oid($device, $nam, array(), $mod, null, "-OQUsb");
d_print_r($oids);
save_mibs($device, $nam, $oids, $mibdefs[$nam], $graphs);
}
else {
d_echo("MIB: no match for $module::$name\n");
}
}
d_echo("Done MIB-based polling");
echo("\n");
}
?>
+4
View File
@@ -0,0 +1,4 @@
ALTER TABLE `graph_types` CHANGE `graph_subtype` `graph_subtype` varchar(64);
ALTER TABLE `device_graphs` CHANGE `graph` `graph` varchar(64);
ALTER TABLE `graph_types` CHANGE `graph_descr` `graph_descr` varchar(255);
ALTER TABLE `graph_types` ADD PRIMARY KEY (`graph_type`, `graph_subtype`, `graph_section`);