diff --git a/centreon/plugins/misc.pm b/centreon/plugins/misc.pm index b535829db..b8a1dc233 100644 --- a/centreon/plugins/misc.pm +++ b/centreon/plugins/misc.pm @@ -389,12 +389,48 @@ sub change_seconds { return $str; } +sub scale_bytesbit { + my (%options) = @_; + + my $base = 1024; + if (defined($options{dst_unit}) && defined($options{src_unit})) { + $options{value} *= 8 if ($options{dst_unit} =~ /b/ && $options{src_unit} =~ /B/); + $options{value} /= 8 if ($options{dst_unit} =~ /B/ && $options{src_unit} =~ /b/); + if ($options{dst_unit} =~ /b/) { + $base = 1000; + } + } + + my %expo = ('' => 0, k => 1, m => 2, g => 3, t => 4, p => 5, e => 6); + my ($src_expo, $dst_expo) = (0, 0); + $src_expo = $expo{lc($options{src_quantity})} if (defined($options{src_quantity}) && $options{src_quantity} =~ /[kmgtpe]/i); + if ($options{dst_unit} eq 'auto') { + my @auto = ('', 'k', 'm', 'g', 't', 'p', 'e'); + my $i = defined($options{src_quantity}) ? $expo{$options{src_quantity}} : 0; + for (; $i < scalar(@auto); $i++) { + last if ($options{value} < $base); + $options{value} = $options{value} / $base; + } + + return ($options{value}, $auto[$i], $options{src_unit}); + } elsif (defined($options{dst_quantity}) && ($options{dst_quantity} eq '' || $options{dst_quantity} =~ /[kmgtpe]/i )) { + my $dst_expo = $expo{lc($options{dst_quantity})}; + if ($dst_expo - $src_expo > 0) { + $options{value} = $options{value} / ($base ** ($dst_expo - $src_expo)); + } elsif ($dst_expo - $src_expo < 0) { + $options{value} = $options{value} * ($base ** (($dst_expo - $src_expo) * -1)); + } + } + + return $options{value}; +} + sub convert_bytes { my (%options) = @_; - my %expo = (k => 1, m => 2, g => 3, t => 4); + + my %expo = (k => 1, m => 2, g => 3, t => 4, p => 5); my $value = $options{value}; - my $base = defined($options{network}) ? 1000 : 1024; - + my $base = defined($options{network}) ? 1000 : 1024; if ($options{unit} =~ /([kmgt])b/i) { $value = $value * ($base ** $expo{lc($1)}); } @@ -402,6 +438,48 @@ sub convert_bytes { return $value; } +sub parse_threshold { + my (%options) = @_; + + my $perf = trim($options{threshold}); + my $perf_result = { arobase => 0, infinite_neg => 0, infinite_pos => 0, start => "", end => "" }; + + my $global_status = 1; + if ($perf =~ /^(\@?)((?:~|(?:\+|-)?\d+(?:[\.,]\d+)?|):)?((?:\+|-)?\d+(?:[\.,]\d+)?)?$/) { + $perf_result->{start} = $2 if (defined($2)); + $perf_result->{end} = $3 if (defined($3)); + $perf_result->{arobase} = 1 if (defined($1) && $1 eq '@'); + $perf_result->{start} =~ s/[\+:]//g; + $perf_result->{end} =~ s/\+//; + if ($perf_result->{end} eq '') { + $perf_result->{end} = 1e500; + $perf_result->{infinite_pos} = 1; + } + $perf_result->{start} = 0 if ($perf_result->{start} eq ''); + $perf_result->{start} =~ s/,/\./; + $perf_result->{end} =~ s/,/\./; + + if ($perf_result->{start} eq '~') { + $perf_result->{start} = -1e500; + $perf_result->{infinite_neg} = 1; + } + } else { + $global_status = 0; + } + + return ($global_status, $perf_result); +} + +sub get_threshold_litteral { + my (%options) = @_; + + my $perf_output = ($options{arobase} == 1 ? "@" : "") . + (($options{infinite_neg} == 0) ? $options{start} : "~") . + ":" . + (($options{infinite_pos} == 0) ? $options{end} : ""); + return $perf_output; +} + 1; __END__ diff --git a/centreon/plugins/output.pm b/centreon/plugins/output.pm index e7fe92608..3fdcbc296 100644 --- a/centreon/plugins/output.pm +++ b/centreon/plugins/output.pm @@ -39,6 +39,7 @@ sub new { "range-perfdata:s" => { name => 'range_perfdata' }, "filter-perfdata:s" => { name => 'filter_perfdata' }, "change-perfdata:s@" => { name => 'change_perfdata' }, + "extend-perfdata:s@" => { name => 'extend_perfdata' }, "filter-uom:s" => { name => 'filter_uom' }, "verbose" => { name => 'verbose' }, "debug" => { name => 'debug' }, @@ -123,15 +124,7 @@ sub check_options { } } - if (defined($self->{option_results}->{change_perfdata})) { - foreach (@{$self->{option_results}->{change_perfdata}}) { - if (! /^(.+?),(.+)$/) { - $self->add_option_msg(short_msg => "Wrong change-perfdata option '" . $_ . "' (syntax: match,substitute)"); - $self->option_exit(); - } - $self->{change_perfdata}->{$1} = $2; - } - } + $self->load_perfdata_extend_args(); } sub add_option_msg { @@ -194,36 +187,6 @@ sub perfdata_add { push @{$self->{perfdatas}}, $perfdata; } -sub change_perfdatas { - my ($self, %options) = @_; - - if ($self->{option_results}->{change_perfdata}) { - foreach (@{$self->{perfdatas}}) { - foreach my $filter (keys %{$self->{change_perfdata}}) { - if ($_->{label} =~ /$filter/) { - eval "\$_->{label} =~ s{$filter}{$self->{change_perfdata}->{$filter}}"; - last; - } - } - } - } - - return if ($self->{explode_perfdata_total} == 0); - foreach (@{$self->{perfdatas}}) { - next if ($_->{max} eq ''); - if ($self->{explode_perfdata_total} == 2) { - $self->perfdata_add(label => $_->{label} . '_max', value => $_->{max}); - next; - } - foreach my $regexp (keys %{$self->{explode_perfdatas}}) { - if ($_->{label} =~ /$regexp/) { - $self->perfdata_add(label => $self->{explode_perfdatas}->{$regexp}, value => $_->{max}); - last; - } - } - } -} - sub range_perfdata { my ($self, %options) = @_; @@ -275,7 +238,7 @@ sub output_json { } if ($options{force_ignore_perfdata} == 0) { - $self->change_perfdatas(); + $self->change_perfdata(); foreach my $perf (@{$self->{perfdatas}}) { next if (defined($self->{option_results}->{filter_perfdata}) && $perf->{label} !~ /$self->{option_results}->{filter_perfdata}/); @@ -365,7 +328,7 @@ sub output_xml { } if ($options{force_ignore_perfdata} == 0) { - $self->change_perfdatas(); + $self->change_perfdata(); foreach my $perf (@{$self->{perfdatas}}) { next if (defined($self->{option_results}->{filter_perfdata}) && $perf->{label} !~ /$self->{option_results}->{filter_perfdata}/); @@ -413,7 +376,7 @@ sub output_txt { print "\n"; } else { print "|"; - $self->change_perfdatas(); + $self->change_perfdata(); foreach my $perf (@{$self->{perfdatas}}) { next if (defined($self->{option_results}->{filter_perfdata}) && $perf->{label} !~ /$self->{option_results}->{filter_perfdata}/); @@ -746,6 +709,274 @@ sub is_disco_show { return 0; } +sub parse_pfdata_scale { + my ($self, %options) = @_; + + # --extend-perfdata=traffic_in,,scale(Mbps),mbps + my $args = { unit => 'auto' }; + if ($options{args} =~ /^([KMGTPEkmgtpe])?(B|b|bps|Bps|b\/s|auto)$/) { + $args->{quantity} = defined($1) ? $1 : ''; + $args->{unit} = $2; + } elsif ($options{args} ne '') { + return 1; + } + + return (0, $args); +} + +sub parse_pfdata_math { + my ($self, %options) = @_; + + # --extend-perfdata=perfx,,math(current + 10 - 100, 1) + my $args = { math => undef, apply_threshold => 0 }; + my ($math, $apply_threshold) = split /\|/, $options{args}; + if ($math =~ /^((?:[\s\.\-\+\*\/0-9\(\)]|current)+)$/) { + $args->{math} = $1; + } elsif ($options{args} ne '') { + return 1; + } + + if (defined($apply_threshold) && $apply_threshold =~ /^\s*(0|1)\s*$/ ) { + $args->{apply_threshold} = $1; + } + + return (0, $args); +} + +sub apply_pfdata_scale { + my ($self, %options) = @_; + + return if (${$options{perf}}->{unit} !~ /^([KMGTPEkmgtpe])?(B|b|bps|Bps|b\/s)$/); + + my ($src_quantity, $src_unit) = ($1, $2); + my ($value, $dst_quantity, $dst_unit) = centreon::plugins::misc::scale_bytesbit(value => ${$options{perf}}->{value}, + src_quantity => $src_quantity, src_unit => $src_unit, dst_quantity => $options{args}->{quantity}, dst_unit => $options{args}->{unit}); + ${$options{perf}}->{value} = sprintf("%.2f", $value); + if (defined($dst_unit)) { + ${$options{perf}}->{unit} = $dst_quantity . $dst_unit; + } else { + ${$options{perf}}->{unit} = $options{args}->{quantity} . $options{args}->{unit}; + } + + if (defined(${$options{perf}}->{max}) && ${$options{perf}}->{max} ne '') { + ($value) = centreon::plugins::misc::scale_bytesbit(value => ${$options{perf}}->{max}, + src_quantity => $src_quantity, src_unit => $src_unit, + dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity}, + dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit}); + ${$options{perf}}->{max} = sprintf("%.2f", $value); + } + + foreach my $threshold ('warning', 'critical') { + next if (${$options{perf}}->{$threshold} eq ''); + my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold}); + next if ($status == 0); + + if ($result->{start} ne '' && $result->{infinite_neg} == 0) { + ($result->{start}) = centreon::plugins::misc::scale_bytesbit(value => $result->{start}, + src_quantity => $src_quantity, src_unit => $src_unit, + dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity}, + dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit}); + } + if ($result->{end} ne '' && $result->{infinite_pos} == 0) { + ($result->{end}) = centreon::plugins::misc::scale_bytesbit(value => $result->{end}, + src_quantity => $src_quantity, src_unit => $src_unit, + dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity}, + dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit}); + } + + ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result); + } +} + +sub apply_pfdata_invert { + my ($self, %options) = @_; + + return if (!defined(${$options{perf}}->{max}) || ${$options{perf}}->{max} eq ''); + + ${$options{perf}}->{value} = ${$options{perf}}->{max} - ${$options{perf}}->{value}; + foreach my $threshold ('warning', 'critical') { + next if (${$options{perf}}->{$threshold} eq ''); + my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold}); + next if ($status == 0); + + my $tmp = { arobase => $result->{arobase}, infinite_pos => 0, infinite_neg => 0, start => $result->{start}, end => $result->{end} }; + $tmp->{infinite_neg} = 1 if ($result->{infinite_pos} == 1); + $tmp->{infinite_pos} = 1 if ($result->{infinite_neg} == 1); + + if ($result->{start} ne '' && $result->{infinite_neg} == 0) { + $tmp->{end} = ${$options{perf}}->{max} - $result->{start}; + } + if ($result->{end} ne '' && $result->{infinite_pos} == 0) { + $tmp->{start} = ${$options{perf}}->{max} - $result->{end}; + } + + ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$tmp); + } +} + +sub apply_pfdata_percent { + my ($self, %options) = @_; + + return if (!defined(${$options{perf}}->{max}) || ${$options{perf}}->{max} eq ''); + + ${$options{perf}}->{value} = sprintf("%.2f", ${$options{perf}}->{value} * 100 / ${$options{perf}}->{max}); + ${$options{perf}}->{unit} = '%'; + foreach my $threshold ('warning', 'critical') { + next if (${$options{perf}}->{$threshold} eq ''); + my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold}); + next if ($status == 0); + + if ($result->{start} ne '' && $result->{infinite_neg} == 0) { + $result->{start} = sprintf("%.2f", $result->{start} * 100 / ${$options{perf}}->{max}); + } + if ($result->{end} ne '' && $result->{infinite_pos} == 0) { + $result->{end} = sprintf("%.2f", $result->{end} * 100 / ${$options{perf}}->{max}); + } + + ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result); + } + + ${$options{perf}}->{max} = 100; +} + +sub apply_pfdata_math { + my ($self, %options) = @_; + + my $math = $options{args}->{math}; + $math =~ s/current/\$value/g; + + my $value = ${$options{perf}}->{value}; + eval "\${\$options{perf}}->{value} = $math"; + + return if ($options{args}->{apply_threshold} == 0); + + foreach my $threshold ('warning', 'critical') { + next if (${$options{perf}}->{$threshold} eq ''); + my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold}); + next if ($status == 0); + + if ($result->{start} ne '' && $result->{infinite_neg} == 0) { + $value = $result->{start}; + eval "\$result->{start} = $math"; + } + if ($result->{end} ne '' && $result->{infinite_pos} == 0) { + $value = $result->{end}; + eval "\$result->{end} = $math"; + } + + ${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result); + } + + ${$options{perf}}->{max} = 100; +} + +sub load_perfdata_extend_args { + my ($self, %options) = @_; + + foreach ([$self->{option_results}->{change_perfdata}, 1], [$self->{option_results}->{extend_perfdata}, 2]) { + next if (!defined($_->[0])); + foreach my $arg (@{$_->[0]}) { + $self->parse_perfdata_extend_args(arg => $arg, type => $_->[1]); + } + } +} + +sub parse_perfdata_extend_args { + my ($self, %options) = @_; + + # --extend-perfdata=searchlabel,newlabel,method[,newuom] + my ($pfdata_match, $pfdata_substitute, $method, $uom_substitute) = split /,/, $options{arg}; + return if (!defined($pfdata_match) || $pfdata_match eq ''); + + $self->{pfdata_extends} = [] if (!defined($self->{pfdata_extends})); + my $pfdata_extends = { + pfdata_match => defined($pfdata_match) && $pfdata_match ne '' ? $pfdata_match : undef, + pfdata_substitute => defined($pfdata_substitute) && $pfdata_substitute ne '' ? $pfdata_substitute : undef, + uom_substitute => defined($uom_substitute) && $uom_substitute ne '' ? $uom_substitute : undef, + type => $options{type} + }; + + if (defined($method) && $method ne '') { + if ($method !~ /^\s*(invert|percent|scale|math)\s*\(\s*(.*?)\s*\)\s*$/) { + $self->output_add(long_msg => "method in argument '$options{arg}' is unknown", debug => 1); + return ; + } + + $pfdata_extends->{method_name} = $1; + my $args = $2; + if (my $func = $self->can('parse_pfdata_' . $pfdata_extends->{method_name})) { + (my $status, $pfdata_extends->{method_args}) = $func->($self, args => $args); + if ($status == 1) { + $self->output_add(long_msg => "argument in method '$options{arg}' is unknown", debug => 1); + return ; + } + } + } + + push @{$self->{pfdata_extends}}, $pfdata_extends; +} + +sub apply_perfdata_explode { + my ($self, %options) = @_; + + return if ($self->{explode_perfdata_total} == 0); + foreach (@{$self->{perfdatas}}) { + next if ($_->{max} eq ''); + if ($self->{explode_perfdata_total} == 2) { + $self->perfdata_add(label => $_->{label} . '_max', value => $_->{max}); + next; + } + foreach my $regexp (keys %{$self->{explode_perfdatas}}) { + if ($_->{label} =~ /$regexp/) { + $self->perfdata_add(label => $self->{explode_perfdatas}->{$regexp}, value => $_->{max}); + last; + } + } + } +} + +sub apply_perfdata_extend { + my ($self, %options) = @_; + + foreach my $extend (@{$self->{pfdata_extends}}) { + my $new_pfdata = []; + + for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) { + next if ($self->{perfdatas}->[$i]->{label} !~ /$extend->{pfdata_match}/); + + my $new_perf = { %{$self->{perfdatas}->[$i]} }; + + if (defined($extend->{pfdata_substitute})) { + eval "\$new_perf->{label} =~ s{$extend->{pfdata_match}}{$extend->{pfdata_substitute}}"; + } + + if (defined($extend->{method_name})) { + my $func = $self->can('apply_pfdata_' . $extend->{method_name}); + $func->($self, perf => \$new_perf, args => $extend->{method_args}); + } + + if (defined($extend->{uom_substitute})) { + $new_perf->{unit} = $extend->{uom_substitute}; + } + + if ($extend->{type} == 1) { + $self->{perfdatas}->[$i] = $new_perf; + } else { + push @$new_pfdata, $new_perf; + } + } + + push @{$self->{perfdatas}}, @$new_pfdata; + } +} + +sub change_perfdata { + my ($self, %options) = @_; + + $self->apply_perfdata_extend(); + $self->apply_perfdata_explode(); +} + 1; __END__ @@ -779,10 +1010,26 @@ Filter perfdata that match the regexp. Put max perfdata (if it exist) in a specific perfdata (without values: same with '_max' suffix) (Multiple options) -=item B<--change-perfdata> +=item B<--change-perfdata> B<--extend-perfdata> -Change perfdata name (Multiple option) -Syntax: regexp_matching,regexp_substitute +Change or extend perfdata. +Syntax: --extend-perfdata=searchlabel,newlabel,target[,newuom] + +Common examples: + +=over 4 + +Change storage free perfdata in used: --change-perfdata=free,used,invert() + +Change storage free perfdata in used: --change-perfdata=used,free,invert() + +Scale traffic values automaticaly: --change-perfdata=traffic,,scale(auto) + +Scale traffic values in Mbps: --change-perfdata=traffic_in,,scale(Mbps),mbps + +Change traffic values in percent: --change-perfdata=traffic_in,,percent() + +=back =item B<--range-perfdata> diff --git a/centreon/plugins/perfdata.pm b/centreon/plugins/perfdata.pm index 8c415ca0e..276d5ed4d 100644 --- a/centreon/plugins/perfdata.pm +++ b/centreon/plugins/perfdata.pm @@ -22,6 +22,7 @@ package centreon::plugins::perfdata; use strict; use warnings; +use centreon::plugins::misc; sub new { my ($class, %options) = @_; @@ -77,13 +78,14 @@ sub threshold_validate { # $options{value} : threshold value my $status = 1; - $self->{threshold_label}->{$options{label}} = {value => $options{value}, start => undef, end => undef, arobase => undef, infinite_neg => undef, infinite_pos => undef}; + $self->{threshold_label}->{$options{label}} = { value => $options{value}, start => undef, end => undef, arobase => undef, infinite_neg => undef, infinite_pos => undef }; if (!defined($options{value}) || $options{value} eq '') { return $status; } - ($status, $self->{threshold_label}->{$options{label}}->{start}, $self->{threshold_label}->{$options{label}}->{end}, $self->{threshold_label}->{$options{label}}->{arobase}, $self->{threshold_label}->{$options{label}}->{infinite_neg}, $self->{threshold_label}->{$options{label}}->{infinite_pos}) = $self->parse_threshold($options{value}); - + ($status, my $result_perf) = + centreon::plugins::misc::parse_threshold(threshold => $options{value}); + $self->{threshold_label}->{$options{label}} = { %{$self->{threshold_label}->{$options{label}}}, %$result_perf }; return $status; } @@ -113,43 +115,6 @@ sub trim { return $value; } -sub parse_threshold { - my ($self, $perf) = @_; - - $perf = $self->trim($perf); - - my $arobase = 0; - my $infinite_neg = 0; - my $infinite_pos = 0; - my $value_start = ""; - my $value_end = ""; - my $global_status = 1; - - if ($perf =~ /^(\@?)((?:~|(?:\+|-)?\d+(?:[\.,]\d+)?|):)?((?:\+|-)?\d+(?:[\.,]\d+)?)?$/) { - $value_start = $2 if (defined($2)); - $value_end = $3 if (defined($3)); - $arobase = 1 if (defined($1) && $1 eq '@'); - $value_start =~ s/[\+:]//g; - $value_end =~ s/\+//; - if ($value_end eq '') { - $value_end = 1e500; - $infinite_pos = 1; - } - $value_start = 0 if ($value_start eq ''); - $value_start =~ s/,/\./; - $value_end =~ s/,/\./; - - if ($value_start eq '~') { - $value_start = -1e500; - $infinite_neg = 1; - } - } else { - $global_status = 0; - } - - return ($global_status, $value_start, $value_end, $arobase, $infinite_neg, $infinite_pos); -} - sub change_bytes { my ($self, %options) = @_; my $divide = defined($options{network}) ? 1000 : 1024;