enh(protocol/snmp): collection mode - add sampling system (#2772)

This commit is contained in:
qgarnier 2021-05-06 11:02:44 +02:00 committed by GitHub
parent a03662289f
commit b5ae2ff417
1 changed files with 94 additions and 8 deletions

View File

@ -27,6 +27,7 @@ use warnings;
use JSON::XS; use JSON::XS;
use Safe; use Safe;
use centreon::plugins::statefile; use centreon::plugins::statefile;
use Digest::MD5 qw(md5_hex);
sub custom_select_threshold { sub custom_select_threshold {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -215,6 +216,7 @@ sub collect_snmp_tables {
} }
my $mapping = {}; my $mapping = {};
my $sampling = {};
foreach (@{$table->{entries}}) { foreach (@{$table->{entries}}) {
$self->validate_name(name => $_->{name}, section => "[snmp > tables > $table->{name}]"); $self->validate_name(name => $_->{name}, section => "[snmp > tables > $table->{name}]");
if (!defined($_->{oid}) || $_->{oid} eq '') { if (!defined($_->{oid}) || $_->{oid} eq '') {
@ -222,6 +224,7 @@ sub collect_snmp_tables {
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
$mapping->{ $_->{name} } = { oid => $_->{oid} }; $mapping->{ $_->{name} } = { oid => $_->{oid} };
$sampling->{ $_->{name} } = 1 if (defined($_->{sampling}) && $_->{sampling} == 1);
if (defined($_->{map}) && $_->{map} ne '') { if (defined($_->{map}) && $_->{map} ne '') {
if (!defined($self->{config}->{mapping}) || !defined($self->{config}->{mapping}->{ $_->{map} })) { if (!defined($self->{config}->{mapping}) || !defined($self->{config}->{mapping}->{ $_->{map} })) {
$self->{output}->add_option_msg(short_msg => "unknown map attribute [snmp > tables > $table->{name} > $_->{name}]: $_->{map}"); $self->{output}->add_option_msg(short_msg => "unknown map attribute [snmp > tables > $table->{name} > $_->{name}]: $_->{map}");
@ -243,6 +246,12 @@ sub collect_snmp_tables {
/$used_instance/; /$used_instance/;
next if (defined($self->{snmp_collected}->{tables}->{ $table->{name} }->{$1})); next if (defined($self->{snmp_collected}->{tables}->{ $table->{name} }->{$1}));
$self->{snmp_collected}->{tables}->{ $table->{name} }->{$1} = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $1); $self->{snmp_collected}->{tables}->{ $table->{name} }->{$1} = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $1);
foreach my $sample_name (keys %$sampling) {
$self->{snmp_collected_sampling}->{tables}->{ $table->{name} } = {}
if (!defined($self->{snmp_collected_sampling}->{tables}->{ $table->{name} }));
$self->{snmp_collected_sampling}->{tables}->{ $table->{name} }->{$1}->{$sample_name} =
$self->{snmp_collected}->{tables}->{ $table->{name} }->{$1}->{$sample_name};
}
} }
} }
} }
@ -260,6 +269,10 @@ sub collect_snmp_leefs {
$self->{snmp_collected}->{leefs}->{ $_->{name} } = defined($_->{default}) ? $_->{default} : ''; $self->{snmp_collected}->{leefs}->{ $_->{name} } = defined($_->{default}) ? $_->{default} : '';
next if (!defined($_->{oid}) || !defined($snmp_result->{ $_->{oid } })); next if (!defined($_->{oid}) || !defined($snmp_result->{ $_->{oid } }));
$self->{snmp_collected}->{leefs}->{ $_->{name} } = $snmp_result->{ $_->{oid } }; $self->{snmp_collected}->{leefs}->{ $_->{name} } = $snmp_result->{ $_->{oid } };
if (defined($_->{sampling}) && $_->{sampling} == 1) {
$self->{snmp_collected_sampling}->{leefs}->{ $_->{name} } = $snmp_result->{ $_->{oid } };
}
if (defined($_->{map}) && $_->{map} ne '') { if (defined($_->{map}) && $_->{map} ne '') {
my $value = $self->get_map_value(value => $snmp_result->{ $_->{oid } }, map => $_->{map}); my $value = $self->get_map_value(value => $snmp_result->{ $_->{oid } }, map => $_->{map});
if (!defined($value)) { if (!defined($value)) {
@ -289,7 +302,8 @@ sub use_snmp_cache {
return 0 if ($self->is_snmp_cache_enabled() == 0); return 0 if ($self->is_snmp_cache_enabled() == 0);
my $has_cache_file = $self->{snmp_cache}->read( my $has_cache_file = $self->{snmp_cache}->read(
statefile => 'cache_snmp_collection_' . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() statefile => 'cache_snmp_collection_' . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' .
md5_hex($self->{option_results}->{config})
); );
$self->{snmp_collected} = $self->{snmp_cache}->get(name => 'snmp_collected'); $self->{snmp_collected} = $self->{snmp_cache}->get(name => 'snmp_collected');
my $reload = defined($self->{config}->{snmp}->{cache}->{reload}) && $self->{config}->{snmp}->{cache}->{reload} =~ /(\d+)/ ? my $reload = defined($self->{config}->{snmp}->{cache}->{reload}) && $self->{config}->{snmp}->{cache}->{reload} =~ /(\d+)/ ?
@ -310,6 +324,70 @@ sub save_snmp_cache {
$self->{snmp_cache}->write(data => { snmp_collected => $self->{snmp_collected} }); $self->{snmp_cache}->write(data => { snmp_collected => $self->{snmp_collected} });
} }
sub collect_snmp_sampling {
my ($self, %options) = @_;
return if ($self->{snmp_collected}->{sampling} == 0);
my $has_cache_file = $self->{snmp_cache}->read(
statefile => 'cache_snmp_collection_sampling_' . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' .
md5_hex($self->{option_results}->{config})
);
my $snmp_collected_sampling_old = $self->{snmp_cache}->get(name => 'snmp_collected_sampling');
# with cache, we need to load the sampling cache maybe. please a statefile-suffix to get uniq files.
# sampling with a global cache can be a nonsense
if (!defined($self->{snmp_collected_sampling})) {
$self->{snmp_collected_sampling} = $snmp_collected_sampling_old;
}
my $delta_time;
if (defined($snmp_collected_sampling_old->{epoch})) {
$delta_time = $self->{snmp_collected_sampling}->{epoch} - $snmp_collected_sampling_old->{epoch};
$delta_time = 1 if ($delta_time <= 0);
}
foreach (keys %{$self->{snmp_collected_sampling}->{leefs}}) {
next if (!defined($snmp_collected_sampling_old->{leefs}->{$_}) || $snmp_collected_sampling_old->{leefs}->{$_} !~ /\d/);
my $old = $snmp_collected_sampling_old->{leefs}->{$_};
my $diff = $self->{snmp_collected_sampling}->{leefs}->{$_} - $old;
my $diff_counter = $diff;
$diff_counter = $self->{snmp_collected_sampling}->{leefs}->{$_} if ($diff_counter < 0);
$self->{snmp_collected}->{leefs}->{ $_ . 'Diff' } = $diff;
$self->{snmp_collected}->{leefs}->{ $_ . 'DiffCounter' } = $diff_counter;
if (defined($delta_time)) {
$self->{snmp_collected}->{leefs}->{ $_ . 'PerSeconds' } = $diff_counter / $delta_time;
$self->{snmp_collected}->{leefs}->{ $_ . 'PerMinutes' } = $diff_counter / $delta_time / 60;
}
}
foreach my $tbl_name (keys %{$self->{snmp_collected_sampling}->{tables}}) {
foreach my $instance (keys %{$self->{snmp_collected_sampling}->{tables}->{$tbl_name}}) {
foreach my $attr (keys %{$self->{snmp_collected_sampling}->{tables}->{$tbl_name}->{$instance}}) {
next if (
!defined($snmp_collected_sampling_old->{tables}->{$tbl_name}) ||
!defined($snmp_collected_sampling_old->{tables}->{$tbl_name}->{$instance}) ||
!defined($snmp_collected_sampling_old->{tables}->{$tbl_name}->{$instance}->{$attr}) ||
$snmp_collected_sampling_old->{tables}->{$tbl_name}->{$instance}->{$attr} !~ /\d/
);
my $old = $snmp_collected_sampling_old->{tables}->{$tbl_name}->{$instance}->{$attr};
my $diff = $self->{snmp_collected_sampling}->{tables}->{$tbl_name}->{$instance}->{$attr} - $old;
my $diff_counter = $diff;
$diff_counter = $self->{snmp_collected_sampling}->{tables}->{$tbl_name}->{$instance}->{$attr} if ($diff_counter < 0);
$self->{snmp_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'Diff' } = $diff;
$self->{snmp_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'DiffCounter' } = $diff_counter;
if (defined($delta_time)) {
$self->{snmp_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'PerSeconds' } = $diff_counter / $delta_time;
$self->{snmp_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'PerMinutes' } = $diff_counter / $delta_time / 60;
}
}
}
}
$self->{snmp_cache}->write(data => { snmp_collected_sampling => $self->{snmp_collected_sampling} });
}
sub collect_snmp { sub collect_snmp {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -318,16 +396,23 @@ sub collect_snmp {
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
return if ($self->use_snmp_cache(snmp => $options{snmp})); if ($self->use_snmp_cache(snmp => $options{snmp}) == 0) {
$self->{snmp_collected_sampling} = { tables => {}, leefs => {}, epoch => time() };
$self->{snmp_collected} = { tables => {}, leefs => {}, epoch => time() }; $self->{snmp_collected} = { tables => {}, leefs => {}, epoch => time(), sampling => 0 };
$self->collect_snmp_tables(snmp => $options{snmp}); $self->collect_snmp_tables(snmp => $options{snmp});
$self->collect_snmp_leefs(snmp => $options{snmp}); $self->collect_snmp_leefs(snmp => $options{snmp});
$self->{snmp_collected}->{sampling} = 1 if (
scalar(keys(%{$self->{snmp_collected_sampling}->{tables}})) > 0 ||
scalar(keys(%{$self->{snmp_collected_sampling}->{leefs}})) > 0
);
$self->save_snmp_cache(); $self->save_snmp_cache();
} }
$self->collect_snmp_sampling(snmp => $options{snmp});
}
sub exist_table_name { sub exist_table_name {
my ($self, %options) = @_; my ($self, %options) = @_;
@ -1049,8 +1134,9 @@ sub manage_selection {
# add some functions types: # add some functions types:
# eval_equal (concatenate, math operation) # eval_equal (concatenate, math operation)
# regexp (regexp substitution, extract a pattern) # regexp (regexp substitution, extract a pattern)
# can add: Diff, PerSecond, PerMinute for snmp values (use statefile) # decode snmp type: ipAddress, DateTime (seconds, strftime)
# can cache only some parts of snmp requests # can cache only some parts of snmp requests:
# use an array for "snmp" ?
$self->read_config(); $self->read_config();
$self->collect_snmp(snmp => $options{snmp}); $self->collect_snmp(snmp => $options{snmp});