From b5ae2ff4171a5083e22b14ee3e049cb108aa6c15 Mon Sep 17 00:00:00 2001 From: qgarnier Date: Thu, 6 May 2021 11:02:44 +0200 Subject: [PATCH] enh(protocol/snmp): collection mode - add sampling system (#2772) --- .../apps/protocols/snmp/mode/collection.pm | 102 ++++++++++++++++-- 1 file changed, 94 insertions(+), 8 deletions(-) diff --git a/centreon-plugins/apps/protocols/snmp/mode/collection.pm b/centreon-plugins/apps/protocols/snmp/mode/collection.pm index 1ac022a05..3a1bdbd28 100644 --- a/centreon-plugins/apps/protocols/snmp/mode/collection.pm +++ b/centreon-plugins/apps/protocols/snmp/mode/collection.pm @@ -27,6 +27,7 @@ use warnings; use JSON::XS; use Safe; use centreon::plugins::statefile; +use Digest::MD5 qw(md5_hex); sub custom_select_threshold { my ($self, %options) = @_; @@ -215,6 +216,7 @@ sub collect_snmp_tables { } my $mapping = {}; + my $sampling = {}; foreach (@{$table->{entries}}) { $self->validate_name(name => $_->{name}, section => "[snmp > tables > $table->{name}]"); if (!defined($_->{oid}) || $_->{oid} eq '') { @@ -222,6 +224,7 @@ sub collect_snmp_tables { $self->{output}->option_exit(); } $mapping->{ $_->{name} } = { oid => $_->{oid} }; + $sampling->{ $_->{name} } = 1 if (defined($_->{sampling}) && $_->{sampling} == 1); if (defined($_->{map}) && $_->{map} ne '') { 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}"); @@ -243,6 +246,12 @@ sub collect_snmp_tables { /$used_instance/; 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); + 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} : ''; next if (!defined($_->{oid}) || !defined($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 '') { my $value = $self->get_map_value(value => $snmp_result->{ $_->{oid } }, map => $_->{map}); if (!defined($value)) { @@ -289,7 +302,8 @@ sub use_snmp_cache { return 0 if ($self->is_snmp_cache_enabled() == 0); 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'); 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} }); } +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 { my ($self, %options) = @_; @@ -318,14 +396,21 @@ sub collect_snmp { $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(), sampling => 0 }; - $self->{snmp_collected} = { tables => {}, leefs => {}, epoch => time() }; + $self->collect_snmp_tables(snmp => $options{snmp}); + $self->collect_snmp_leefs(snmp => $options{snmp}); - $self->collect_snmp_tables(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 { @@ -1049,8 +1134,9 @@ sub manage_selection { # add some functions types: # eval_equal (concatenate, math operation) # regexp (regexp substitution, extract a pattern) - # can add: Diff, PerSecond, PerMinute for snmp values (use statefile) - # can cache only some parts of snmp requests + # decode snmp type: ipAddress, DateTime (seconds, strftime) + # can cache only some parts of snmp requests: + # use an array for "snmp" ? $self->read_config(); $self->collect_snmp(snmp => $options{snmp});