(plugin) apps::protocols::snmp - add snmp cache system (#4443)

This commit is contained in:
qgarnier 2023-06-06 10:26:48 +02:00 committed by GitHub
parent 4f8e3a2ea9
commit 1b79704575
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 245 additions and 4 deletions

View File

@ -0,0 +1,115 @@
#
# Copyright 2023 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::protocols::snmp::mode::cache;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use JSON::XS;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'file:s' => { name => 'file' },
'snmpwalk:s@' => { name => 'snmpwalk' },
'snmpget:s@' => { name => 'snmpget' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (!defined($self->{option_results}->{file}) || $self->{option_results}->{file} eq '') {
$self->{output}->add_option_msg(short_msg => "Missing parameter --file");
$self->{output}->option_exit();
}
}
sub run {
my ($self, %options) = @_;
my $snmp_datas = {};
if (defined($self->{option_results}->{snmpwalk})) {
foreach my $oid (@{$self->{option_results}->{snmpwalk}}) {
my $result = $options{snmp}->get_table(oid => $oid);
$snmp_datas = { %$snmp_datas, %$result };
}
}
if (defined($self->{option_results}->{snmpget})) {
my $result = $options{snmp}->get_leef(oids => $self->{option_results}->{snmpget});
$snmp_datas = { %$snmp_datas, %$result };
}
my $json;
eval {
$json = JSON::XS->new->encode($snmp_datas);
};
my $fh;
if (!open($fh, '>', $self->{option_results}->{file})) {
$self->{output}->add_option_msg(short_msg => "Can't open file '$self->{option_results}->{file}': $!");
$self->{output}->option_exit();
}
print $fh $json;
close($fh);
$self->{output}->output_add(
severity => 'OK',
short_msg => 'SNMP cache file created'
);
$self->{output}->display(force_ignore_perfdata => 1);
$self->{output}->exit();
}
1;
__END__
=head1 MODE
Cache SNMP datas in a JSON cache file.
=over 8
=item B<--file>
JSON cache file path.
=item B<--snmpget>
Retrieve a management value.
=item B<--snmpwalk>
Retrieve a subtree of management values.
=back
=cut

View File

@ -30,6 +30,7 @@ sub new {
bless $self, $class; bless $self, $class;
$self->{modes} = { $self->{modes} = {
'cache' => 'apps::protocols::snmp::mode::cache',
'collection' => 'apps::protocols::snmp::mode::collection', 'collection' => 'apps::protocols::snmp::mode::collection',
'discovery' => 'snmp_standard::mode::discovery', 'discovery' => 'snmp_standard::mode::discovery',
'dynamic-command' => 'snmp_standard::mode::dynamiccommand', 'dynamic-command' => 'snmp_standard::mode::dynamiccommand',

View File

@ -22,6 +22,7 @@ package centreon::plugins::snmp;
use strict; use strict;
use warnings; use warnings;
use centreon::plugins::misc;
use SNMP; use SNMP;
use Socket; use Socket;
use POSIX; use POSIX;
@ -54,6 +55,7 @@ sub new {
'maxrepetitions:s' => { name => 'maxrepetitions', default => 50 }, 'maxrepetitions:s' => { name => 'maxrepetitions', default => 50 },
'subsetleef:s' => { name => 'subsetleef', default => 50 }, 'subsetleef:s' => { name => 'subsetleef', default => 50 },
'subsettable:s' => { name => 'subsettable', default => 100 }, 'subsettable:s' => { name => 'subsettable', default => 100 },
'snmp-cache-file:s' => { name => 'snmp_cache_file' },
'snmp-autoreduce:s' => { name => 'snmp_autoreduce' }, 'snmp-autoreduce:s' => { name => 'snmp_autoreduce' },
'snmp-force-getnext' => { name => 'snmp_force_getnext' }, 'snmp-force-getnext' => { name => 'snmp_force_getnext' },
'snmp-username:s' => { name => 'snmp_security_name' }, 'snmp-username:s' => { name => 'snmp_security_name' },
@ -221,6 +223,24 @@ sub autoreduce_leef {
return 0; return 0;
} }
sub get_leef_cache {
my ($self, %options) = @_;
my $results = {};
foreach my $oid (@{$options{oids}}) {
if (defined($self->{snmp_cache}->{$oid})) {
$results->{$oid} = $self->{snmp_cache}->{$oid};
}
}
if ($options{nothing_quit} == 1 && scalar(keys %$results) <= 0) {
$self->{output}->add_option_msg(short_msg => 'SNMP GET Request: Cant get a single value.');
$self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
}
return $results;
}
sub get_leef { sub get_leef {
my ($self, %options) = @_; my ($self, %options) = @_;
# $options{dont_quit} = integer # $options{dont_quit} = integer
@ -247,6 +267,10 @@ sub get_leef {
@{$self->{oids_loaded}} = (); @{$self->{oids_loaded}} = ();
} }
if ($self->{use_snmp_cache} == 1) {
return $self->get_leef_cache(oids => $options{oids}, nothing_quit => $nothing_quit);
}
my $results = {}; my $results = {};
$self->{array_ref_ar} = []; $self->{array_ref_ar} = [];
my $subset_current = 0; my $subset_current = 0;
@ -396,6 +420,43 @@ sub multiple_find_bigger {
return $getting->{pop(@values)}; return $getting->{pop(@values)};
} }
sub get_multiple_table_cache {
my ($self, %options) = @_;
my $results = {};
foreach my $entry (@{$options{oids}}) {
my $result = $self->get_table_cache(
oid => $entry->{oid},
start => $entry->{start},
end => $entry->{end},
nothing_quit => 0
);
if ($options{return_type} == 0) {
$results->{ $entry->{oid} } = $result;
} else {
$results = { %$results, %$result };
}
}
my $total = 0;
if ($options{nothing_quit} == 1) {
if ($options{return_type} == 1) {
$total = scalar(keys %$results);
} else {
foreach (keys %$results) {
$total += scalar(keys %{$results->{$_}});
}
}
if ($total == 0) {
$self->{output}->add_option_msg(short_msg => 'SNMP Table Request: Cant get a single value.');
$self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
}
}
return $results;
}
sub get_multiple_table { sub get_multiple_table {
my ($self, %options) = @_; my ($self, %options) = @_;
# $options{dont_quit} = integer # $options{dont_quit} = integer
@ -408,6 +469,14 @@ sub get_multiple_table {
my ($nothing_quit) = (defined($options{nothing_quit}) && $options{nothing_quit} == 1) ? 1 : 0; my ($nothing_quit) = (defined($options{nothing_quit}) && $options{nothing_quit} == 1) ? 1 : 0;
$self->set_error(); $self->set_error();
if ($self->{use_snmp_cache} == 1) {
return $self->get_multiple_table_cache(
oids => $options{oids},
return_type => $return_type,
nothing_quit => $nothing_quit
);
}
my $working_oids = {}; my $working_oids = {};
my $results = {}; my $results = {};
# Check overlap # Check overlap
@ -560,6 +629,29 @@ sub get_multiple_table {
return $results; return $results;
} }
sub get_table_cache {
my ($self, %options) = @_;
my $branch = defined($options{start}) ? $options{start} : $options{oid};
my $results = {};
foreach my $oid ($self->oid_lex_sort(keys %{$self->{snmp_cache}})) {
if ($oid =~ /^$branch\./) {
$results->{$oid} = $self->{snmp_cache}->{$oid};
if (defined($options{end}) && $self->check_oid_up(current => $oid, end => $options{end})) {
last;
}
}
}
if ($options{nothing_quit} == 1 && scalar(keys %$results) <= 0) {
$self->{output}->add_option_msg(short_msg => 'SNMP Table Request: Cant get a single value.');
$self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
}
return $results;
}
sub get_table { sub get_table {
my ($self, %options) = @_; my ($self, %options) = @_;
# $options{dont_quit} = integer # $options{dont_quit} = integer
@ -578,6 +670,15 @@ sub get_table {
$options{end} = $self->clean_oid($options{end}); $options{end} = $self->clean_oid($options{end});
} }
if ($self->{use_snmp_cache} == 1) {
return $self->get_table_cache(
oid => $options{oid},
start => $options{start},
end => $options{end},
nothing_quit => $nothing_quit
);
}
# we use a medium (UDP have a PDU limit. SNMP protcol cant send multiples for one request) # we use a medium (UDP have a PDU limit. SNMP protcol cant send multiples for one request)
# So we need to manage # So we need to manage
# It's for "bulk". We ask 50 next values. If you set 1, it's like a getnext (snmp v1) # It's for "bulk". We ask 50 next values. If you set 1, it's like a getnext (snmp v1)
@ -597,7 +698,7 @@ sub get_table {
$self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit}); $self->{output}->option_exit(exit_litteral => $self->{snmp_errors_exit});
} }
my $main_indice = $1 . "." . $2; my $main_indice = $1 . '.' . $2;
my $results = {}; my $results = {};
# Quit if base not the same or 'ENDOFMIBVIEW' value # Quit if base not the same or 'ENDOFMIBVIEW' value
@ -751,7 +852,28 @@ sub check_oid_up {
sub check_options { sub check_options {
my ($self, %options) = @_; my ($self, %options) = @_;
# $options{option_results} = ref to options result
$self->{snmp_errors_exit} = $options{option_results}->{snmp_errors_exit};
$self->{use_snmp_cache} = 0;
if (defined($options{option_results}->{snmp_cache_file}) && $options{option_results}->{snmp_cache_file} ne '') {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'JSON::XS',
error_msg => "Cannot load module 'JSON::XS'."
);
my $content = centreon::plugins::misc::slurp_file(output => $self->{output}, file => $options{option_results}->{snmp_cache_file});
eval {
$self->{snmp_cache} = JSON::XS->new->decode($content);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode json cache file: $@");
$self->{output}->option_exit();
}
$self->{use_snmp_cache} = 1;
return ;
}
if (!defined($options{option_results}->{host})) { if (!defined($options{option_results}->{host})) {
$self->{output}->add_option_msg(short_msg => 'Missing parameter --hostname.'); $self->{output}->add_option_msg(short_msg => 'Missing parameter --hostname.');
@ -768,7 +890,6 @@ sub check_options {
$self->{maxrepetitions} = $options{option_results}->{maxrepetitions}; $self->{maxrepetitions} = $options{option_results}->{maxrepetitions};
$self->{subsetleef} = (defined($options{option_results}->{subsetleef}) && $options{option_results}->{subsetleef} =~ /^[0-9]+$/) ? $options{option_results}->{subsetleef} : 50; $self->{subsetleef} = (defined($options{option_results}->{subsetleef}) && $options{option_results}->{subsetleef} =~ /^[0-9]+$/) ? $options{option_results}->{subsetleef} : 50;
$self->{subsettable} = (defined($options{option_results}->{subsettable}) && $options{option_results}->{subsettable} =~ /^[0-9]+$/) ? $options{option_results}->{subsettable} : 100; $self->{subsettable} = (defined($options{option_results}->{subsettable}) && $options{option_results}->{subsettable} =~ /^[0-9]+$/) ? $options{option_results}->{subsettable} : 100;
$self->{snmp_errors_exit} = $options{option_results}->{snmp_errors_exit};
$self->{snmp_autoreduce} = 0; $self->{snmp_autoreduce} = 0;
$self->{snmp_autoreduce_divisor} = 2; $self->{snmp_autoreduce_divisor} = 2;
if (defined($options{option_results}->{snmp_autoreduce})) { if (defined($options{option_results}->{snmp_autoreduce})) {
@ -1023,6 +1144,10 @@ Auto reduce SNMP request size in case of SNMP errors (By default, the divisor is
Use snmp getnext function (even in snmp v2c and v3). Use snmp getnext function (even in snmp v2c and v3).
=item B<--snmp-cache-file>
Use SNMP cache file.
=item B<--snmp-username> =item B<--snmp-username>
Security name (only for SNMP v3). Security name (only for SNMP v3).