From 1977322edb2d5bbe7fe6781e3f9b921d2be9bcb2 Mon Sep 17 00:00:00 2001 From: qgarnier Date: Wed, 4 Oct 2017 13:45:55 +0200 Subject: [PATCH] change elastic search plugin --- apps/elasticsearch/mode/cluster.pm | 204 ------------------- apps/elasticsearch/mode/indices.pm | 200 ------------------- apps/elasticsearch/mode/nodescount.pm | 171 ---------------- apps/elasticsearch/restapi/custom/api.pm | 202 +++++++++++++++++++ apps/elasticsearch/restapi/mode/cluster.pm | 203 +++++++++++++++++++ apps/elasticsearch/restapi/mode/indices.pm | 219 +++++++++++++++++++++ apps/elasticsearch/restapi/mode/nodes.pm | 144 ++++++++++++++ apps/elasticsearch/{ => restapi}/plugin.pm | 13 +- 8 files changed, 775 insertions(+), 581 deletions(-) delete mode 100644 apps/elasticsearch/mode/cluster.pm delete mode 100644 apps/elasticsearch/mode/indices.pm delete mode 100644 apps/elasticsearch/mode/nodescount.pm create mode 100644 apps/elasticsearch/restapi/custom/api.pm create mode 100644 apps/elasticsearch/restapi/mode/cluster.pm create mode 100644 apps/elasticsearch/restapi/mode/indices.pm create mode 100644 apps/elasticsearch/restapi/mode/nodes.pm rename apps/elasticsearch/{ => restapi}/plugin.pm (69%) diff --git a/apps/elasticsearch/mode/cluster.pm b/apps/elasticsearch/mode/cluster.pm deleted file mode 100644 index 79b398efe..000000000 --- a/apps/elasticsearch/mode/cluster.pm +++ /dev/null @@ -1,204 +0,0 @@ -# -# Copyright 2017 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::elasticsearch::mode::cluster; - -use base qw(centreon::plugins::mode); - -use strict; -use warnings; -use centreon::plugins::http; -use JSON; - -my $thresholds = { - cluster => [ - ['green', 'OK'], - ['yellow', 'WARNING'], - ['red', 'CRITICAL'], - ], -}; - -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 => - { - "hostname:s" => { name => 'hostname' }, - "port:s" => { name => 'port', default => 9200 }, - "proto:s" => { name => 'proto' }, - "urlpath:s" => { name => 'url_path', default => '/_cluster/health' }, - "credentials" => { name => 'credentials' }, - "username:s" => { name => 'username' }, - "password:s" => { name => 'password' }, - "timeout:s" => { name => 'timeout' }, - "threshold-overload:s@" => { name => 'threshold_overload' }, - }); - - $self->{http} = centreon::plugins::http->new(output => $self->{output}); - return $self; -} - -sub check_options { - my ($self, %options) = @_; - $self->SUPER::init(%options); - - $self->{overload_th} = {}; - foreach my $val (@{$self->{option_results}->{threshold_overload}}) { - if ($val !~ /^(.*?),(.*?),(.*)$/) { - $self->{output}->add_option_msg(short_msg => "Wrong threshold-overload option '" . $val . "'."); - $self->{output}->option_exit(); - } - my ($section, $status, $filter) = ($1, $2, $3); - if ($self->{output}->is_litteral_status(status => $status) == 0) { - $self->{output}->add_option_msg(short_msg => "Wrong threshold-overload status '" . $val . "'."); - $self->{output}->option_exit(); - } - $self->{overload_th}->{$section} = [] if (!defined($self->{overload_th}->{$section})); - push @{$self->{overload_th}->{$section}}, {filter => $filter, status => $status}; - } - - $self->{http}->set_options(%{$self->{option_results}}); -} - -sub get_severity { - my ($self, %options) = @_; - my $status = 'UNKNOWN'; # default - - if (defined($self->{overload_th}->{$options{section}})) { - foreach (@{$self->{overload_th}->{$options{section}}}) { - if ($options{value} =~ /$_->{filter}/i) { - $status = $_->{status}; - return $status; - } - } - } - foreach (@{$thresholds->{$options{section}}}) { - if ($options{value} =~ /$$_[0]/i) { - $status = $$_[1]; - return $status; - } - } - return $status; -} - - -sub run { - my ($self, %options) = @_; - - my $jsoncontent = $self->{http}->request(); - - my $json = JSON->new; - - my $webcontent; - - eval { - $webcontent = $json->decode($jsoncontent); - }; - - if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); - $self->{output}->option_exit(); - } - - my $exit = $self->get_severity(section => 'cluster', value => $webcontent->{status}); - - if ($webcontent->{status} eq 'green') { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("All shard are allocated for %s", $webcontent->{cluster_name})); - } elsif ($webcontent->{status} eq 'yellow') { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Primary shards are allocated but replicas not for %s", $webcontent->{cluster_name})); - } elsif ($webcontent->{status} eq 'red') { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Some or all primary shards aren't ready for %s", $webcontent->{cluster_name})); - } - - $self->{output}->perfdata_add(label => 'primary_shard', - value => sprintf("%d", $webcontent->{active_primary_shards}), - min => 0, - ); - $self->{output}->perfdata_add(label => 'shard', - value => sprintf("%d", $webcontent->{active_shards}), - min => 0, - ); - $self->{output}->perfdata_add(label => 'unassigned_shard', - value => sprintf("%d", $webcontent->{unassigned_shards}), - min => 0, - ); - - $self->{output}->display(); - $self->{output}->exit(); - -} - -1; - -__END__ - -=head1 MODE - -Check Elasticsearch cluster health - -=over 8 - -=item B<--hostname> - -IP Addr/FQDN of the Elasticsearch host - -=item B<--port> - -Port used by Elasticsearch API (Default: '9200') - -=item B<--proto> - -Specify https if needed (Default: 'http') - -=item B<--urlpath> - -Set path to get Elasticsearch information (Default: '/_cluster/health') - -=item B<--credentials> - -Specify this option if you access webpage over basic authentification - -=item B<--username> - -Specify username for API authentification - -=item B<--password> - -Specify password for API authentification - -=item B<--timeout> - -Threshold for HTTP timeout (Default: 5) - -=item B<--threshold-overload> - -Set to overload default threshold values (syntax: section,status,regexp) -It used before default thresholds (order stays). -Example: --threshold-overload='cluster,CRITICAL,^(?!(on)$)' - -=back - -=cut diff --git a/apps/elasticsearch/mode/indices.pm b/apps/elasticsearch/mode/indices.pm deleted file mode 100644 index cd05970f1..000000000 --- a/apps/elasticsearch/mode/indices.pm +++ /dev/null @@ -1,200 +0,0 @@ -# -# Copyright 2017 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::elasticsearch::mode::indices; - -use base qw(centreon::plugins::mode); - -use strict; -use warnings; -use centreon::plugins::http; -use JSON; -use Data::Dumper; - -my $thresholds = { - indices => [ - ['green', 'OK'], - ['yellow', 'WARNING'], - ['red', 'CRITICAL'], - ], -}; - -my %map_states_indices = ( - OK => 'green', - WARNING => 'yellow', - CRITICAL => 'red', -); - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options); - bless $self, $class; - - $self->{version} = '1.1'; - $options{options}->add_options(arguments => - { - "hostname:s" => { name => 'hostname' }, - "port:s" => { name => 'port', default => 9200 }, - "proto:s" => { name => 'proto' }, - "urlpath:s" => { name => 'url_path', default => '/_cluster/health' }, - "credentials" => { name => 'credentials' }, - "username:s" => { name => 'username' }, - "password:s" => { name => 'password' }, - "timeout:s" => { name => 'timeout' }, - "threshold-overload:s@" => { name => 'threshold_overload' }, - }); - - $self->{http} = centreon::plugins::http->new(output => $self->{output}); - return $self; -} - -sub check_options { - my ($self, %options) = @_; - $self->SUPER::init(%options); - - $self->{overload_th} = {}; - foreach my $val (@{$self->{option_results}->{threshold_overload}}) { - if ($val !~ /^(.*?),(.*?),(.*)$/) { - $self->{output}->add_option_msg(short_msg => "Wrong threshold-overload option '" . $val . "'."); - $self->{output}->option_exit(); - } - my ($section, $status, $filter) = ($1, $2, $3); - if ($self->{output}->is_litteral_status(status => $status) == 0) { - $self->{output}->add_option_msg(short_msg => "Wrong threshold-overload status '" . $val . "'."); - $self->{output}->option_exit(); - } - $self->{overload_th}->{$section} = [] if (!defined($self->{overload_th}->{$section})); - push @{$self->{overload_th}->{$section}}, {filter => $filter, status => $status}; - } - - $self->{option_results}->{get_param} = [ 'level=indices' ]; - $self->{http}->set_options(%{$self->{option_results}}); -} - -sub get_severity { - my ($self, %options) = @_; - my $status = 'UNKNOWN'; # default - - if (defined($self->{overload_th}->{$options{section}})) { - foreach (@{$self->{overload_th}->{$options{section}}}) { - if ($options{value} =~ /$_->{filter}/i) { - $status = $_->{status}; - return $status; - } - } - } - foreach (@{$thresholds->{$options{section}}}) { - if ($options{value} =~ /$$_[0]/i) { - $status = $$_[1]; - return $status; - } - } - - return $status; -} - -sub run { - my ($self, %options) = @_; - - my $jsoncontent = $self->{http}->request(); - - my $json = JSON->new; - my $webcontent; - - eval { - $webcontent = $json->decode($jsoncontent); - }; - if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); - $self->{output}->option_exit(); - } - - $self->{output}->output_add(severity => 'OK', - short_msg => sprintf("All indices are in green state.")); - my $exit = 'OK'; - foreach my $indicename (sort (keys %{$webcontent->{indices}})) { - my $tmp_exit = $self->get_severity(section => 'indices', value => $webcontent->{indices}->{$indicename}->{status}); - $exit = $self->{output}->get_most_critical(status => [ $tmp_exit, $exit ]); - if (!$self->{output}->is_status(value => $tmp_exit, compare => 'OK', litteral => 1)) { - $self->{output}->output_add(long_msg => sprintf("Indice %s status is in %s state", - $indicename, $webcontent->{indices}->{$indicename}->{status})); - } - } - - if (!$self->{output}->is_status(value => $exit, compare => 'OK', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Some indices are in wrong state")); - } - - $self->{output}->display(); - $self->{output}->exit(); -} - -1; - -__END__ - -=head1 MODE - -Check Elasticsearch indices health - -=over 8 - -=item B<--hostname> - -IP Addr/FQDN of the Elasticsearch host - -=item B<--port> - -Port used by Elasticsearch API (Default: '9200') - -=item B<--proto> - -Specify https if needed (Default: 'http') - -=item B<--urlpath> - -Set path to get Elasticsearch information (Default: '/_cluster/health') - -=item B<--credentials> - -Specify this option if you access webpage over basic authentification - -=item B<--username> - -Specify username for API authentification - -=item B<--password> - -Specify password for API authentification - -=item B<--threshold-overload> - -Set to overload default threshold values (syntax: section,status,regexp) -It used before default thresholds (order stays). -Example: --threshold-overload='indices,CRITICAL,^(?!(on)$)' - -=item B<--timeout> - -Threshold for HTTP timeout (Default: 3) - -=back - -=cut diff --git a/apps/elasticsearch/mode/nodescount.pm b/apps/elasticsearch/mode/nodescount.pm deleted file mode 100644 index 15af2d3eb..000000000 --- a/apps/elasticsearch/mode/nodescount.pm +++ /dev/null @@ -1,171 +0,0 @@ -# -# Copyright 2017 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::elasticsearch::mode::nodescount; - -use base qw(centreon::plugins::mode); - -use strict; -use warnings; -use centreon::plugins::http; -use JSON; - -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 => - { - "hostname:s" => { name => 'hostname' }, - "port:s" => { name => 'port', default => 9200 }, - "proto:s" => { name => 'proto' }, - "urlpath:s" => { name => 'url_path', default => '/_cluster/stats' }, - "credentials" => { name => 'credentials' }, - "username:s" => { name => 'username' }, - "password:s" => { name => 'password' }, - "warning:s" => { name => 'warning' }, - "critical:s" => { name => 'critical' }, - "timeout:s" => { name => 'timeout' }, - }); - - $self->{http} = centreon::plugins::http->new(output => $self->{output}); - return $self; -} - -sub check_options { - my ($self, %options) = @_; - $self->SUPER::init(%options); - - if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) { - $self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'."); - $self->{output}->option_exit(); - } - if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) { - $self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'."); - $self->{output}->option_exit(); - } - - $self->{http}->set_options(%{$self->{option_results}}); -} - -sub run { - my ($self, %options) = @_; - - my $jsoncontent = $self->{http}->request(); - - my $json = JSON->new; - my $webcontent; - - eval { - $webcontent = $json->decode($jsoncontent); - }; - - if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); - $self->{output}->option_exit(); - } - - my $exit = $self->{perfdata}->threshold_check(value => $webcontent->{nodes}->{count}->{total}, threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]); - - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Number of nodes for cluster %s : %d", - $webcontent->{cluster_name}, $webcontent->{nodes}->{count}->{total})); - $self->{output}->perfdata_add(label => "node", - value => $webcontent->{nodes}->{count}->{total}, - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'), - min => 0, - ); - $self->{output}->perfdata_add(label => "nodemasteronly", - value => $webcontent->{nodes}->{count}->{master_only}, - min => 0, - ); - $self->{output}->perfdata_add(label => "nodedataonly", - value => $webcontent->{nodes}->{count}->{data_only}, - min => 0, - ); - $self->{output}->perfdata_add(label => "nodemasterdata", - value => $webcontent->{nodes}->{count}->{master_data}, - min => 0, - ); - $self->{output}->perfdata_add(label => "nodeclient", - value => $webcontent->{nodes}->{count}->{client}, - min => 0, - ); - $self->{output}->display(); - $self->{output}->exit(); - -} - -1; - -__END__ - -=head1 MODE - -Check Elasticsearch number of nodes - -=over 8 - -=item B<--hostname> - -IP Addr/FQDN of the Elasticsearch host - -=item B<--port> - -Port used by Elasticsearch API (Default: '9200') - -=item B<--proto> - -Specify https if needed (Default: 'http') - -=item B<--urlpath> - -Set path to get Elasticsearch information (Default: '/_cluster/stats') - -=item B<--credentials> - -Specify this option if you access webpage over basic authentification - -=item B<--username> - -Specify username for API authentification - -=item B<--password> - -Specify password for API authentification - -=item B<--warning> - -Threshold warning. - -=item B<--critical> - -Threshold critical. - -=item B<--timeout> - -Threshold for HTTP timeout (Default: 5) - -=back - -=cut diff --git a/apps/elasticsearch/restapi/custom/api.pm b/apps/elasticsearch/restapi/custom/api.pm new file mode 100644 index 000000000..d5d130d9b --- /dev/null +++ b/apps/elasticsearch/restapi/custom/api.pm @@ -0,0 +1,202 @@ +# +# Copyright 2017 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::elasticsearch::restapi::custom::api; + +use strict; +use warnings; +use centreon::plugins::http; +use JSON::XS; + +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 => + { + "hostname:s@" => { name => 'hostname' }, + "port:s@" => { name => 'port' }, + "proto:s@" => { name => 'proto' }, + "username:s@" => { name => 'username' }, + "password:s@" => { name => 'password' }, + "proxyurl:s@" => { name => 'proxyurl' }, + "timeout:s@" => { name => 'timeout' }, + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API 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->{hostname} = (defined($self->{option_results}->{hostname})) ? shift(@{$self->{option_results}->{hostname}}) : undef; + $self->{port} = (defined($self->{option_results}->{port})) ? shift(@{$self->{option_results}->{port}}) : 9200; + $self->{proto} = (defined($self->{option_results}->{proto})) ? shift(@{$self->{option_results}->{proto}}) : 'http'; + $self->{username} = (defined($self->{option_results}->{username})) ? shift(@{$self->{option_results}->{username}}) : ''; + $self->{password} = (defined($self->{option_results}->{password})) ? shift(@{$self->{option_results}->{password}}) : ''; + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? shift(@{$self->{option_results}->{timeout}}) : 10; + $self->{proxyurl} = (defined($self->{option_results}->{proxyurl})) ? shift(@{$self->{option_results}->{proxyurl}}) : undef; + + if (!defined($self->{hostname})) { + $self->{output}->add_option_msg(short_msg => "Need to specify hostname option."); + $self->{output}->option_exit(); + } + + if (!defined($self->{hostname}) || + scalar(@{$self->{option_results}->{hostname}}) == 0) { + return 0; + } + return 1; +} + +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} = 1; + $self->{option_results}->{username} = $self->{username}; + $self->{option_results}->{password} = $self->{password}; +} + +sub settings { + my ($self, %options) = @_; + + $self->build_options_for_httplib(); + $self->{http}->set_options(%{$self->{option_results}}); +} + +sub get { + my ($self, %options) = @_; + + $self->settings(); + + my $response = $self->{http}->request(url_path => $options{path}, + critical_status => '', warning_status => ''); + 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 (defined($content->{errmsg})) { + $self->{output}->add_option_msg(short_msg => "Cannot get data: " . $content->{errmsg}); + $self->{output}->option_exit(); + } + + return $content; +} + +1; + +__END__ + +=head1 NAME + +Elasticsearch REST API + +=head1 SYNOPSIS + +Elasticsearch Rest API custom mode + +=head1 REST API OPTIONS + +=over 8 + +=item B<--hostname> + +Elasticsearch hostname. + +=item B<--port> + +Port used (Default: 9200) + +=item B<--proto> + +Specify https if needed (Default: 'http') + +=item B<--username> + +Elasticsearch username. + +=item B<--password> + +Elasticsearch password. + +=item B<--proxyurl> + +Proxy URL if any + +=item B<--timeout> + +Set HTTP timeout + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/apps/elasticsearch/restapi/mode/cluster.pm b/apps/elasticsearch/restapi/mode/cluster.pm new file mode 100644 index 000000000..541cd924e --- /dev/null +++ b/apps/elasticsearch/restapi/mode/cluster.pm @@ -0,0 +1,203 @@ +# +# Copyright 2017 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::elasticsearch::restapi::mode::cluster; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + my $msg = "Cluster '" . $self->{result_values}->{display} . "' Status : " . $self->{result_values}->{status}; + + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'}; + return 0; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0 }, + ]; + + $self->{maps_counters}->{global} = [ + { label => 'status', threshold => 0, set => { + key_values => [ { name => 'status' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_status_calc'), + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => $self->can('custom_status_threshold'), + } + }, + { label => 'active-primary-shards', set => { + key_values => [ { name => 'active_primary_shards' } ], + output_template => 'Active Primary Shards : %s', + perfdatas => [ + { label => 'active_primary_shards', value => 'active_primary_shards_absolute', template => '%s', + min => 0 }, + ], + } + }, + { label => 'active-shards', set => { + key_values => [ { name => 'active_shards' } ], + output_template => 'Active Shards : %s', + perfdatas => [ + { label => 'active_shards', value => 'active_shards_absolute', template => '%s', + min => 0 }, + ], + } + }, + { label => 'unassigned-shards', set => { + key_values => [ { name => 'unassigned_shards' } ], + output_template => 'Unassigned Shards : %s', + perfdatas => [ + { label => 'unassigned_shards', value => 'unassigned_shards_absolute', template => '%s', + min => 0 }, + ], + } + }, + ]; +} + +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 => + { + "elastic-path:s" => { name => 'elastic_path', default => '/_cluster/health' }, + "warning-status:s" => { name => 'warning_status', default => '%{status} =~ /yellow/i' }, + "critical-status:s" => { name => 'critical_status', default => '%{status} =~ /red/i' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub change_macros { + my ($self, %options) = @_; + + foreach (('warning_status', 'critical_status')) { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => $self->{option_results}->{elastic_path}); + $self->{global} = { + display => $result->{cluster_name}, + status => $result->{status}, + active_primary_shards => $result->{active_primary_shards}, + active_shards => $result->{active_shards}, + unassigned_shards => $result->{unassigned_shards}, + }; +} + +1; + +__END__ + +=head1 MODE + +Check Elasticsearch cluster. + +=over 8 + +=item B<--elastic-path> + +Set path to get Elasticsearch information (Default: '/_cluster/health') + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='^status$' + +=item B<--warning-*> + +Threshold warning. +Can be: 'active-primary-shards', 'active-shards', 'unassigned-shards'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'active-primary-shards', 'active-shards', 'unassigned-shards'. + +=item B<--warning-status> + +Set warning threshold for status (Default: '%{status} =~ /yellow/i') +Can used special variables like: %{status}. + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} =~ /red/i'). +Can used special variables like: %{status}. + +=back + +=cut diff --git a/apps/elasticsearch/restapi/mode/indices.pm b/apps/elasticsearch/restapi/mode/indices.pm new file mode 100644 index 000000000..cdc6be4f6 --- /dev/null +++ b/apps/elasticsearch/restapi/mode/indices.pm @@ -0,0 +1,219 @@ +# +# Copyright 2017 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::elasticsearch::restapi::mode::indices; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + my $msg = 'status : ' . $self->{result_values}->{status}; + + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_display'}; + return 0; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'indices', type => 1, cb_prefix_output => 'prefix_indices_output', message_multiple => 'All indices are ok' }, + ]; + + $self->{maps_counters}->{indices} = [ + { label => 'status', threshold => 0, set => { + key_values => [ { name => 'status' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_status_calc'), + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => $self->can('custom_status_threshold'), + } + }, + { label => 'active-primary-shards', set => { + key_values => [ { name => 'active_primary_shards' }, { name => 'display' } ], + output_template => 'Active Primary Shards : %s', + perfdatas => [ + { label => 'active_primary_shards', value => 'active_primary_shards_absolute', template => '%s', + min => 0, label_extra_instance => 1, instance_use => 'display_absolute' }, + ], + } + }, + { label => 'active-shards', set => { + key_values => [ { name => 'active_shards' }, { name => 'display' } ], + output_template => 'Active Shards : %s', + perfdatas => [ + { label => 'active_shards', value => 'active_shards_absolute', template => '%s', + min => 0, label_extra_instance => 1, instance_use => 'display_absolute' }, + ], + } + }, + ]; +} + +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 => + { + "elastic-path:s" => { name => 'elastic_path', default => '/_cluster/health?level=indices' }, + "filter-name:s" => { name => 'filter_name' }, + "warning-status:s" => { name => 'warning_status', default => '%{status} =~ /yellow/i' }, + "critical-status:s" => { name => 'critical_status', default => '%{status} =~ /red/i' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub prefix_indices_output { + my ($self, %options) = @_; + + return "Indices '" . $options{instance_value}->{display} . "' "; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach (('warning_status', 'critical_status')) { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub manage_selection { + my ($self, %options) = @_; + + $self->{indices} = {}; + my $result = $options{custom}->get(path => $self->{option_results}->{elastic_path}); + + foreach my $indice (keys %{$result->{indices}}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $indice !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $indice . "': no matching filter.", debug => 1); + next; + } + + $self->{indices}->{$indice} = { + display => $indice, + status => $result->{indices}->{$indice}->{status}, + active_primary_shards => $result->{indices}->{$indice}->{active_primary_shards}, + active_shards => $result->{indices}->{$indice}->{active_shards}, + }; + } + + if (scalar(keys %{$self->{indices}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No indices found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check Elasticsearch indices. + +=over 8 + +=item B<--elastic-path> + +Set path to get Elasticsearch information (Default: '/_cluster/health?level=indices') + +=item B<--filter-name> + +Filter name (can be a regexp). + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='^status$' + +=item B<--warning-*> + +Threshold warning. +Can be: 'active-primary-shards', 'active-shards'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'active-primary-shards', 'active-shards'. + +=item B<--warning-status> + +Set warning threshold for status (Default: '%{status} =~ /yellow/i') +Can used special variables like: %{display}, %{status}. + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} =~ /red/i'). +Can used special variables like: %{display}, %{status}. + +=back + +=cut diff --git a/apps/elasticsearch/restapi/mode/nodes.pm b/apps/elasticsearch/restapi/mode/nodes.pm new file mode 100644 index 000000000..bc71a28c1 --- /dev/null +++ b/apps/elasticsearch/restapi/mode/nodes.pm @@ -0,0 +1,144 @@ +# +# Copyright 2017 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::elasticsearch::restapi::mode::nodes; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'nodes', type => 0, cb_prefix_output => 'prefix_output' }, + ]; + + $self->{maps_counters}->{nodes} = [ + { label => 'total', set => { + key_values => [ { name => 'total' } ], + output_template => 'Total : %s', + perfdatas => [ + { label => 'total', value => 'total_absolute', template => '%s', + min => 0 }, + ], + } + }, + { label => 'masteronly', set => { + key_values => [ { name => 'master_only' } ], + output_template => 'Master Only : %s', + perfdatas => [ + { label => 'master_only', value => 'master_only_absolute', template => '%s', + min => 0 }, + ], + } + }, + { label => 'dataonly', set => { + key_values => [ { name => 'data_only' } ], + output_template => 'Data Only : %s', + perfdatas => [ + { label => 'data_only', value => 'data_only_absolute', template => '%s', + min => 0 }, + ], + } + }, + { label => 'masterdata', set => { + key_values => [ { name => 'master_data' } ], + output_template => 'Master Data : %s', + perfdatas => [ + { label => 'master_data', value => 'master_data_absolute', template => '%s', + min => 0 }, + ], + } + }, + { label => 'client', set => { + key_values => [ { name => 'client' } ], + output_template => 'Client : %s', + perfdatas => [ + { label => 'client', value => 'client_absolute', template => '%s', + min => 0 }, + ], + } + }, + ]; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Nodes "; +} + +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 => + { + "elastic-path:s" => { name => 'elastic_path', default => '/_cluster/stats' }, + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => $self->{option_results}->{elastic_path}); + $self->{nodes} = { + %{$result->{nodes}->{count}} + }; +} + +1; + +__END__ + +=head1 MODE + +Check Elasticsearch nodes. + +=over 8 + +=item B<--elastic-path> + +Set path to get Elasticsearch information (Default: '/_cluster/stats') + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='^total$' + +=item B<--warning-*> + +Threshold warning. +Can be: 'total', 'masteronly', 'dataonly', 'masterdata', 'client'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'total', 'masteronly', 'dataonly', 'masterdata', 'client'. + +=back + +=cut diff --git a/apps/elasticsearch/plugin.pm b/apps/elasticsearch/restapi/plugin.pm similarity index 69% rename from apps/elasticsearch/plugin.pm rename to apps/elasticsearch/restapi/plugin.pm index 3407ba309..24598f393 100644 --- a/apps/elasticsearch/plugin.pm +++ b/apps/elasticsearch/restapi/plugin.pm @@ -18,11 +18,11 @@ # limitations under the License. # -package apps::elasticsearch::plugin; +package apps::elasticsearch::restapi::plugin; use strict; use warnings; -use base qw(centreon::plugins::script_simple); +use base qw(centreon::plugins::script_custom); sub new { my ($class, %options) = @_; @@ -31,10 +31,11 @@ sub new { $self->{version} = '0.1'; %{$self->{modes}} = ( - 'cluster' => 'apps::elasticsearch::mode::cluster', - 'nodescount' => 'apps::elasticsearch::mode::nodescount', - 'indices' => 'apps::elasticsearch::mode::indices', + 'cluster' => 'apps::elasticsearch::restapi::mode::cluster', + 'indices' => 'apps::elasticsearch::restapi::mode::indices', + 'nodes' => 'apps::elasticsearch::restapi::mode::nodes', ); + $self->{custom_modes}{api} = 'apps::elasticsearch::restapi::custom::api'; return $self; } @@ -44,6 +45,6 @@ __END__ =head1 PLUGIN DESCRIPTION -Check elasticsearch status. +Check elasticsearch through HTTP/REST API. =cut