/usr/share/perl5/Munin/Node/SNMPConfig.pm is in munin-node 2.0.25-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 | package Munin::Node::SNMPConfig;
# $Id$
use strict;
use warnings;
use Net::SNMP;
use Munin::Node::Configure::HostEnumeration;
use Munin::Node::Configure::Debug;
### SNMP Probing ###############################################################
sub new
{
my ($class, %opts) = @_;
my %sec_args;
my $hosts = $opts{hosts};
die "No host list specified\n" unless scalar @$hosts;
my $version = $opts{version} || '2c';
my $port = $opts{port} || 161;
if ($version eq '3') {
# Privacy
my $privpw = $opts{privpassword};
my $privproto = $opts{privprotocol} || 'des';
if ($privpw) {
$sec_args{-privpassword} = $privpw;
$sec_args{-privprotocol} = $privproto;
DEBUG('Enabled SNMPv3 privacy');
}
# Authentication
my $authpw = $opts{authpassword} || $privpw;
my $authproto = $opts{authprotocol} || 'md5';
if ($authpw) {
$sec_args{-authpassword} = $authpw;
$sec_args{-authprotocol} = $authproto;
DEBUG('Enabled SNMPv3 authentication');
}
# Username
$sec_args{-username} = $opts{username};
}
else {
# version 1 or 2c
$sec_args{-community} = $opts{community} || 'public';
}
my %snmp = (
hosts => $hosts,
port => $port,
version => $version,
sec_args => \%sec_args,
);
return bless \%snmp, $class;
}
sub run_probes
{
my ($self, $plugins) = @_;
foreach my $host (expand_hosts(@{$self->{hosts}})) {
$self->_probe_single_host($host, $plugins);
}
return;
}
# Checks each plugin in turn against the host, and make a note of
# those that are supported.
sub _probe_single_host
{
my ($self, $host, $plugins) = @_;
DEBUG("SNMP-probing $host");
my ($session, $error) = Net::SNMP->session(
-hostname => $host,
-port => $self->{port},
-version => $self->{version},
%{$self->{sec_args}},
# Disable munging of responses into "human readable" form
-translate => 0,
);
unless ($session) {
DEBUG("Dropping host '$host': $error");
return 0;
}
foreach my $plugin ($plugins->list) {
DEBUG("Running autoconf on $plugin->{name} for $host");
if (my @suggestions = _snmp_autoconf_plugin($plugin, $session)) {
$plugin->{default} = 'yes';
$plugin->add_suggestions(@suggestions);
}
else {
DEBUG("Host '$host' doesn't support $plugin->{name}");
# TODO: Check whether there was a timeout? that would indicate a
# bad community string, or no SNMP support (either no daemon, or
# unsupported protocol version) -- any downsides? are there
# devices that timeout rather than return noSuchObject? compromise
# would be to have a --ignore-snmp-timeouts cmdline flag.
#
# TODO, redux: capture and handle SNMPv3 errors.
}
}
DEBUG("Finished probing $host");
$session->close;
return 1;
}
# If the SNMP agent supports the plugin, returns a list of arrayrefs, each of
# which represents a valid plugin instance.
sub _snmp_autoconf_plugin
{
my ($plugin, $session) = @_;
my $hostname = $session->hostname;
my @valid_indexes;
# First round of requirements -- check for specific OIDs.
if ($plugin->{require_oid}) {
DEBUG("Checking required OIDs");
foreach my $req (@{$plugin->{require_oid}}) {
my ($oid, $filter) = @$req;
unless (_snmp_check_require($session, $oid, $filter)) {
DEBUG("Missing requirement.");
return;
}
}
}
# Second round of requirements -- check for valid rows in a table.
if ($plugin->{table}) {
my @columns = map { $_->[0] } @{$plugin->{table}};
DEBUG('Fetching columns: ' . join ', ', @columns);
my $result = $session->get_entries(-columns => \@columns);
unless ($result) {
DEBUG('Failed to get required columns');
return;
}
# sort into rows
my $subtabs = join '|', map { quotemeta } @columns;
my $re = qr/^($subtabs)\.(.*)/;
my %table;
while (my ($oid, $value) = each %$result) {
my ($column, $index) = $oid =~ /$re/;
DEBUG("Row '$index', column '$column', value '$value'");
$table{$index}->{$column} = $value;
}
DEBUG('Checking for valid rows');
# work out what rows are invalid
# can shortcut unless it's a double-wildcard plugin
while (my ($index, $row) = each %table) {
if (_snmp_check_row($index, $row, @{$plugin->{table}})) {
if ($plugin->is_wildcard) {
DEBUG(qq{Adding row '$index' to the list of valid indexes});
push @valid_indexes, $row->{$plugin->{index}};
}
else {
DEBUG('Table contains at least one valid row.');
return [ $hostname ];
}
}
}
# if we got here, there were no matching rows.
unless ($plugin->is_wildcard and @valid_indexes) {
DEBUG('No valid rows found');
return;
}
}
# return list of arrayrefs, one for each good suggestion
return $plugin->is_wildcard ? map { [ $hostname, $_ ] } @valid_indexes
: [ $hostname ];
}
# returns true if the row in a table fulfils all the requirements, false
# otherwise.
sub _snmp_check_row
{
my ($index, $row, @requirements) = @_;
foreach my $req (@requirements) {
my ($oid, $regex) = @$req;
unless (defined $row->{$oid}) {
DEBUG(qq{Row '$index' doesn't have an entry for column '$oid'});
return 0;
}
if ($regex and $row->{$oid} !~ /$regex/) {
DEBUG(qq{Row '$index', column '$oid'. Value '$row->{$oid}' doesn't match '$regex'});
return 0;
}
}
DEBUG(qq{Row '$index' is valid});
return 1;
}
# Returns true if the SNMP agent supports the 'require', false otherwise.
sub _snmp_check_require
{
my ($session, $oid, $filter) = @_;
my $value = _snmp_get_single($session, $oid);
return !(!defined $value or ($filter and $value !~ /$filter/));
}
# Retrieves the value for the given OID from the session
sub _snmp_get_single
{
my ($session, $oid) = @_;
my $response = $session->get_request($oid);
unless (defined $response and $session->error_status == 0) {
DEBUG("Request failed for oid '$oid'");
return;
}
DEBUG("Fetched $oid -> '$response->{$oid}'");
return $response->{$oid};
}
1;
__END__
=head1 NAME
Munin::Node::SNMPConfig - Subroutines providing munin-node-configure's SNMP
scanning capabilities.
=head1 SYNOPSIS
my $snmp = Munin::Node::SNMPConfig->new(
community => 'secret',
version => 1,
);
$snmp->probe_hosts(\%plugins);
=head1 SUBROUTINES
=over
=item B<new(%arguments)>
Constructor. Valid arguments are:
=over 4
=item hosts
The list of hosts to scan, in a format understood by
L<Munin::Node::Configure::HostEnumeration>. Required.
=item port
Port to connect to. Default is 161.
=item version
The SNMP version to use. Default is '2c'.
=item community
The community string to use for SNMP version 1 or 2c. Default is 'public'.
=item username
The SNMPv3 username to use.
=item authpassword
SNMPv3 Authentication password. Optional when encryption is also enabled, in
which case defaults to the privacy password (C<privpassword>). The
password is sent encrypted (one way hash) over the network.
=item authprotocol
SNMPv3 Authentication protocol. One of 'md5' or 'sha' (HMAC-MD5-96, RFC1321
and SHA-1/HMAC-SHA-96, NIST FIPS PIB 180, RFC2264). The default is 'md5'.
=item privpassword
SNMPv3 Privacy password to enable encryption. An empty ('') password is
considered as no password and will not enable encryption.
Privacy requires a v3privprotocol as well as a v3authprotocol and a
v3authpassword, but all of these are defaulted (to 'des', 'md5', and the
v3privpassword value, respectively) and may therefore be left unspecified.
=item privprotocol
If the v3privpassword is set this setting controls what kind of encryption is
used to achieve privacy in the session. Only the very weak 'des' encryption
method is supported officially. The default is 'des'.
The implementing perl module (L<Net::SNMP>) also supports '3des' (CBC-3DES-EDE
aka Triple-DES, NIST FIPS 46-3) as specified in IETF
draft-reeder-snmpv3-usm-3desede. Whether or not this works with any particular
device, we do not know.
=back
=item B<run_probes($plugins)>
Connects to each host in turn, and checks which plugins it supports, based on
the OIDs they reported during snmpconf. If all the requirements are
fulfilled, it will added to the corresponding plugin's suggestions list.
$plugins should be a Munin::Node::Configure::PluginList object.
=back
=cut
# vim: ts=4 : sw=4 : expandtab
|