From e46ed8941ee8067b36399f46325ac1f641d70eb1 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Thu, 3 Mar 2016 17:47:41 +0100 Subject: [PATCH] + Fix #228 --- snmp_standard/mode/stringvalue.pm | 303 +++++++++++++++++++++++++----- 1 file changed, 256 insertions(+), 47 deletions(-) diff --git a/snmp_standard/mode/stringvalue.pm b/snmp_standard/mode/stringvalue.pm index 52cfbae7a..de2bbb0db 100644 --- a/snmp_standard/mode/stringvalue.pm +++ b/snmp_standard/mode/stringvalue.pm @@ -35,15 +35,44 @@ sub new { $options{options}->add_options(arguments => { "oid:s" => { name => 'oid' }, + "oid-leef:s" => { name => 'oid_leef' }, + "oid-table:s" => { name => 'oid_table' }, + "oid-instance:s" => { name => 'oid_instance' }, + "filter-table:s" => { name => 'filter_table' }, + "warning-regexp:s" => { name => 'warning_regexp' }, "critical-regexp:s" => { name => 'critical_regexp' }, "unknown-regexp:s" => { name => 'unknown_regexp' }, - "format:s" => { name => 'format', default => 'current value is %s' }, - "map-values:s" => { name => 'map_values' }, - "map-values-separator:s" => { name => 'map_values_separator', default => ',' }, - "regexp-map-values" => { name => 'use_regexp_map_values' }, "regexp-isensitive" => { name => 'use_iregexp' }, + + "warning-absent:s@" => { name => 'warning_absent' }, + "critical-absent:s@" => { name => 'critical_absent' }, + "unknown-absent:s@" => { name => 'unknown_absent' }, + "warning-present:s@" => { name => 'warning_present' }, + "critical-present:s@" => { name => 'critical_present' }, + "unknown-present:s@" => { name => 'unknown_present' }, + + "format-ok:s" => { name => 'format_ok', default => '%{filter_rows} value(s)' }, + "format-warning:s" => { name => 'format_warning', default => 'value(s): %{details_warning}' }, + "format-critical:s" => { name => 'format_critical', default => 'value(s): %{details_critical}' }, + "format-unknown:s" => { name => 'format_unknown', default => 'value(s): %{details_unknown}' }, + + "format-details-ok:s" => { name => 'format_details_ok', default => '%{value}' }, + "format-details-warning:s" => { name => 'format_details_warning', default => '%{value}' }, + "format-details-critical:s" => { name => 'format_details_critical', default => '%{value}' }, + "format-details-unknown:s" => { name => 'format_details_unknown', default => '%{value}' }, + + "format-details-separator-ok:s" => { name => 'format_details_separator_ok', default => ', ' }, + "format-details-separator-warning:s" => { name => 'format_details_separator_warning', default => ', ' }, + "format-details-separator-critical:s" => { name => 'format_details_separator_critical', default => ', ' }, + "format-details-separator-unknown:s" => { name => 'format_details_separator_unknown', default => ', ' }, + + "map-values:s" => { name => 'map_values' }, + "map-value-other:s" => { name => 'map_value_other' }, + "map-values-separator:s" => { name => 'map_values_separator', default => ',' }, }); + + $self->{macros} = { ok => {}, warning => {}, critical => {}, unknown => {} }; return $self; } @@ -51,12 +80,16 @@ sub check_options { my ($self, %options) = @_; $self->SUPER::init(%options); - if (!defined($self->{option_results}->{oid}) || $self->{option_results}->{oid} eq '') { - $self->{output}->add_option_msg(short_msg => "Need to specify an OID."); + $self->{option_results}->{oid_leef} = $self->{option_results}->{oid} if (defined($self->{option_results}->{oid}) && $self->{option_results}->{oid} ne ''); + if ((!defined($self->{option_results}->{oid_leef}) || $self->{option_results}->{oid_leef} eq '') && + (!defined($self->{option_results}->{oid_table}) || $self->{option_results}->{oid_table} eq '')) { + $self->{output}->add_option_msg(short_msg => "Need to specify an OID with option --oid-leef or --oid-table."); $self->{output}->option_exit(); } - $self->{option_results}->{oid} = '.' . $self->{option_results}->{oid} if ($self->{option_results}->{oid} !~ /^\./); - + foreach (('oid_leef', 'oid_table', 'oid_instance')) { + $self->{option_results}->{$_} = '.' . $self->{option_results}->{$_} if (defined($self->{option_results}->{$_}) && $self->{option_results}->{$_} ne '' && $self->{option_results}->{$_} !~ /^\./); + } + $self->{map_values} = {}; if (defined($self->{option_results}->{map_values})) { foreach (split /$self->{option_results}->{map_values_separator}/, $self->{option_results}->{map_values}) { @@ -66,47 +99,196 @@ sub check_options { } } -sub check_regexp { +sub get_instance_value { + my ($self, %options) = @_; + + if (!defined($self->{option_results}->{oid_instance}) || $self->{option_results}->{oid_instance} eq '' + || !defined($self->{results}->{$self->{option_results}->{oid_instance} . '.' . $options{instance}})) { + return $options{instance}; + } + + return $self->{results}->{$self->{option_results}->{oid_instance} . '.' . $options{instance}}; +} + +sub get_map_value { + my ($self, %options) = @_; + + my $value = $options{value}; + return '' if (!defined($options{value})); + if (defined($self->{map_values}->{$options{value}})) { + $value = $self->{map_values}->{$options{value}}; + } elsif (defined($self->{option_results}->{map_value_other}) && $self->{option_results}->{map_value_other} ne '') { + $value = $self->{option_results}->{map_value_other}; + } + + return $value; +} + +sub get_snmp_values { + my ($self, %options) = @_; + + $self->{instances} = {}; + if (defined($self->{option_results}->{oid_leef}) && $self->{option_results}->{oid_leef} ne '') { + $self->{results} = $self->{snmp}->get_leef(oids => [$self->{option_results}->{oid_leef}], nothing_quit => 1); + $self->{macros}->{rows} = 1; + $self->{macros}->{filter_rows} = 1; + $self->{instances}->{0} = $self->get_map_value(value => $self->{results}->{$self->{option_results}->{oid_leef}}); + return 0; + } + + my $tables = [ { oid => $self->{option_results}->{oid_table}} ]; + push @$tables, { oid => $self->{option_results}->{oid_instance} } if (defined($self->{option_results}->{oid_instance}) && $self->{option_results}->{oid_instance} ne ''); + $self->{results} = $self->{snmp}->get_multiple_table(oids => $tables, nothing_quit => 1, return_type => 1); + my ($row, $filter_row) = (0, 0); + foreach (keys %{$self->{results}}) { + next if ($_ !~ /^$self->{option_results}->{oid_table}\.(.*)$/); + + $row++; + my $instance = $self->get_instance_value(instance => $1); + my $value = $self->get_map_value(value => $self->{results}->{$_}); + $self->{output}->output_add(long_msg => sprintf("[instance: %s][value: %s]", $_, $value), debug => 1); + if (defined($self->{option_results}->{filter_table}) && $self->{option_results}->{filter_table} ne '' && + $value !~ /$self->{option_results}->{filter_table}/) { + $self->{output}->output_add(long_msg => sprintf("skipping oid '%s' value '%s': not matching the filter", $_, $value), debug => 1); + next; + } + + $self->{instances}->{$instance} = $value; + $filter_row++; + } + $self->{macros}->{rows} = $row; + $self->{macros}->{filter_rows} = $filter_row; + + return 0; +} + +sub checking_regexp { my ($self, %options) = @_; return 0 if (!defined($self->{option_results}->{$options{severity} . '_regexp'})); my $regexp = $self->{option_results}->{$options{severity} . '_regexp'}; if (defined($self->{option_results}->{use_iregexp}) && $options{value} =~ /$regexp/i) { - $self->{exit_code} = $options{severity}; + $self->{instances}->{$options{severity}}->{$options{instance}} = $options{value}; return 1; } elsif (!defined($self->{option_results}->{use_iregexp}) && $options{value} =~ /$regexp/) { - $self->{exit_code} = $options{severity}; + $self->{instances}->{$options{severity}}->{$options{instance}} = $options{value}; return 1; - } + } return 0; } -sub run { +sub store_ok { my ($self, %options) = @_; - # $options{snmp} = snmp object - $self->{snmp} = $options{snmp}; - - my $result = $self->{snmp}->get_leef(oids => [$self->{option_results}->{oid}], nothing_quit => 1); - my $value_check = $result->{$self->{option_results}->{oid}}; - my $value_display = $value_check; - if (defined($self->{option_results}->{map_values})) { - # If we don't find it. We keep the original value - $value_display = defined($self->{map_values}->{$value_check}) ? $self->{map_values}->{$value_check} : $value_check; - if (defined($self->{option_results}->{use_regexp_map_values})) { - $value_check = $value_display; + foreach my $severity (('critical', 'warning', 'unknown')) { + foreach my $type (('absent', 'present')) { + if (defined($self->{option_results}->{$severity . '_' . $type}) && scalar(@{$self->{option_results}->{$severity . '_' . $type}}) > 0) { + return 0; + } } } - - $self->{exit_code} = 'ok'; - $self->check_regexp(severity => 'critical', value => $value_check) || - $self->check_regexp(severity => 'warning', value => $value_check) || - $self->check_regexp(severity => 'unknown', value => $value_check); - $self->{output}->output_add(severity => $self->{exit_code}, - short_msg => sprintf($self->{option_results}->{format}, $value_display)); + $self->{instances}->{ok}->{$options{instance}} = $options{value}; +} + +sub checking_exist { + my ($self, %options) = @_; + + foreach my $severity (('critical', 'warning', 'unknown')) { + foreach my $absent (@{$self->{option_results}->{$severity . '_absent'}}) { + my $match = 0; + foreach (keys %{$self->{instances}}) { + if ($self->{instances}->{$_} eq $absent) { + $match = 1; + last; + } + } + + if ($match == 0) { + $self->{instances}->{$severity}->{$absent} = $absent; + } + } + + foreach my $present (@{$self->{option_results}->{$severity . '_present'}}) { + my $match = 0; + foreach (keys %{$self->{instances}}) { + if ($self->{instances}->{$_} eq $present) { + $self->{instances}->{$severity}->{$_} = $self->{instances}->{$_}; + } + } + } + } +} + +sub change_macros { + my ($self, %options) = @_; + + my $value = $self->{option_results}->{'format_' . $options{severity}}; + while ($value =~ /%\{(.*?)\}/g) { + $value =~ s/%\{($1)\}/\$self->{macros}->{$1}/g; + } + + return $value; +} + +sub build_format_details { + my ($self, %options) = @_; + + foreach my $severity (('ok', 'critical', 'warning', 'unknown')) { + $self->{macros}->{'details_' . $severity} = ''; + my $append = ''; + foreach my $instance (sort keys %{$self->{instances}->{$severity}}) { + my $details = $self->{option_results}->{'format_details_' . $severity}; + $details =~ s/%{rows}/$self->{macros}->{rows}/g; + $details =~ s/%{filter_rows}/$self->{macros}->{filter_rows}/g; + $details =~ s/%{instance}/$instance/g; + $details =~ s/%{value}/$self->{instances}->{$severity}->{$instance}/g; + + $self->{macros}->{'details_' . $severity} .= $append . $details; + $append = $self->{option_results}->{'format_details_separator_' . $severity}; + } + } +} + +sub display_severity { + my ($self, %options) = @_; + + if (!(defined($options{force}) && $options{force} == 1) && scalar(keys %{$self->{instances}->{$options{severity}}}) == 0) { + return 0; + } + + my $display = $self->change_macros(severity => $options{severity}); + eval "\$display = \"$display\""; + $self->{output}->output_add(severity => $options{severity}, + short_msg => $display); +} + +sub display_result { + my ($self, %options) = @_; + + $self->build_format_details(); + $self->display_severity(severity => 'ok', force => 1); + foreach my $severity (('critical', 'warning', 'unknown')) { + $self->display_severity(severity => $severity); + } +} + +sub run { + my ($self, %options) = @_; + $self->{snmp} = $options{snmp}; + + $self->get_snmp_values(); + + foreach (keys %{$self->{instances}}) { + $self->checking_regexp(severity => 'critical', instance => $_, value => $self->{instances}->{$_}) || + $self->checking_regexp(severity => 'warning', instance => $_, value => $self->{instances}->{$_}) || + $self->checking_regexp(severity => 'unknown', instance => $_, value => $self->{instances}->{$_}) || + $self->store_ok(instance => $_, value => $self->{instances}->{$_}); + } + $self->checking_exist(); + $self->display_result(); $self->{output}->display(); $self->{output}->exit(); @@ -118,47 +300,74 @@ __END__ =head1 MODE -Check an SNMP string value (can be a String or an Integer). +Check SNMP string values (can be a String or an Integer). +Check values absent: +centreon_plugins.pl --plugin=snmp_standard::plugin --mode=string-value --hostname=127.0.0.1 --snmp-version=2c --snmp-community=public + --oid-table='.1.3.6.1.2.1.25.4.2.1.2' --format-ok='%{filter_rows} processes' --format-critical='processes are absent: %{details_critical}' --critical-absent='centengine' --critical-absent='crond' --filter-table='centengine|crond' + +Check table status: +centreon_plugins.pl --plugin=snmp_standard::plugin --mode=string-value --hostname=127.0.0.1 --snmp-version=2c --snmp-community=akcp + --oid-table='.1.3.6.1.4.1.3854.1.2.2.1.16.1.4' --oid-instance='.1.3.6.1.4.1.3854.1.2.2.1.16.1.1' --map-values='1=>noStatus,2=>normal,3=>highWarning,4=>highCritical,5=>lowWarning,6=>lowCritical,7=>sensorError' --map-value-other='unknown' --format-ok='All %{rows} entries [%{filter_rows}/%{rows} Temperatures] are ok.' --format-critical='%{details_critical}' --format-details-critical='%{instance} status is %{value}' --critical-regexp='highCritical|lowCritical|sensorError' + +Check like the old plugin: +centreon_plugins.pl --plugin=snmp_standard::plugin --mode=string-value --hostname=127.0.0.1 --snmp-version=2c --snmp-community=public + --oid='.1.3.6.1.2.1.1.1.0' --format-ok='current value is: %{details_ok}' --format-details-warning='current value is: %{details_warning}' --format-details-critical='current value is: %{details_critical}' + =over 8 -=item B<--oid> +=item B<--oid> or <--oid-leef> OID value to check (numeric format only). +=item B<--oid-table> + +OID table value to check (numeric format only). + +=item B<--oid-instance> + +OID table value for the instance (numeric format only). +Can be used to have human readable instance name. + +=item B<--filter-table> + +Filter value from --oid-table option (can be a regexp). + =item B<--warning-regexp> -Return Warning if the oid value match the regexp. +Return Warning if an oid value match the regexp. =item B<--critical-regexp> -Return Critical if the oid value match the regexp. +Return Critical if an oid value match the regexp. -=item B<--unknown-regexp> +=item B<--regexp-isensitive> -Return Unknown if the oid value match the regexp. +Allows to use regexp non case-sensitive. -=item B<--format> +=item B<--format-*> -Output format (Default: 'current value is %s'). +Output format according the threshold. +Can be: +'ok' (default: '%{filter_rows} value(s)'), +'warning' (default: 'value(s): %{details_warning}'), +'critical' (default: 'value(s): %{details_critical}'), +'unknown' (default: 'value(s): %{details_unknown}'). +Can used: %{rows}, %{filter_rows}, %{details_warning}, %{details_ok}, %{details_critical}, %{details_unknown} =item B<--map-values> Use to transform an integer value in most common case. Example: --map-values='1=>ok,10=>fan failed,11=>psu recovery' +=item B<--map-value-other> + +Use to transform an integer value not defined in --map-values option. + =item B<--map-values-separator> Separator uses between values (default: coma). -=item B<--regexp-map-values> - -Use the 'map values' to match in regexp (need --map-values option). - -=item B<--regexp-isensitive> - -Allows to use regexp non case-sensitive. - =back =cut