From 7ea8cbf336fb2bc6b795fc6599e4299a7c4152fc Mon Sep 17 00:00:00 2001 From: Colin Gagnaire Date: Wed, 28 Nov 2018 17:37:34 +0100 Subject: [PATCH] add pvx plugin (#1246) --- apps/pvx/restapi/custom/api.pm | 284 ++++++++++++++++++ apps/pvx/restapi/mode/httphits.pm | 190 ++++++++++++ apps/pvx/restapi/mode/networkconnection.pm | 200 ++++++++++++ apps/pvx/restapi/mode/networktraffic.pm | 238 +++++++++++++++ .../pvx/restapi/mode/networkuserexperience.pm | 162 ++++++++++ apps/pvx/restapi/plugin.pm | 59 ++++ 6 files changed, 1133 insertions(+) create mode 100644 apps/pvx/restapi/custom/api.pm create mode 100644 apps/pvx/restapi/mode/httphits.pm create mode 100644 apps/pvx/restapi/mode/networkconnection.pm create mode 100644 apps/pvx/restapi/mode/networktraffic.pm create mode 100644 apps/pvx/restapi/mode/networkuserexperience.pm create mode 100644 apps/pvx/restapi/plugin.pm diff --git a/apps/pvx/restapi/custom/api.pm b/apps/pvx/restapi/custom/api.pm new file mode 100644 index 000000000..2e53fb28d --- /dev/null +++ b/apps/pvx/restapi/custom/api.pm @@ -0,0 +1,284 @@ +# +# Copyright 2018 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::pvx::restapi::custom::api; + +use strict; +use warnings; +use centreon::plugins::http; +use DateTime; +use JSON::XS; +use URI::Encode; + +sub new { + my ($class, %options) = @_; + my $self = {}; + bless $self, $class; + + if (!defined($options{output})) { + print "Class Custom: Need to specify 'output' argument.\n"; + exit 3; + } + if (!defined($options{options})) { + $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); + $options{output}->option_exit(); + } + + if (!defined($options{noptions})) { + $options{options}->add_options(arguments => + { + "api-key:s" => { name => 'api_key' }, + "hostname:s" => { name => 'hostname' }, + "url-path:s" => { name => 'url_path' }, + "port:s" => { name => 'port' }, + "proto:s" => { name => 'proto' }, + "credentials" => { name => 'credentials' }, + "basic" => { name => 'basic' }, + "username:s" => { name => 'username' }, + "password:s" => { name => 'password' }, + "proxyurl:s" => { name => 'proxyurl' }, + "timeout:s" => { name => 'timeout' }, + "ssl-opt:s@" => { name => 'ssl_opt' }, + "timeframe:s" => { name => 'timeframe' }, + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'RESTAPI OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{mode} = $options{mode}; + $self->{http} = centreon::plugins::http->new(output => $self->{output}); + + return $self; + +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults { + my ($self, %options) = @_; + + foreach (keys %{$options{default}}) { + if ($_ eq $self->{mode}) { + for (my $i = 0; $i < scalar(@{$options{default}->{$_}}); $i++) { + foreach my $opt (keys %{$options{default}->{$_}[$i]}) { + if (!defined($self->{option_results}->{$opt}[$i])) { + $self->{option_results}->{$opt}[$i] = $options{default}->{$_}[$i]->{$opt}; + } + } + } + } + } +} + +sub check_options { + my ($self, %options) = @_; + + $self->{api_key} = (defined($self->{option_results}->{api_key})) ? $self->{option_results}->{api_key} : undef; + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : undef; + $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443; + $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; + $self->{url_path} = (defined($self->{option_results}->{url_path})) ? $self->{option_results}->{url_path} : '/api'; + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10; + $self->{proxyurl} = (defined($self->{option_results}->{proxyurl})) ? $self->{option_results}->{proxyurl} : undef; + $self->{ssl_opt} = (defined($self->{option_results}->{ssl_opt})) ? $self->{option_results}->{ssl_opt} : undef; + $self->{username} = (defined($self->{option_results}->{username})) ? $self->{option_results}->{username} : undef; + $self->{password} = (defined($self->{option_results}->{password})) ? $self->{option_results}->{password} : undef; + $self->{credentials} = (defined($self->{option_results}->{credentials})) ? 1 : undef; + $self->{basic} = (defined($self->{option_results}->{basic})) ? 1 : undef; + $self->{timeframe} = (defined($self->{option_results}->{timeframe})) ? $self->{option_results}->{timeframe} : undef; + + if (!defined($self->{hostname}) || $self->{hostname} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify hostname option."); + $self->{output}->option_exit(); + } + if (!defined($self->{api_key}) || $self->{api_key} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify api-key option."); + $self->{output}->option_exit(); + } + + return 0; +} + +sub build_options_for_httplib { + my ($self, %options) = @_; + + $self->{option_results}->{hostname} = $self->{hostname}; + $self->{option_results}->{timeout} = $self->{timeout}; + $self->{option_results}->{port} = $self->{port}; + $self->{option_results}->{proto} = $self->{proto}; + $self->{option_results}->{proxyurl} = $self->{proxyurl}; + $self->{option_results}->{credentials} = $self->{credentials}; + $self->{option_results}->{basic} = $self->{basic}; + $self->{option_results}->{username} = $self->{username}; + $self->{option_results}->{password} = $self->{password}; + $self->{option_results}->{warning_status} = ''; + $self->{option_results}->{critical_status} = ''; +} + +sub settings { + my ($self, %options) = @_; + + $self->build_options_for_httplib(); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->add_header(key => 'Content-Type', value => 'application/x-www-form-urlencoded'); + $self->{http}->add_header(key => 'PVX-Authorization', value => $self->{api_key}); + $self->{http}->set_options(%{$self->{option_results}}); +} + +sub get_connection_info { + my ($self, %options) = @_; + + return $self->{hostname} . ":" . $self->{port}; +} + +sub get_hostname { + my ($self, %options) = @_; + + return $self->{hostname}; +} + +sub get_port { + my ($self, %options) = @_; + + return $self->{port}; +} + +sub query_range { + my ($self, %options) = @_; + + my $data; + my $start_time = DateTime->now->subtract(seconds => $options{timeframe})->epoch; + my $end_time = DateTime->now->epoch; + my $uri = URI::Encode->new({encode_reserved => 1}); + + my $query = sprintf("%s SINCE %s UNTIL %s", $options{query}, $start_time, $end_time); + $query .= sprintf(" BY %s", $options{instance}) if (defined($options{instance}) && $options{instance} ne ''); + $query .= sprintf(" WHERE %s", $options{filter}) if (defined($options{filter}) && $options{filter} ne ''); + $query .= sprintf(" FROM %s", $options{from}) if (defined($options{from}) && $options{from} ne ''); + $query .= sprintf(" TOP %s", $options{top}) if (defined($options{top}) && $options{top} ne ''); + + $self->{output}->output_add(long_msg => sprintf("Query: '/query?expr=%s'", $query), debug => 1); + my $result = $self->get_endpoint(url_path => '/query?expr=' . $uri->encode($query)); + + return $result->{data}; +} + +sub get_endpoint { + my ($self, %options) = @_; + + $self->settings; + my $response = $self->{http}->request(url_path => $self->{url_path} . $options{url_path}); + + my $content; + eval { + $content = JSON::XS->new->utf8->decode($response); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); + $self->{output}->option_exit(); + } + + if ($content->{type} eq 'error') { + $self->{output}->add_option_msg(short_msg => "Cannot get data: " . $content->{error}); + $self->{output}->option_exit(); + } + + return $content->{result}; +} + +1; + +__END__ + +=head1 NAME + +PVX REST API + +=head1 SYNOPSIS + +PVX Rest API custom mode + +=head1 REST API OPTIONS + +=over 8 + +=item B<--timeframe> + +Set timeframe in seconds (i.e. 3600 to check last hour). + +=item B<--api-key> + +PVX API key. + +=item B<--hostname> + +PVX hostname. + +=item B<--url-path> + +PVX url path (Default: '/api') + +=item B<--port> + +API port (Default: 443) + +=item B<--proto> + +Specify https if needed (Default: 'https') + +=item B<--username> + +Specify username for authentication + +=item B<--password> + +Specify password for authentication + +=item B<--basic> + +Specify this option if you access the API over basic authentication and don't want a '401 UNAUTHORIZED' error to be logged on your webserver. + +Specify this option if you access the API over hidden basic authentication or you'll get a '404 NOT FOUND' error. + +(Use with --credentials) + +=item B<--proxyurl> + +Proxy URL if any + +=item B<--timeout> + +Set HTTP timeout + +=item B<--ssl-opt> + +Set SSL Options (--ssl-opt="SSL_version => TLSv1" --ssl-opt="SSL_verify_mode => SSL_VERIFY_NONE"). + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/apps/pvx/restapi/mode/httphits.pm b/apps/pvx/restapi/mode/httphits.pm new file mode 100644 index 000000000..ea4a3404b --- /dev/null +++ b/apps/pvx/restapi/mode/httphits.pm @@ -0,0 +1,190 @@ +# +# Copyright 2018 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::pvx::restapi::mode::httphits; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'instances', type => 1, cb_prefix_output => 'prefix_instances_output', message_multiple => 'All metrics are ok' }, + ]; + + $self->{maps_counters}->{instances} = [ + { label => 'ratio', set => { + key_values => [ { name => 'ratio' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Ratio: %.2f', + perfdatas => [ + { label => 'ratio', value => 'ratio_absolute', template => '%.2f', + min => 0, label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + { label => 'hits-error', set => { + key_values => [ { name => 'error_hits' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Hits Error: %.3f hits/s', + perfdatas => [ + { label => 'hits_error', value => 'error_hits_absolute', template => '%.3f', + min => 0, unit => 'hits/s', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + { label => 'hits', set => { + key_values => [ { name => 'hits' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Hits: %.3f hits/s', + perfdatas => [ + { label => 'hits', value => 'hits_absolute', template => '%.3f', + min => 0, unit => 'hits/s', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + ]; +} + +sub prefix_instances_output { + my ($self, %options) = @_; + + return $options{instance_value}->{instance_label} . " '" . $options{instance_value}->{key} . "' "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "instance:s" => { name => 'instance', default => 'layer' }, + "top:s" => { name => 'top' }, + "filter:s" => { name => 'filter' }, + "from:s" => { name => 'from', default => 'http' }, + "filter-counters:s" => { name => 'filter_counters' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{instance}) || $self->{option_results}->{instance} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --instance option as a PVQL object."); + $self->{output}->option_exit(); + } + + $self->{pvql_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $instance_label = $self->{option_results}->{instance}; + $instance_label =~ s/\./ /g; + $instance_label =~ s/(\w+)/\u\L$1/g; + + $self->{instances} = {}; + + my $apps; + if ($self->{option_results}->{instance} =~ /application/) { + my $results = $options{custom}->get_endpoint(url_path => '/get-configuration'); + $apps = $results->{applications}; + } + + my $results = $options{custom}->query_range( + query => 'error.hits, hits', + instance => $self->{option_results}->{instance}, + top => $self->{option_results}->{top}, + filter => $self->{option_results}->{filter}, + from => $self->{option_results}->{from}, + timeframe => $self->{pvql_timeframe}, + ); + + foreach my $result (@{$results}) { + next if (!defined(${$result->{key}}[0])); + my $instance; + $instance = ${$result->{key}}[0]->{value} if (defined(${$result->{key}}[0]->{value})); + $instance = ${$result->{key}}[0]->{status} if (defined(${$result->{key}}[0]->{status})); + $self->{instances}->{$instance}->{key} = $instance; + $self->{instances}->{$instance}->{key} = $apps->{$instance}->{name} if ($self->{option_results}->{instance} =~ /application/); + $self->{instances}->{$instance}->{error_hits} = (defined(${$result->{values}}[0]->{value})) ? ${$result->{values}}[0]->{value} / $self->{pvql_timeframe} : 0; + $self->{instances}->{$instance}->{hits} = ${$result->{values}}[1]->{value} / $self->{pvql_timeframe}; + $self->{instances}->{$instance}->{ratio} = (defined(${$result->{values}}[0]->{value})) ? ${$result->{values}}[1]->{value} / (${$result->{values}}[0]->{value} + ${$result->{values}}[1]->{value}) : 1; + $self->{instances}->{$instance}->{instance_label} = $instance_label; + } + + if (scalar(keys %{$self->{instances}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No instances or results found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check http hits. + +=over 8 + +=item B<--instance> + +Filter on a specific instance (Must be a PVQL object, Default: 'host') + +(Object 'application' will be mapped with applications name) + +=item B<--filter> + +Add a PVQL filter (Example: --filter='host = "www.lo.ki"') + +=item B<--from> + +Add a PVQL from clause to filter on a specific layer (Default: 'http') + +=item B<--top> + +Only search for the top X results (top is made on 'hits_error'). + +=item B<--warning-*> + +Threshold warning. +Can be: 'ratio', 'hits-error' (hits/s), 'hits' (hits/s). + +=item B<--critical-*> + +Threshold critical. +Can be: 'ratio', 'hits-error' (hits/s), 'hits' (hits/s). + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='ratio' + +=back + +=cut diff --git a/apps/pvx/restapi/mode/networkconnection.pm b/apps/pvx/restapi/mode/networkconnection.pm new file mode 100644 index 000000000..e33baef12 --- /dev/null +++ b/apps/pvx/restapi/mode/networkconnection.pm @@ -0,0 +1,200 @@ +# +# Copyright 2018 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::pvx::restapi::mode::networkconnection; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'instances', type => 1, cb_prefix_output => 'prefix_instances_output', message_multiple => 'All connection metrics are ok' }, + ]; + + $self->{maps_counters}->{instances} = [ + { label => 'ratio', set => { + key_values => [ { name => 'syns_ratio' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Ratio: %.2f', + perfdatas => [ + { label => 'ratio', value => 'syns_ratio_absolute', template => '%.2f', + min => 0, label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + { label => 'attempt', set => { + key_values => [ { name => 'syns' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Connections Attempts: %.2f conn/s', + perfdatas => [ + { label => 'attempt', value => 'syns_absolute', template => '%.2f', + min => 0, unit => 'connections/s', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + { label => 'successful', set => { + key_values => [ { name => 'ct_count' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Successful Connections: %.2f conn/s', + perfdatas => [ + { label => 'successful', value => 'ct_count_absolute', template => '%.2f', + min => 0, unit => 'connections/s', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + { label => 'connection-time', set => { + key_values => [ { name => 'ct' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Average Connection Time: %.3f ms', + perfdatas => [ + { label => 'connection_time', value => 'ct_absolute', template => '%.3f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + ]; +} + +sub prefix_instances_output { + my ($self, %options) = @_; + + return $options{instance_value}->{instance_label} . " '" . $options{instance_value}->{key} . "' "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "instance:s" => { name => 'instance', default => 'layer' }, + "top:s" => { name => 'top' }, + "filter:s" => { name => 'filter' }, + "from:s" => { name => 'from' }, + "filter-counters:s" => { name => 'filter_counters' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{instance}) || $self->{option_results}->{instance} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --instance option as a PVQL object."); + $self->{output}->option_exit(); + } + + $self->{pvql_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $instance_label = $self->{option_results}->{instance}; + $instance_label =~ s/\./ /g; + $instance_label =~ s/(\w+)/\u\L$1/g; + + $self->{instances} = {}; + + my $apps; + if ($self->{option_results}->{instance} =~ /application/) { + my $results = $options{custom}->get_endpoint(url_path => '/get-configuration'); + $apps = $results->{applications}; + } + + my $results = $options{custom}->query_range( + query => 'syns.ratio, syns, ct.count, ct', + instance => $self->{option_results}->{instance}, + top => $self->{option_results}->{top}, + filter => $self->{option_results}->{filter}, + from => $self->{option_results}->{from}, + timeframe => $self->{pvql_timeframe}, + ); + + foreach my $result (@{$results}) { + next if (!defined(${$result->{key}}[0])); + my $instance; + $instance = ${$result->{key}}[0]->{value} if (defined(${$result->{key}}[0]->{value})); + $instance = ${$result->{key}}[0]->{status} if (defined(${$result->{key}}[0]->{status})); + $self->{instances}->{$instance}->{key} = $instance; + $self->{instances}->{$instance}->{key} = $apps->{$instance}->{name} if ($self->{option_results}->{instance} =~ /application/); + $self->{instances}->{$instance}->{syns_ratio} = (defined(${$result->{values}}[0]->{value})) ? ${$result->{values}}[0]->{value} : 1; + $self->{instances}->{$instance}->{syns} = ${$result->{values}}[1]->{value} / $self->{pvql_timeframe}; + $self->{instances}->{$instance}->{ct_count} = ${$result->{values}}[2]->{value} / $self->{pvql_timeframe}; + $self->{instances}->{$instance}->{ct} = (defined(${$result->{values}}[3]->{value})) ? ${$result->{values}}[3]->{value} * 1000 : 0; + $self->{instances}->{$instance}->{instance_label} = $instance_label; + } + + if (scalar(keys %{$self->{instances}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No instances or results found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check instances connection metrics. + +=over 8 + +=item B<--instance> + +Filter on a specific instance (Must be a PVQL object, Default: 'layer') + +(Object 'application' will be mapped with applications name) + +=item B<--filter> + +Add a PVQL filter (Example: --filter='application = "mysql"') + +=item B<--from> + +Add a PVQL from clause to filter on a specific layer (Example: --from='tcp') + +=item B<--top> + +Only search for the top X results (top is made on 'ratio'). + +=item B<--warning-*> + +Threshold warning. +Can be: 'ratio', 'attempt' (conn/s), 'successful' (conn/s), 'connection-time' (ms). + +=item B<--critical-*> + +Threshold critical. +Can be: 'ratio', 'attempt' (conn/s), 'successful' (conn/s), 'connection-time' (ms). + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='ratio' + +=back + +=cut diff --git a/apps/pvx/restapi/mode/networktraffic.pm b/apps/pvx/restapi/mode/networktraffic.pm new file mode 100644 index 000000000..226353a58 --- /dev/null +++ b/apps/pvx/restapi/mode/networktraffic.pm @@ -0,0 +1,238 @@ +# +# Copyright 2018 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::pvx::restapi::mode::networktraffic; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0, cb_init => 'skip_global' }, + { name => 'instances', type => 1, cb_prefix_output => 'prefix_instances_output', message_multiple => 'All traffic usage are ok' }, + ]; + + $self->{maps_counters}->{global} = [ + { label => 'total-traffic', set => { + key_values => [ { name => 'total_traffic' } ], + output_template => 'Total Traffic: %.2f %s/s', + output_change_bytes => 2, + perfdatas => [ + { label => 'total_traffic', value => 'total_traffic_absolute', template => '%d', + min => 0, unit => 'b/s' }, + ], + } + }, + { label => 'total-server-traffic', set => { + key_values => [ { name => 'total_server_traffic' } ], + output_template => 'Total Server Traffic: %.2f %s/s', + output_change_bytes => 2, + perfdatas => [ + { label => 'total_server_traffic', value => 'total_server_traffic_absolute', template => '%d', + min => 0, unit => 'b/s' }, + ], + } + }, + { label => 'total-client-traffic', set => { + key_values => [ { name => 'total_client_traffic' } ], + output_template => 'Total Client Traffic: %.2f %s/s', + output_change_bytes => 2, + perfdatas => [ + { label => 'total_client_traffic', value => 'total_client_traffic_absolute', template => '%d', + min => 0, unit => 'b/s' }, + ], + } + }, + ]; + $self->{maps_counters}->{instances} = [ + { label => 'traffic', set => { + key_values => [ { name => 'traffic' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Traffic: %.2f %s/s', + output_change_bytes => 2, + perfdatas => [ + { label => 'traffic', value => 'traffic_absolute', template => '%d', + min => 0, unit => 'b/s', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + { label => 'server-traffic', set => { + key_values => [ { name => 'server_traffic' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Server Traffic: %.2f %s/s', + output_change_bytes => 2, + perfdatas => [ + { label => 'server_traffic', value => 'server_traffic_absolute', template => '%d', + min => 0, unit => 'b/s', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + { label => 'client-traffic', set => { + key_values => [ { name => 'client_traffic' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'Client Traffic: %.2f %s/s', + output_change_bytes => 2, + perfdatas => [ + { label => 'client_traffic', value => 'client_traffic_absolute', template => '%d', + min => 0, unit => 'b/s', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + ]; +} + +sub skip_global { + my ($self, %options) = @_; + + scalar(keys %{$self->{instances}}) > 1 ? return(0) : return(1); +} + + +sub prefix_instances_output { + my ($self, %options) = @_; + + return $options{instance_value}->{instance_label} . " '" . $options{instance_value}->{key} . "' "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "instance:s" => { name => 'instance', default => 'layer' }, + "top:s" => { name => 'top' }, + "filter:s" => { name => 'filter' }, + "from:s" => { name => 'from' }, + "filter-counters:s" => { name => 'filter_counters' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{instance}) || $self->{option_results}->{instance} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --instance option as a PVQL object."); + $self->{output}->option_exit(); + } + + $self->{pvql_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $instance_label = $self->{option_results}->{instance}; + $instance_label =~ s/\./ /g; + $instance_label =~ s/(\w+)/\u\L$1/g; + + $self->{instances} = {}; + + my $apps; + if ($self->{option_results}->{instance} =~ /application/) { + my $results = $options{custom}->get_endpoint(url_path => '/get-configuration'); + $apps = $results->{applications}; + } + + my $results = $options{custom}->query_range( + query => 'traffic, server.traffic, client.traffic', + instance => $self->{option_results}->{instance}, + top => $self->{option_results}->{top}, + filter => $self->{option_results}->{filter}, + from => $self->{option_results}->{from}, + timeframe => $self->{pvql_timeframe}, + ); + + foreach my $result (@{$results}) { + next if (!defined(${$result->{key}}[0])); + my $instance; + $instance = ${$result->{key}}[0]->{value} if (defined(${$result->{key}}[0]->{value})); + $instance = ${$result->{key}}[0]->{status} if (defined(${$result->{key}}[0]->{status})); + $self->{instances}->{$instance}->{key} = $instance; + $self->{instances}->{$instance}->{key} = $apps->{$instance}->{name} if ($self->{option_results}->{instance} =~ /application/); + $self->{instances}->{$instance}->{traffic} = ${$result->{values}}[0]->{value} * 8 / $self->{pvql_timeframe}; + $self->{instances}->{$instance}->{server_traffic} = ${$result->{values}}[1]->{value} * 8 / $self->{pvql_timeframe}; + $self->{instances}->{$instance}->{client_traffic} = ${$result->{values}}[2]->{value} * 8 / $self->{pvql_timeframe}; + $self->{instances}->{$instance}->{instance_label} = $instance_label; + $self->{global}->{total_traffic} += ${$result->{values}}[0]->{value} * 8 / $self->{pvql_timeframe}; + $self->{global}->{total_server_traffic} += ${$result->{values}}[1]->{value} * 8 / $self->{pvql_timeframe}; + $self->{global}->{total_client_traffic} += ${$result->{values}}[2]->{value} * 8 / $self->{pvql_timeframe}; + } + + if (scalar(keys %{$self->{instances}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No instances or results found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check instances traffic usage. + +=over 8 + +=item B<--instance> + +Filter on a specific instance (Must be a PVQL object, Default: 'layer') + +(Object 'application' will be mapped with applications name) + +=item B<--filter> + +Add a PVQL filter (Example: --filter='application = "mysql"') + +=item B<--from> + +Add a PVQL from clause to filter on a specific layer (Example: --from='tcp') + +=item B<--top> + +Only search for the top X results (top is made on 'traffic'). + +=item B<--warning-*> + +Threshold warning. +Can be: 'total-traffic', 'total-client-traffic', 'total-server-traffic', +'traffic', 'client-traffic', 'server-traffic'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'total-traffic', 'total-client-traffic', 'total-server-traffic', +'traffic', 'client-traffic', 'server-traffic'. + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='total-traffic' + +=back + +=cut diff --git a/apps/pvx/restapi/mode/networkuserexperience.pm b/apps/pvx/restapi/mode/networkuserexperience.pm new file mode 100644 index 000000000..0eadb30eb --- /dev/null +++ b/apps/pvx/restapi/mode/networkuserexperience.pm @@ -0,0 +1,162 @@ +# +# Copyright 2018 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::pvx::restapi::mode::networkuserexperience; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'instances', type => 1, cb_prefix_output => 'prefix_instances_output', message_multiple => 'All metrics are ok' }, + ]; + + $self->{maps_counters}->{instances} = [ + { label => 'time', set => { + key_values => [ { name => 'user_experience' }, { name => 'key' }, { name => 'instance_label' } ], + output_template => 'End-User Experience: %.3f s', + perfdatas => [ + { label => 'time', value => 'user_experience_absolute', template => '%.3f', + min => 0, unit => 's', label_extra_instance => 1, instance_use => 'key_absolute' }, + ], + } + }, + ]; +} + +sub prefix_instances_output { + my ($self, %options) = @_; + + return $options{instance_value}->{instance_label} . " '" . $options{instance_value}->{key} . "' "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "instance:s" => { name => 'instance', default => 'layer' }, + "top:s" => { name => 'top' }, + "filter:s" => { name => 'filter' }, + "from:s" => { name => 'from' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{instance}) || $self->{option_results}->{instance} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --instance option as a PVQL object."); + $self->{output}->option_exit(); + } + + $self->{pvql_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $instance_label = $self->{option_results}->{instance}; + $instance_label =~ s/\./ /g; + $instance_label =~ s/(\w+)/\u\L$1/g; + + $self->{instances} = {}; + + my $apps; + if ($self->{option_results}->{instance} =~ /application/) { + my $results = $options{custom}->get_endpoint(url_path => '/get-configuration'); + $apps = $results->{applications}; + } + + my $results = $options{custom}->query_range( + query => 'user.experience', + instance => $self->{option_results}->{instance}, + top => $self->{option_results}->{top}, + filter => $self->{option_results}->{filter}, + from => $self->{option_results}->{from}, + timeframe => $self->{pvql_timeframe}, + ); + + foreach my $result (@{$results}) { + next if (!defined(${$result->{key}}[0])); + my $instance; + $instance = ${$result->{key}}[0]->{value} if (defined(${$result->{key}}[0]->{value})); + $instance = ${$result->{key}}[0]->{status} if (defined(${$result->{key}}[0]->{status})); + $self->{instances}->{$instance}->{key} = $instance; + $self->{instances}->{$instance}->{key} = $apps->{$instance}->{name} if ($self->{option_results}->{instance} =~ /application/); + $self->{instances}->{$instance}->{user_experience} = (defined(${$result->{values}}[0]->{value})) ? ${$result->{values}}[0]->{value} : 0; + $self->{instances}->{$instance}->{instance_label} = $instance_label; + } + + if (scalar(keys %{$self->{instances}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No instances or results found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check instances connection metrics. + +=over 8 + +=item B<--instance> + +Filter on a specific instance (Must be a PVQL object, Default: 'layer') + +(Object 'application' will be mapped with applications name) + +=item B<--filter> + +Add a PVQL filter (Example: --filter='application = "mysql"') + +=item B<--from> + +Add a PVQL from clause to filter on a specific layer (Example: --from='tcp') + +=item B<--top> + +Only search for the top X results. + +=item B<--warning-time> + +Threshold warning (s). + +=item B<--critical-time> + +Threshold critical (s). + +=back + +=cut diff --git a/apps/pvx/restapi/plugin.pm b/apps/pvx/restapi/plugin.pm new file mode 100644 index 000000000..70bbc7141 --- /dev/null +++ b/apps/pvx/restapi/plugin.pm @@ -0,0 +1,59 @@ +# +# Copyright 2018 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::pvx::restapi::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_custom); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '0.1'; + %{$self->{modes}} = ( + 'http-hits' => 'apps::pvx::restapi::mode::httphits', + 'network-connection' => 'apps::pvx::restapi::mode::networkconnection', + 'network-user-experience' => 'apps::pvx::restapi::mode::networkuserexperience', + 'network-traffic' => 'apps::pvx::restapi::mode::networktraffic', + ); + + $self->{custom_modes}{api} = 'apps::pvx::restapi::custom::api'; + return $self; +} + +sub init { + my ( $self, %options ) = @_; + + $self->SUPER::init(%options); +} + + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check PVX (Performance Vision). + +=cut