diff --git a/.github/docker/Dockerfile.packaging-plugins-alma8 b/.github/docker/Dockerfile.packaging-plugins-alma8 index a4183760b..517f727b1 100644 --- a/.github/docker/Dockerfile.packaging-plugins-alma8 +++ b/.github/docker/Dockerfile.packaging-plugins-alma8 @@ -1,4 +1,4 @@ -FROM docker-proxy.centreon.com/almalinux:8.5 +FROM docker-proxy.centreon.com/almalinux:8 RUN < Source: https://www.centreon.com Files: * -Copyright: 2021 Centreon +Copyright: 2023 Centreon License: Apache-2.0 Files: debian/* -Copyright: 2021 Centreon +Copyright: 2023 Centreon License: Apache-2.0 License: Apache-2.0 diff --git a/.github/packaging/rpm/plugin.spectemplate b/.github/packaging/rpm/plugin.spectemplate index 3e4b32699..3e7bae4ab 100644 --- a/.github/packaging/rpm/plugin.spectemplate +++ b/.github/packaging/rpm/plugin.spectemplate @@ -4,7 +4,7 @@ Release: @RELEASE@%{?dist} Source0: %{name}-%{version}.tar.gz Summary: @SUMMARY@ Group: Development/Libraries -License: ASL 2.0 +License: Apache-2.0 URL: https://www.centreon.com/ BuildArch: noarch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) diff --git a/.github/workflows/docker-builder-packaging-plugins.yml b/.github/workflows/docker-builder-packaging-plugins.yml index 3d8b9ab7d..307759f7c 100644 --- a/.github/workflows/docker-builder-packaging-plugins.yml +++ b/.github/workflows/docker-builder-packaging-plugins.yml @@ -19,11 +19,11 @@ on: jobs: create-and-push-docker: - runs-on: [self-hosted, common] + runs-on: ubuntu-22.04 strategy: matrix: - distrib: [centos7, alma8, bullseye] + distrib: [centos7, alma8, alma9, bullseye] include: - project: plugins steps: diff --git a/.github/workflows/plugin-delivery.yml b/.github/workflows/plugin-delivery.yml index efb1b9ba5..e3406ec24 100644 --- a/.github/workflows/plugin-delivery.yml +++ b/.github/workflows/plugin-delivery.yml @@ -36,7 +36,7 @@ jobs: runs-on: [self-hosted, common] strategy: matrix: - distrib: [el7, el8] + distrib: [el7, el8, el9] steps: - name: Checkout sources diff --git a/.github/workflows/plugin-package.yml b/.github/workflows/plugin-package.yml index 50c09671a..222a01479 100644 --- a/.github/workflows/plugin-package.yml +++ b/.github/workflows/plugin-package.yml @@ -59,6 +59,9 @@ jobs: - package_extension: rpm image: packaging-plugins-alma8 distrib: el8 + - package_extension: rpm + image: packaging-plugins-alma9 + distrib: el9 - package_extension: deb image: packaging-plugins-bullseye distrib: bullseye diff --git a/changelog b/changelog index d5fea0577..ee61bc30f 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,11 @@ +2023-01-18 Quentin Garnier + * Plugin added: Talend TMC API + * Plugin added: Sensor APC SNMP + * Plugin added: Vectra SNMP + * Plugin added: Stormshield API + * Mode added: [netapp/ontap/restapi] 'quotas' + * Break: pure storage refactoring + 2022-11-15 Quentin Garnier * Plugin added: Thales Mistral VS9 Rest API * Plugin added: PICOS SNMP diff --git a/packaging/centreon-plugin-Applications-Jenkins/deb.json b/packaging/centreon-plugin-Applications-Jenkins/deb.json index 540c269f3..677d98118 100644 --- a/packaging/centreon-plugin-Applications-Jenkins/deb.json +++ b/packaging/centreon-plugin-Applications-Jenkins/deb.json @@ -1,5 +1,3 @@ { - "dependencies": [ - "libjson-perl" - ] + "dependencies": [] } diff --git a/packaging/centreon-plugin-Applications-Jenkins/pkg.json b/packaging/centreon-plugin-Applications-Jenkins/pkg.json index cc29e8392..a39336c60 100644 --- a/packaging/centreon-plugin-Applications-Jenkins/pkg.json +++ b/packaging/centreon-plugin-Applications-Jenkins/pkg.json @@ -3,7 +3,7 @@ "pkg_summary": "Centreon Plugin", "plugin_name": "centreon_jenkins.pl", "files": [ - "centreon/plugins/script_simple.pm", + "centreon/plugins/script_custom.pm", "apps/jenkins/" ] } diff --git a/packaging/centreon-plugin-Applications-Jenkins/rpm.json b/packaging/centreon-plugin-Applications-Jenkins/rpm.json index 186961460..677d98118 100644 --- a/packaging/centreon-plugin-Applications-Jenkins/rpm.json +++ b/packaging/centreon-plugin-Applications-Jenkins/rpm.json @@ -1,5 +1,3 @@ { - "dependencies": [ - "perl(JSON)" - ] + "dependencies": [] } diff --git a/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/deb.json b/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/deb.json new file mode 100644 index 000000000..41a67333c --- /dev/null +++ b/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/deb.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "libjmx4perl-perl" + ] +} diff --git a/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/pkg.json b/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/pkg.json new file mode 100644 index 000000000..801f8a1cf --- /dev/null +++ b/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/pkg.json @@ -0,0 +1,10 @@ +{ + "pkg_name": "centreon-plugin-Applications-Oracle-Ucp-Jmx", + "pkg_summary": "Centreon Plugin", + "plugin_name": "centreon_oracle_ucp_jmx.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "centreon/common/protocols/jmx/", + "apps/oracle/ucp/jmx/" + ] +} diff --git a/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/rpm.json b/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/rpm.json new file mode 100644 index 000000000..5d2a81f7a --- /dev/null +++ b/packaging/centreon-plugin-Applications-Oracle-Ucp-Jmx/rpm.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "perl(JMX::Jmx4Perl)" + ] +} \ No newline at end of file diff --git a/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/deb.json b/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/deb.json new file mode 100644 index 000000000..663aaaceb --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/deb.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "libsnmp-perl" + ] +} \ No newline at end of file diff --git a/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/pkg.json b/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/pkg.json new file mode 100644 index 000000000..27157e209 --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/pkg.json @@ -0,0 +1,10 @@ +{ + "pkg_name": "centreon-plugin-Hardware-Storage-Wd-Nas-Snmp", + "pkg_summary": "Centreon Plugin", + "plugin_name": "centreon_wd_nas_snmp.pl", + "files": [ + "centreon/plugins/script_snmp.pm", + "centreon/plugins/snmp.pm", + "storage/wd/nas/snmp/" + ] +} diff --git a/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/rpm.json b/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/rpm.json new file mode 100644 index 000000000..418a331fc --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-Wd-Nas-Snmp/rpm.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "perl(SNMP)" + ] +} \ No newline at end of file diff --git a/packaging/centreon-plugin-Operatingsystems-Windows-Wsman/pkg.json b/packaging/centreon-plugin-Operatingsystems-Windows-Wsman/pkg.json index 4b8fff21d..b641a585b 100644 --- a/packaging/centreon-plugin-Operatingsystems-Windows-Wsman/pkg.json +++ b/packaging/centreon-plugin-Operatingsystems-Windows-Wsman/pkg.json @@ -7,6 +7,7 @@ "centreon/plugins/wsman.pm", "os/windows/wsman/", "centreon/common/powershell/functions.pm", - "centreon/common/powershell/windows/pendingreboot.pm" + "centreon/common/powershell/windows/pendingreboot.pm", + "centreon/common/powershell/windows/updates.pm" ] } diff --git a/src/apps/backup/rubrik/restapi/custom/api.pm b/src/apps/backup/rubrik/restapi/custom/api.pm index f3e3dfea1..52d76eb79 100644 --- a/src/apps/backup/rubrik/restapi/custom/api.pm +++ b/src/apps/backup/rubrik/restapi/custom/api.pm @@ -45,6 +45,9 @@ sub new { $options{options}->add_options(arguments => { 'api-username:s' => { name => 'api_username' }, 'api-password:s' => { name => 'api_password' }, + 'service-account:s' => { name => 'service_account' }, + 'secret:s' => { name => 'secret' }, + 'organization-id:s' => { name => 'organization_id' }, 'hostname:s' => { name => 'hostname' }, 'port:s' => { name => 'port' }, 'proto:s' => { name => 'proto' }, @@ -80,6 +83,9 @@ sub check_options { $self->{option_results}->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 30; $self->{api_username} = (defined($self->{option_results}->{api_username})) ? $self->{option_results}->{api_username} : ''; $self->{api_password} = (defined($self->{option_results}->{api_password})) ? $self->{option_results}->{api_password} : ''; + $self->{service_account} = (defined($self->{option_results}->{service_account})) ? $self->{option_results}->{service_account} : ''; + $self->{secret} = (defined($self->{option_results}->{secret})) ? $self->{option_results}->{secret} : ''; + $self->{organization_id} = (defined($self->{option_results}->{organization_id})) ? $self->{option_results}->{organization_id} : ''; $self->{unknown_http_status} = (defined($self->{option_results}->{unknown_http_status})) ? $self->{option_results}->{unknown_http_status} : '%{http_code} < 200 or %{http_code} >= 300'; $self->{warning_http_status} = (defined($self->{option_results}->{warning_http_status})) ? $self->{option_results}->{warning_http_status} : ''; $self->{critical_http_status} = (defined($self->{option_results}->{critical_http_status})) ? $self->{option_results}->{critical_http_status} : ''; @@ -94,6 +100,15 @@ sub check_options { return 0 if ($self->{token} ne ''); } + if ($self->{service_account} ne '') { + if ($self->{secret} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify --secret option.'); + $self->{output}->option_exit(); + } + $self->{cache}->check_options(option_results => $self->{option_results}); + return 0; + } + if ($self->{api_username} eq '') { $self->{output}->add_option_msg(short_msg => 'Need to specify --api-username option.'); $self->{output}->option_exit(); @@ -141,7 +156,68 @@ sub get_token { credentials => 1, basic => 1, username => $self->{api_username}, - password => $self->{api_password} + password => $self->{api_password}, + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + + my $decoded; + eval { + $decoded = JSON::XS->new->utf8->decode($content); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); + $self->{output}->option_exit(); + } + + $token = $decoded->{token}; + my $datas = { + updated => time(), + token => $decoded->{token}, + md5_secret => $md5_secret + }; + $self->{cache}->write(data => $datas); + } + + return $token; +} + +sub get_service_account_token { + my ($self, %options) = @_; + + my $has_cache_file = $self->{cache}->read(statefile => 'rubrik_api_' . md5_hex($self->{option_results}->{hostname} . '_' . $self->{service_account})); + my $token = $self->{cache}->get(name => 'token'); + my $md5_secret_cache = $self->{cache}->get(name => 'md5_secret'); + my $md5_secret = md5_hex($self->{service_account} . $self->{secret}); + + if ($has_cache_file == 0 || + !defined($token) || + (defined($md5_secret_cache) && $md5_secret_cache ne $md5_secret) + ) { + my $json_request = { + serviceAccountId => $self->{service_account}, + secret => $self->{secret} + }; + $json_request->{organizationId} = $self->{organization_id} if ($self->{organization_id} ne ''); + + my $encoded; + eval { + $encoded = encode_json($json_request); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => 'cannot encode json request'); + $self->{output}->option_exit(); + } + + $self->settings(); + my $content = $self->{http}->request( + method => 'POST', + url_path => '/api/v1/service_account/session', + query_form_post => $encoded, + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} ); my $decoded; @@ -181,7 +257,15 @@ sub credentials { } my $creds = {}; - if (defined($self->{token})) { + if ($self->{service_account} ne '') { + $token = $self->get_service_account_token(); + $creds = { + header => ['Authorization: Bearer ' . $token], + unknown_status => '', + warning_status => '', + critical_status => '' + }; + } elsif (defined($self->{token})) { $creds = { header => ['Authorization: Bearer ' . $token], unknown_status => '', @@ -273,6 +357,18 @@ Port used (Default: 443) Specify https if needed (Default: 'https') +=item B<--service-account> + +Service account ID (with --secret and --organization-id options). + +=item B<--secret> + +Service account secret (with --service-account and --organization-id options). + +=item B<--organization-id> + +Organization ID (with --service-account and --secret options). + =item B<--api-username> API username. diff --git a/src/apps/ceph/restapi/mode/storage.pm b/src/apps/ceph/restapi/mode/storage.pm new file mode 100644 index 000000000..83a946def --- /dev/null +++ b/src/apps/ceph/restapi/mode/storage.pm @@ -0,0 +1,118 @@ +# +# Copyright 2023 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::ceph::restapi::mode::storage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total_space}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used_space}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free_space}); + return sprintf( + 'raw storage space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)', + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used_space}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free_space} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0, cb_prefix_output => 'prefix_global_output' } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'space-usage', nlabel => 'storage.raw.space.usage.bytes', set => { + key_values => [ { name => 'used_space' }, { name => 'free_space' }, { name => 'prct_used_space' }, { name => 'prct_free_space' }, { name => 'total_space' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total_space', unit => 'B', cast_int => 1, label_extra_instance => 1 } + ] + } + }, + { label => 'space-usage-free', nlabel => 'storage.raw.space.free.bytes', display_ok => 0, set => { + key_values => [ { name => 'free_space' }, { name => 'used_space' }, { name => 'prct_used_space' }, { name => 'prct_free_space' }, { name => 'total_space' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total_space', unit => 'B', cast_int => 1, label_extra_instance => 1 } + ] + } + }, + { label => 'space-usage-prct', nlabel => 'storage.raw.space.usage.percentage', display_ok => 0, set => { + key_values => [ { name => 'prct_used_space' }, { name => 'used_space' }, { name => 'free_space' }, { name => 'prct_free_space' }, { name => 'total_space' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $health = $options{custom}->request_api(endpoint => '/api/health/minimal'); + + $self->{global} = { + total_space => $health->{df}->{stats}->{total_bytes}, + used_space => $health->{df}->{stats}->{total_used_raw_bytes}, + free_space => $health->{df}->{stats}->{total_bytes} - $health->{df}->{stats}->{total_used_raw_bytes}, + prct_used_space => $health->{df}->{stats}->{total_used_raw_bytes} * 100 / $health->{df}->{stats}->{total_bytes}, + prct_free_space => ($health->{df}->{stats}->{total_bytes} - $health->{df}->{stats}->{total_used_raw_bytes}) * 100 / $health->{df}->{stats}->{total_bytes} + }; +} + +1; + +__END__ + +=head1 MODE + +Check raw storage. + +=over 8 + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'space-usage', 'space-usage-free', 'space-usage-prct'. + +=back + +=cut diff --git a/src/apps/ceph/restapi/plugin.pm b/src/apps/ceph/restapi/plugin.pm index 9ded2b046..61545fa03 100644 --- a/src/apps/ceph/restapi/plugin.pm +++ b/src/apps/ceph/restapi/plugin.pm @@ -33,7 +33,8 @@ sub new { 'health' => 'apps::ceph::restapi::mode::health', 'list-pools' => 'apps::ceph::restapi::mode::listpools', 'osd' => 'apps::ceph::restapi::mode::osd', - 'pools' => 'apps::ceph::restapi::mode::pools' + 'pools' => 'apps::ceph::restapi::mode::pools', + 'storage' => 'apps::ceph::restapi::mode::storage' }; $self->{custom_modes}->{api} = 'apps::ceph::restapi::custom::api'; diff --git a/src/apps/jenkins/custom/api.pm b/src/apps/jenkins/custom/api.pm new file mode 100644 index 000000000..af6e3f4a6 --- /dev/null +++ b/src/apps/jenkins/custom/api.pm @@ -0,0 +1,204 @@ +# +# Copyright 2023 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::jenkins::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 => { + 'username:s' => { name => 'username' }, + 'password:s' => { name => 'password' }, + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port' }, + 'proto:s' => { name => 'proto' }, + 'timeout:s' => { name => 'timeout' }, + 'unknown-http-status:s' => { name => 'unknown_http_status' }, + 'warning-http-status:s' => { name => 'warning_http_status' }, + 'critical-http-status:s' => { name => 'critical_http_status' } + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl'); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults {} + +sub check_options { + my ($self, %options) = @_; + + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : ''; + $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->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10; + $self->{username} = (defined($self->{option_results}->{username})) ? $self->{option_results}->{username} : ''; + $self->{password} = (defined($self->{option_results}->{password})) ? $self->{option_results}->{password} : ''; + $self->{unknown_http_status} = (defined($self->{option_results}->{unknown_http_status})) ? $self->{option_results}->{unknown_http_status} : '%{http_code} < 200 or %{http_code} >= 300'; + $self->{warning_http_status} = (defined($self->{option_results}->{warning_http_status})) ? $self->{option_results}->{warning_http_status} : ''; + $self->{critical_http_status} = (defined($self->{option_results}->{critical_http_status})) ? $self->{option_results}->{critical_http_status} : ''; + + if ($self->{hostname} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --hostname option."); + $self->{output}->option_exit(); + } + if ($self->{username} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --username option."); + $self->{output}->option_exit(); + } + if ($self->{password} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --password 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}->{timeout} = $self->{timeout}; + $self->{option_results}->{credentials} = 1; + $self->{option_results}->{basic} = 1; + $self->{option_results}->{username} = $self->{username}; + $self->{option_results}->{password} = $self->{password}; +} + +sub settings { + my ($self, %options) = @_; + + return if (defined($self->{settings_done})); + $self->build_options_for_httplib(); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->add_header(key => 'Content-Type', value => 'application/json'); + $self->{http}->set_options(%{$self->{option_results}}); + $self->{settings_done} = 1; +} + +sub get_hostname { + my ($self, %options) = @_; + + return $self->{hostname}; +} + +sub request_api { + my ($self, %options) = @_; + + $self->settings(); + my ($content) = $self->{http}->request( + url_path => $options{endpoint}, + get_param => $options{get_param}, + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + + if (!defined($content) || $content eq '') { + $self->{output}->add_option_msg(short_msg => "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']"); + $self->{output}->option_exit(); + } + + my $decoded; + eval { + $decoded = JSON::XS->new->utf8->decode($content); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode response (add --debug option to display returned content)"); + $self->{output}->option_exit(); + } + + return $decoded; +} + +1; + +__END__ + +=head1 NAME + +Jenkins Rest API + +=head1 REST API OPTIONS + +Jenkins Rest API + +=over 8 + +=item B<--hostname> + +Hostname. + +=item B<--port> + +Port used (Default: 443) + +=item B<--proto> + +Specify https if needed (Default: 'https') + +=item B<--username> + +API username. + +=item B<--password> + +API password. + +=item B<--timeout> + +Set timeout in seconds (Default: 10). + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/src/apps/jenkins/mode/jobs.pm b/src/apps/jenkins/mode/jobs.pm new file mode 100644 index 000000000..86a6d126c --- /dev/null +++ b/src/apps/jenkins/mode/jobs.pm @@ -0,0 +1,134 @@ +# +# Copyright 2023 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::jenkins::mode::jobs; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub prefix_job_output { + my ($self, %options) = @_; + + return "Job '" . $options{instance} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'jobs', type => 1, cb_prefix_output => 'prefix_job_output', message_multiple => 'All jobs are ok' } + ]; + + $self->{maps_counters}->{jobs} = [ + { label => 'score', nlabel => 'job.score.percentage', set => { + key_values => [ { name => 'score' } ], + output_template => 'score: %.2f %%', + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 } + ] + } + }, + { label => 'violations', nlabel => 'job.violations.count', set => { + key_values => [ { name => 'violations' } ], + output_template => 'violations: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-job-name:s' => { name => 'filter_job_name' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $jobs = $options{custom}->request_api( + endpoint => '/api/json', + get_param => [ + 'tree=jobs[name,buildable,healthReport[description,score],jobs[name,buildable,healthReport[description,score]]]' + ] + ); + + $self->{jobs} = {}; + foreach my $job (@{$jobs->{jobs}}) { + my $name = $job->{name}; + next if (defined($self->{option_results}->{filter_job_name}) && $self->{option_results}->{filter_job_name} ne '' && + $name !~ /$self->{option_results}->{filter_job_name}/); + + if (defined($job->{healthReport}->[0]->{score})) { + $self->{jobs}->{$name} = { score => $job->{healthReport}->[0]->{score}, violations => 0 }; + if ($job->{healthReport}->[0]->{description} =~ /^.+?([0-9]+)/ ) { + $self->{jobs}->{$name}->{violations} = $1; + } + } + + next if (!defined($job->{jobs})); + + foreach my $subjob (@{$job->{jobs}}) { + my $subname = $name . '/' . $subjob->{name}; + next if (defined($self->{option_results}->{filter_job_name}) && $self->{option_results}->{filter_job_name} ne '' && + $subname !~ /$self->{option_results}->{filter_job_name}/); + + if (defined($subjob->{healthReport}->[0]->{score})) { + $self->{jobs}->{$subname} = { score => $subjob->{healthReport}->[0]->{score}, violations => 0 }; + if ($subjob->{healthReport}->[0]->{description} =~ /^.+?([0-9]+)/ ) { + $self->{jobs}->{$subname}->{violations} = $1; + } + } + } + } +} + +1; + +__END__ + +=head1 MODE + +Check jobs. + +=over 8 + +=item B<--filter-job-name> + +Filter jobs by name (can be a regexp). + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'score', 'violations'. + +=back + +=cut diff --git a/src/apps/jenkins/mode/jobstate.pm b/src/apps/jenkins/mode/jobstate.pm deleted file mode 100644 index 9508a477f..000000000 --- a/src/apps/jenkins/mode/jobstate.pm +++ /dev/null @@ -1,197 +0,0 @@ -# -# Copyright 2023 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::jenkins::mode::jobstate; - -use base qw(centreon::plugins::mode); - -use strict; -use warnings; -use centreon::plugins::http; -use centreon::plugins::statefile; -use JSON; - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options); - bless $self, $class; - - $options{options}->add_options(arguments => { - "hostname:s" => { name => 'hostname' }, - "port:s" => { name => 'port' }, - "proto:s" => { name => 'proto' }, - "urlpath:s" => { name => 'url_path' }, - "timeout:s" => { name => 'timeout' }, - "credentials" => { name => 'credentials' }, - "basic" => { name => 'basic' }, - "username:s" => { name => 'username' }, - "password:s" => { name => 'password' }, - "jobname:s" => { name => 'jobname' }, - "warning:s" => { name => 'warning' }, - "critical:s" => { name => 'critical' }, - "checkstyle" => { name => 'checkstyle' }, - }); - - $self->{http} = centreon::plugins::http->new(%options); - 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(); - } - if (!defined($self->{option_results}->{jobname})) { - $self->{output}->add_option_msg(short_msg => "Please set the jobname option"); - $self->{output}->option_exit(); - } - - $self->{option_results}->{url_path} = $self->{option_results}->{url_path} . "/job/" . $self->{option_results}->{jobname} . "/api/json"; - $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 $description_tendency = $webcontent->{healthReport}->[0]->{description}; - my $score_tendency = $webcontent->{healthReport}->[0]->{score}; - - my ($description_violations, $number_violations); - - my $exit1 = $self->{perfdata}->threshold_check(value => $score_tendency, threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]); - - - $self->{output}->output_add(severity => $exit1, - short_msg => sprintf("%s", $description_tendency)); - $self->{output}->perfdata_add(label => 'score_tendency', - value => sprintf("%d", $score_tendency), - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'), - min => 0, - ); - - if (defined($self->{option_results}->{checkstyle})) { - if (defined($webcontent->{healthReport}->[1]->{description})) { - my $description_violations; - my $number_violations; - $description_violations = $webcontent->{healthReport}->[1]->{description}; - if ( $description_violations =~ /^.+?([0-9]+)$/ ) { - $number_violations = $1; - } - - $self->{output}->add_option_msg(short_msg => sprintf("%s", $description_violations)); - $self->{output}->perfdata_add(label => 'number_violations', - value => sprintf("%d", $number_violations), - min => 0, - ); - } - } - - $self->{output}->display(); - $self->{output}->exit(); - -} - -1; - -__END__ - -=head1 MODE - -Check Jenkins specific job tendency (score) and checkstyle violation - -=over 8 - -=item B<--hostname> - -IP Addr/FQDN of the Jenkins host - -=item B<--port> - -Port used by Jenkins API - -=item B<--proto> - -Specify https if needed (Default: 'http') - -=item B<--urlpath> - -Set path to get Jenkins information - -=item B<--credentials> - -Required to use username/password authentication method - -=item B<--basic> - -Specify this option if you access API over basic authentication and don't want a '401 UNAUTHORIZED' error to be logged on your webserver. - -Specify this option if you access API over hidden basic authentication or you'll get a '404 NOT FOUND' error. - -(Use with --credentials) - -=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<--warning> - -Warning Threshold for tendency score - -=item B<--critical> - -Critical Threshold for tendency score - -=item B<--checkstyle> - -Add checkstyle's violation output and perfdata - -=back - -=cut diff --git a/src/apps/jenkins/mode/listjobs.pm b/src/apps/jenkins/mode/listjobs.pm new file mode 100644 index 000000000..1d343fb45 --- /dev/null +++ b/src/apps/jenkins/mode/listjobs.pm @@ -0,0 +1,116 @@ +# +# Copyright 2023 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::jenkins::mode::listjobs; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $jobs = $options{custom}->request_api( + endpoint => '/api/json', + get_param => [ + 'tree=jobs[name,buildable,jobs[name,buildable]]' + ] + ); + + my $results = []; + foreach my $job (@{$jobs->{jobs}}) { + push @$results, { name => $job->{name} }; + + foreach my $subjob (@{$job->{jobs}}) { + push @$results, { name => $job->{name} . '/' . $subjob->{name} }; + } + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $jobs = $self->manage_selection(%options); + foreach (@$jobs) { + $self->{output}->output_add( + long_msg => sprintf( + '[name: %s]', + $_->{name} + ) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List jobs:' + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $jobs = $self->manage_selection(%options); + foreach (@$jobs) { + $self->{output}->add_disco_entry( + %$_ + ); + } +} + +1; + +__END__ + +=head1 MODE + +List jobs. + +=over 8 + +=back + +=cut diff --git a/src/apps/jenkins/plugin.pm b/src/apps/jenkins/plugin.pm index 30397800c..c4a978ec5 100644 --- a/src/apps/jenkins/plugin.pm +++ b/src/apps/jenkins/plugin.pm @@ -22,18 +22,19 @@ package apps::jenkins::plugin; use strict; use warnings; -use base qw(centreon::plugins::script_simple); +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}} = ( - 'job-state' => 'apps::jenkins::mode::jobstate', - ); + $self->{modes} = { + 'jobs' => 'apps::jenkins::mode::jobs', + 'list-jobs' => 'apps::jenkins::mode::listjobs' + }; + + $self->{custom_modes}->{api} = 'apps::jenkins::custom::api'; return $self; } diff --git a/src/apps/oracle/ucp/jmx/mode/connectionpools.pm b/src/apps/oracle/ucp/jmx/mode/connectionpools.pm new file mode 100644 index 000000000..83b82b5a8 --- /dev/null +++ b/src/apps/oracle/ucp/jmx/mode/connectionpools.pm @@ -0,0 +1,258 @@ +# +# Copyright 2023 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::oracle::ucp::jmx::mode::connectionpools; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub custom_usage_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => $self->{result_values}->{poolName}, + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => $self->{result_values}->{total} > 0 ? $self->{result_values}->{total} : undef + ); +} + +sub custom_usage_free_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => $self->{result_values}->{poolName}, + value => $self->{result_values}->{free}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => $self->{result_values}->{total} + ); +} + +sub custom_usage_prct_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => $self->{result_values}->{poolName}, + value => sprintf('%.2f', $self->{result_values}->{prct_used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, max => 100 + ); +} + +sub custom_usage_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => $self->{result_values}->{used}, threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' }, + ] + ); +} + +sub custom_usage_free_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => $self->{result_values}->{free}, threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' } + ] + ); +} + +sub custom_usage_prct_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => $self->{result_values}->{prct_used}, threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' } + ] + ); +} + +sub custom_usage_output { + my ($self, %options) = @_; + + my $msg; + if ($self->{result_values}->{total} <= 0) { + $msg = sprintf("used: %s (unlimited)", $self->{result_values}->{used}); + } else { + $msg = sprintf( + "total: %s used: %s (%.2f%%) free: %s (%.2f%%)", + $self->{result_values}->{total}, + $self->{result_values}->{used}, $self->{result_values}->{prct_used}, + $self->{result_values}->{free}, $self->{result_values}->{prct_free} + ); + } + + return $msg; +} + +sub custom_usage_calc { + my ($self, %options) = @_; + + $self->{result_values}->{poolName} = $options{new_datas}->{$self->{instance} . '_poolName'}; + $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_total'}; + $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_used'}; + + if ($self->{result_values}->{total} == 0) { + return -10 if ($options{extra_options}->{label} ne 'usage'); + return 0; + } + + $self->{result_values}->{prct_used} = $self->{result_values}->{used} * 100 / $self->{result_values}->{total}; + $self->{result_values}->{prct_free} = 100 - $self->{result_values}->{prct_used}; + $self->{result_values}->{free} = $self->{result_values}->{total} - $self->{result_values}->{used}; + + return 0; +} + +sub prefix_pool_output { + my ($self, %options) = @_; + + return sprintf( + "Connection pool '%s' ", + $options{instance_value}->{poolName} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'pools', type => 1, cb_prefix_output => 'prefix_pool_output', message_multiple => 'All connection pools are ok', skipped_code => { -10 => 1 } } + ]; + + $self->{maps_counters}->{pools} = [ + { label => 'usage', nlabel => 'connection_pool.usage.count', set => { + key_values => [ + { name => 'poolName' }, { name => 'used' }, { name => 'total' } + ], + closure_custom_calc_extra_options => { label => 'usage' }, + closure_custom_calc => $self->can('custom_usage_calc'), + closure_custom_output => $self->can('custom_usage_output'), + closure_custom_threshold_check => $self->can('custom_usage_threshold'), + closure_custom_perfdata => $self->can('custom_usage_perfdata') + } + }, + { label => 'usage-free', nlabel => 'connection_pool.free.count', display_ok => 0, set => { + key_values => [ + { name => 'poolName' }, { name => 'used' }, { name => 'total' } + ], + closure_custom_calc_extra_options => { label => 'free' }, + closure_custom_calc => $self->can('custom_usage_calc'), + closure_custom_output => $self->can('custom_usage_output'), + closure_custom_threshold_check => $self->can('custom_usage_free_threshold'), + closure_custom_perfdata => $self->can('custom_usage_free_perfdata') + } + }, + { label => 'usage-prct', nlabel => 'connection_pool.usage.percentage', display_ok => 0, set => { + key_values => [ + { name => 'poolName' }, { name => 'used' }, { name => 'total' } + ], + closure_custom_calc_extra_options => { label => 'prct' }, + closure_custom_calc => $self->can('custom_usage_calc'), + closure_custom_output => $self->can('custom_usage_output'), + closure_custom_threshold_check => $self->can('custom_usage_prct_threshold'), + closure_custom_perfdata => $self->can('custom_usage_prct_perfdata') + } + } + ]; +} + + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-pool-name:s' => { name => 'filter_pool_name' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $request = [ + { mbean => "oracle.ucp.admin.UniversalConnectionPoolMBean:name=UniversalConnectionPoolManager*,poolName=*", attributes => + [ { name => 'remainingPoolCapacityCount' }, { name => 'totalConnectionsCount' }, { name => 'maxPoolSize' } ] } + ]; + + my $result = $options{custom}->get_attributes(request => $request, nothing_quit => 1); + + $self->{pools} = {}; + foreach my $key (keys %$result) { + $key =~ /(?:[:,])poolName=(.*?)(?:,|$)/; + my $poolName = $1; + + next if (defined($self->{option_results}->{filter_pool_name}) && $self->{option_results}->{filter_pool_name} ne '' && + $poolName !~ /$self->{option_results}->{filter_pool_name}/); + + $self->{pools}->{$poolName} = { + poolName => $poolName, + used => $result->{$key}->{totalConnectionsCount}, + total => defined($result->{$key}->{maxPoolSize}) ? $result->{$key}->{maxPoolSize} : 0 + }; + } + + if (scalar(keys %{$self->{pools}}) <= 0) { + $self->{output}->add_option_msg(short_msg => 'No connection pools found'); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check connection pools. + +=over 8 + +=item B<--filter-pool-name> + +Filter connection pools by name (can be a regexp). + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'usage', 'usage-free', 'usage-prct'. + +=back + +=cut diff --git a/src/apps/oracle/ucp/jmx/mode/listconnectionpools.pm b/src/apps/oracle/ucp/jmx/mode/listconnectionpools.pm new file mode 100644 index 000000000..0f68a81d8 --- /dev/null +++ b/src/apps/oracle/ucp/jmx/mode/listconnectionpools.pm @@ -0,0 +1,121 @@ +# +# Copyright 2023 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::oracle::ucp::jmx::mode::listconnectionpools; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $mbeans = $options{custom}->get_attributes( + request => [ + { + mbean => "oracle.ucp.admin.UniversalConnectionPoolMBean:name=UniversalConnectionPoolManager*,poolName=*", + attributes => [ { name => 'totalConnectionsCount' } ] + } + ] + ); + + my $results = {}; + foreach my $mbean (keys %$mbeans) { + $mbean =~ /(?:[:,])poolName=(.*?)(?:,|$)/; + my $poolName = $1; + + $mbean =~ /(?:[:,])name=(.*?)(?:,|$)/; + my $name = $1; + + $results->{$name} = { + name => $name, + poolName => $poolName + }; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach my $instance (sort keys %$results) { + $self->{output}->output_add( + long_msg => + '[name = ' . $results->{$instance}->{name} . "]" . + " [poolName = '" . $results->{$instance}->{poolName} . "']" + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List connection pools:' + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'poolName']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach my $instance (sort keys %$results) { + $self->{output}->add_disco_entry( + %{$results->{$instance}} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List connection pools. + +=over 8 + +=back + +=cut diff --git a/src/apps/oracle/ucp/jmx/plugin.pm b/src/apps/oracle/ucp/jmx/plugin.pm new file mode 100644 index 000000000..8d504671b --- /dev/null +++ b/src/apps/oracle/ucp/jmx/plugin.pm @@ -0,0 +1,49 @@ +# +# Copyright 2023 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::oracle::ucp::jmx::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->{modes} = { + 'connection-pools' => 'apps::oracle::ucp::jmx::mode::connectionpools', + 'list-connection-pools' => 'apps::oracle::ucp::jmx::mode::listconnectionpools' + }; + + $self->{custom_modes}->{jolokia} = 'centreon::common::protocols::jmx::custom::jolokia'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Oracle Universal Connection Pool in JMX. Need Jolokia agent. + +=cut diff --git a/src/apps/pfsense/snmp/mode/runtime.pm b/src/apps/pfsense/snmp/mode/runtime.pm index a04f2d9d0..8075b9244 100644 --- a/src/apps/pfsense/snmp/mode/runtime.pm +++ b/src/apps/pfsense/snmp/mode/runtime.pm @@ -67,26 +67,36 @@ sub run { $valueRuntime = $result->{$oid_pfsenseRuntime}; if ($valueStatus == 1) { - my $exit_code = $self->{perfdata}->threshold_check(value => $valueRuntime, - threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]); - $self->{output}->perfdata_add(label => 'runtime', unit => 's', - value => floor($valueRuntime / 100), - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'), - min => 0); - $self->{output}->output_add(severity => $exit_code, - short_msg => sprintf("PfSense running since : %s", - centreon::plugins::misc::change_seconds(value => floor($valueRuntime / 100)))); - + my $exit_code = $self->{perfdata}->threshold_check( + value => $valueRuntime, + threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ] + ); + $self->{output}->perfdata_add( + label => 'runtime', unit => 's', + value => floor($valueRuntime / 100), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'), + min => 0 + ); + $self->{output}->output_add( + severity => $exit_code, + short_msg => sprintf( + "PfSense running since : %s", + centreon::plugins::misc::change_seconds(value => floor($valueRuntime / 100)) + ) + ); } else { - $self->{output}->perfdata_add(label => 'runtime', unit => 's', - value => 0, - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'), - min => 0); - $self->{output}->output_add(severity => 'critical', - short_msg => 'PfSense not running'); - + $self->{output}->perfdata_add( + label => 'runtime', unit => 's', + value => 0, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'), + min => 0 + ); + $self->{output}->output_add( + severity => 'critical', + short_msg => 'PfSense not running' + ); } $self->{output}->display(); $self->{output}->exit(); diff --git a/src/apps/pfsense/snmp/mode/statetable.pm b/src/apps/pfsense/snmp/mode/statetable.pm new file mode 100644 index 000000000..1c4dd4c8d --- /dev/null +++ b/src/apps/pfsense/snmp/mode/statetable.pm @@ -0,0 +1,132 @@ +# +# Copyright 2023 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::pfsense::snmp::mode::statetable; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use Digest::MD5 qw(md5_hex); + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of state table '; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0, cb_prefix_output => 'prefix_global_output' } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'entries', nlabel => 'state_table.entries.count', set => { + key_values => [ { name => 'count' } ], + output_template => 'entries: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + }, + { label => 'searches', nlabel => 'state_table.search.count', set => { + key_values => [ { name => 'searches', diff => 1 } ], + output_template => 'searches: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + }, + { label => 'entries-inserted', nlabel => 'state_table.entries.inserted.count', set => { + key_values => [ { name => 'inserted', diff => 1 } ], + output_template => 'entries inserted: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + }, + { label => 'entries-removed', nlabel => 'state_table.entries.removed.count', set => { + key_values => [ { name => 'removed', diff => 1 } ], + output_template => 'entries removed: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + if ($options{snmp}->is_snmpv1()) { + $self->{output}->add_option_msg(short_msg => "Can't check SNMP 64 bits counters with SNMPv1."); + $self->{output}->option_exit(); + } + my $mapping = { + count => { oid => '.1.3.6.1.4.1.12325.1.200.1.3.1' }, # pfStateTableCount + searches => { oid => '.1.3.6.1.4.1.12325.1.200.1.3.2' }, # pfStateTableSearches + inserted => { oid => '.1.3.6.1.4.1.12325.1.200.1.3.3' }, # pfStateTableInserts + removed => { oid => '.1.3.6.1.4.1.12325.1.200.1.3.4' } # pfStateTableRemovals + }; + my $snmp_result = $options{snmp}->get_leef(oids => [ map($_->{oid} . '.0', values(%$mapping)) ], nothing_quit => 1); + + $self->{global} = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => 0); + + $self->{cache_name} = 'pfsense_' . $self->{mode} . '_' . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' . + (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all')); +} + +1; + +__END__ + +=head1 MODE + +Check state table. + +=over 8 + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='count' + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'entries', 'searches', 'entries-inserted', 'entries-removed'. + +=back + +=cut diff --git a/src/apps/pfsense/snmp/plugin.pm b/src/apps/pfsense/snmp/plugin.pm index b91fadf97..dea92d0eb 100644 --- a/src/apps/pfsense/snmp/plugin.pm +++ b/src/apps/pfsense/snmp/plugin.pm @@ -29,12 +29,12 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options); bless $self, $class; - $self->{version} = '0.1'; $self->{modes} = { 'list-pfinterfaces' => 'apps::pfsense::snmp::mode::listpfinterfaces', 'packet-stats' => 'apps::pfsense::snmp::mode::packetstats', 'pfinterfaces' => 'apps::pfsense::snmp::mode::pfinterfaces', - 'runtime' => 'apps::pfsense::snmp::mode::runtime' + 'runtime' => 'apps::pfsense::snmp::mode::runtime', + 'state-table' => 'apps::pfsense::snmp::mode::statetable' }; return $self; diff --git a/src/apps/protocols/cifs/mode/files.pm b/src/apps/protocols/cifs/mode/files.pm index b7eb80104..e563e24b3 100644 --- a/src/apps/protocols/cifs/mode/files.pm +++ b/src/apps/protocols/cifs/mode/files.pm @@ -118,11 +118,12 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - 'directory:s@' => { name => 'directory' }, - 'file:s@' => { name => 'file' }, - 'max-depth:s' => { name => 'max_depth', default => 0 }, - 'timezone:s' => { name => 'timezone' }, - 'unit:s' => { name => 'unit', default => 's' } + 'filter-file:s' => { name => 'filter_file' } + 'directory:s@' => { name => 'directory' }, + 'file:s@' => { name => 'file' }, + 'max-depth:s' => { name => 'max_depth', default => 0 }, + 'timezone:s' => { name => 'timezone' }, + 'unit:s' => { name => 'unit', default => 's' } }); return $self; @@ -190,6 +191,9 @@ sub check_directory { my $name = $dir . '/' . $file->[1]; + next if (defined($self->{option_results}->{filter_file}) && $self->{option_results}->{filter_file} ne '' && + $name !~ /$self->{option_results}->{filter_file}/); + if ($file->[0] == SMBC_DIR) { if (defined($self->{option_results}->{max_depth}) && $level + 1 <= $self->{option_results}->{max_depth}) { push @$list, { name => $name, level => $level + 1 }; @@ -263,7 +267,7 @@ Check files. =item B<--directory> -Check directory (Multiple option) +Check directory (Multiple option). =item B<--max-depth> @@ -271,7 +275,11 @@ Don't check fewer levels (Default: '0'. Means current dir only). Used for direct =item B<--file> -Check file (Multiple option) +Check file (Multiple option). + +=item B<--filter-file> + +Filter files (can be a regexp. Directory in the name). =item B<--timezone> diff --git a/src/apps/protocols/cifs/mode/filescount.pm b/src/apps/protocols/cifs/mode/filescount.pm index 19ec631f5..2ed1b0e43 100644 --- a/src/apps/protocols/cifs/mode/filescount.pm +++ b/src/apps/protocols/cifs/mode/filescount.pm @@ -144,7 +144,7 @@ Count files in a directory (can be recursive). =item B<--directory> -Check files in the directory (Multiple option) +Check files in the directory (Multiple option). =item B<--max-depth> diff --git a/src/apps/protocols/cifs/mode/filesdate.pm b/src/apps/protocols/cifs/mode/filesdate.pm index 4c032fea5..e762407ea 100644 --- a/src/apps/protocols/cifs/mode/filesdate.pm +++ b/src/apps/protocols/cifs/mode/filesdate.pm @@ -89,10 +89,11 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - 'directory:s@' => { name => 'directory' }, - 'file:s@' => { name => 'file' }, - 'timezone:s' => { name => 'timezone' }, - 'unit:s' => { name => 'unit', default => 's' } + 'filter-file:s' => { name => 'filter_file' }, + 'directory:s@' => { name => 'directory' }, + 'file:s@' => { name => 'file' }, + 'timezone:s' => { name => 'timezone' }, + 'unit:s' => { name => 'unit', default => 's' } }); return $self; @@ -155,6 +156,9 @@ sub manage_selection { my $name = $dir . '/' . $file->[1]; + next if (defined($self->{option_results}->{filter_file}) && $self->{option_results}->{filter_file} ne '' && + $name !~ /$self->{option_results}->{filter_file}/); + $rv = $options{custom}->stat_file(file => $name); if ($rv->{code} != 0) { $self->{output}->add_option_msg(short_msg => "cannot stat file '" . $name . "': " . $rv->{message}); @@ -206,6 +210,10 @@ Check files in the directory (no recursive) (Multiple option) Check file (Multiple option) +=item B<--filter-file> + +Filter files (can be a regexp. Directory in the name). + =item B<--timezone> Set the timezone of display date. diff --git a/src/apps/protocols/cifs/mode/filessize.pm b/src/apps/protocols/cifs/mode/filessize.pm index 88ff8bcdd..a0f73e0ea 100644 --- a/src/apps/protocols/cifs/mode/filessize.pm +++ b/src/apps/protocols/cifs/mode/filessize.pm @@ -58,9 +58,10 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - 'directory:s@' => { name => 'directory' }, - 'file:s@' => { name => 'file' }, - 'max-depth:s' => { name => 'max_depth', default => 0 } + 'filter-file:s' => { name => 'filter_file' }, + 'directory:s@' => { name => 'directory' }, + 'file:s@' => { name => 'file' }, + 'max-depth:s' => { name => 'max_depth', default => 0 } }); return $self; @@ -120,6 +121,9 @@ sub check_directory { my $name = $dir . '/' . $file->[1]; + next if (defined($self->{option_results}->{filter_file}) && $self->{option_results}->{filter_file} ne '' && + $name !~ /$self->{option_results}->{filter_file}/); + if ($file->[0] == SMBC_DIR) { if (defined($self->{option_results}->{max_depth}) && $level + 1 <= $self->{option_results}->{max_depth}) { push @$list, { name => $name, level => $level + 1 }; @@ -175,6 +179,10 @@ Can get sub directory size with --max-depth option. Check file (Multiple option) +=item B<--filter-file> + +Filter files (can be a regexp. Directory in the name). + =item B<--max-depth> Don't check fewer levels (Default: '0'. Means current dir only). diff --git a/src/apps/protocols/nrpe/mode/query.pm b/src/apps/protocols/nrpe/mode/query.pm index a6963ce3a..126fc3622 100644 --- a/src/apps/protocols/nrpe/mode/query.pm +++ b/src/apps/protocols/nrpe/mode/query.pm @@ -33,6 +33,7 @@ sub new { $options{options}->add_options(arguments => { 'command:s' => { name => 'command' }, 'arg:s@' => { name => 'arg' }, + 'extra-args' => { name => 'extra_args' }, 'sanitize-message:s' => { name => 'sanitize_message' } }); @@ -70,9 +71,12 @@ sub sanitize_message { sub run { my ($self, %options) = @_; + my @arg = defined($self->{option_results}->{arg}) ? @{$self->{option_results}->{arg}} : (); + push @arg, @{$self->{option_extras}} if ($self->{option_results}->{extra_args}); + my $result = $options{custom}->request( command => $self->{option_results}->{command}, - arg => $self->{option_results}->{arg} + arg => \@arg ); $self->{output}->output_add( @@ -112,6 +116,13 @@ In nrpe use following command to get server version: --command='_NRPE_CHECK' Set arguments (Multiple option. Example: --arg='arg1' --arg='arg2'). +=item B<--extra-args> + +Use extra arguments from command line (all values placed after a double-dash '--') +as additional "--arg" options for the NRPE command. + +Example: --arg='arg1' --extra-args -- 'arg2' 'arg3' 'arg4' + =item B<--sanitize-message> Sanitize message by removing heading code and diff --git a/src/apps/protocols/x509/custom/opensslcli.pm b/src/apps/protocols/x509/custom/opensslcli.pm new file mode 100644 index 000000000..19d374cb4 --- /dev/null +++ b/src/apps/protocols/x509/custom/opensslcli.pm @@ -0,0 +1,207 @@ +# +# Copyright 2023 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::protocols::x509::custom::opensslcli; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use centreon::plugins::ssh; +use centreon::plugins::misc; +use Net::SSLeay 1.42; +use DateTime; +use Socket; + +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 => { + 'ssh-hostname:s' => { name => 'ssh_hostname' }, + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port' } + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'OPENSSL CLI OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{ssh} = centreon::plugins::ssh->new(%options); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults {} + +sub check_options { + my ($self, %options) = @_; + + $self->{sslhost} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : ''; + $self->{port} = (defined($self->{option_results}->{port})) && $self->{option_results}->{port} =~ /(\d+)/ ? $1 : ''; + $self->{ssh_hostname} = defined($self->{option_results}->{ssh_hostname}) && $self->{option_results}->{ssh_hostname} ne '' ? $self->{option_results}->{ssh_hostname} : ''; + + if ($self->{sslhost} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify --hostname option.'); + $self->{output}->option_exit(); + } + if ($self->{port} eq '') { + $self->{output}->add_option_msg(short_msg => "Please set --port option"); + $self->{output}->option_exit(); + } + + if ($self->{ssh_hostname} ne '') { + $self->{option_results}->{hostname} = $self->{ssh_hostname}; + $self->{ssh}->check_options(option_results => $self->{option_results}); + } + + return 0; +} + +sub pem_type { + my ($self, %options) = @_; + + my $bio_cert = Net::SSLeay::BIO_new(Net::SSLeay::BIO_s_mem()); + if (!$bio_cert) { + $self->{output}->add_option_msg(short_msg => "Cannot init Net::SSLeay: $!"); + $self->{output}->option_exit(); + } + if (Net::SSLeay::BIO_write($bio_cert, $options{cert}) < 0) { + Net::SSLeay::BIO_free($bio_cert); + $self->{output}->add_option_msg(short_msg => "Cannot write certificate: $!"); + $self->{output}->option_exit(); + } + my $x509 = Net::SSLeay::PEM_read_bio_X509($bio_cert); + Net::SSLeay::BIO_free($bio_cert); + if (!$x509) { + $self->{output}->add_option_msg(short_msg => "Cannot read certificate: $!"); + $self->{output}->option_exit(); + } + + my $cert_infos = {}; + $cert_infos->{issuer} = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_issuer_name($x509)); + $cert_infos->{expiration_date} = Net::SSLeay::P_ASN1_TIME_get_isotime(Net::SSLeay::X509_get_notAfter($x509)); + my $subject = Net::SSLeay::X509_NAME_oneline(Net::SSLeay::X509_get_subject_name($x509)); + if ($subject =~ /CN=(.*?)(?:\/(?:C|ST|L|O)=|\Z)/) { + $cert_infos->{subject} = $1; + } + + my @subject_alt_names = Net::SSLeay::X509_get_subjectAltNames($x509); + my $append = ''; + $cert_infos->{alt_subjects} = ''; + for (my $i = 0; $i < $#subject_alt_names; $i += 2) { + my ($type, $name) = ($subject_alt_names[$i], $subject_alt_names[$i + 1]); + if ($type == &Net::SSLeay::GEN_IPADD) { + $name = Socket::inet_ntop(length($name) > 4 ? Socket::AF_INET6 : Socket::AF_INET, $name); + } + $cert_infos->{alt_subjects} .= $append . $name; + $append = ', '; + } + + $cert_infos->{expiration_date} =~ /^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)Z$/; # 2033-05-16T20:39:37Z + my $dt = DateTime->new(year => $1, month => $2, day => $3, hour => $4, minute => $5, second => $6); + $cert_infos->{expiration} = $dt->epoch(); + + return $cert_infos; +} + +sub get_certificate_informations { + my ($self, %options) = @_; + + my $timeout = 30; + + my ($stdout, $exit_code); + if ($self->{ssh_hostname} ne '') { + ($stdout, $exit_code) = $self->{ssh}->execute( + hostname => $self->{ssh_hostname}, + command => 'openssl', + command_options => "s_client -connect '" . $self->{sslhost} . ':' . $self->{port} . "'", + timeout => $timeout, + no_quit => 1 + ); + } else { + ($stdout, $exit_code) = centreon::plugins::misc::execute( + output => $self->{output}, + sudo => $self->{option_results}->{sudo}, + options => { timeout => $timeout }, + command => 'openssl', + command_options => "s_client -connect '" . $self->{sslhost} . ':' . $self->{port} . "'", + no_quit => 1 + ); + } + + $self->{output}->output_add(long_msg => "command response: $stdout", debug => 1); + + if ($stdout !~ /^(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)/ms) { + $self->{output}->add_option_msg(short_msg => "cannot find the server certificate"); + $self->{output}->option_exit(); + } + + my $cert = $1; + my $cert_infos = $self->pem_type(cert => $cert); + + return $cert_infos; +} + +1; + +__END__ + +=head1 NAME + +openssl connections + +=head1 OPENSSL CLI OPTIONS + +openssl connection + +=over 8 + +=item B<--hostname> + +IP Addr/FQDN of the host. + +=item B<--port> + +Port used by host. + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/src/apps/protocols/x509/plugin.pm b/src/apps/protocols/x509/plugin.pm index 916bb12cb..1d973cfb6 100644 --- a/src/apps/protocols/x509/plugin.pm +++ b/src/apps/protocols/x509/plugin.pm @@ -37,6 +37,7 @@ sub new { $self->{custom_modes}->{tcp} = 'apps::protocols::x509::custom::tcp'; $self->{custom_modes}->{https} = 'apps::protocols::x509::custom::https'; $self->{custom_modes}->{file} = 'apps::protocols::x509::custom::file'; + $self->{custom_modes}->{opensslcli} = 'apps::protocols::x509::custom::opensslcli'; return $self; } diff --git a/src/apps/thales/mistral/vs9/restapi/custom/api.pm b/src/apps/thales/mistral/vs9/restapi/custom/api.pm index c8a283c53..623159211 100644 --- a/src/apps/thales/mistral/vs9/restapi/custom/api.pm +++ b/src/apps/thales/mistral/vs9/restapi/custom/api.pm @@ -181,7 +181,7 @@ sub clean_token { my ($self, %options) = @_; my $datas = { updated => time() }; - $self->{cache}->write(data => $datas); + $self->{cache_connect}->write(data => $datas); } sub request_api { diff --git a/src/centreon/common/airespace/snmp/mode/listaps.pm b/src/centreon/common/airespace/snmp/mode/listaps.pm new file mode 100644 index 000000000..25713863b --- /dev/null +++ b/src/centreon/common/airespace/snmp/mode/listaps.pm @@ -0,0 +1,136 @@ +# +# Copyright 2023 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 centreon::common::airespace::snmp::mode::listaps; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + # Collecting all the relevant informations user may needs when using discovery function for AP in Cisco WLC controllers. + # They had been select with https://oidref.com/1.3.6.1.4.1.14179.2.2.1.1.3 as support. + my $mapping = { + name => { oid => '.1.3.6.1.4.1.14179.2.2.1.1.3' }, # bsnAPName + location => { oid => '.1.3.6.1.4.1.14179.2.2.1.1.4' }, # bsnAPLocation + model => { oid => '.1.3.6.1.4.1.14179.2.2.1.1.16' } # bsnAPModel + }; + # parent oid for all the mapping usage + my $oid_bsnAPEntry = '.1.3.6.1.4.1.14179.2.2.1.1'; + + my $snmp_result = $options{snmp}->get_table( + oid => $oid_bsnAPEntry, + start => $mapping->{name}->{oid}, # First oid of the mapping => here : 3 + end => $mapping->{model}->{oid} # Last oid of the mapping => here : 16 + ); + + my $results = {}; + # Iterate for all oids catch in snmp result above + foreach my $oid (keys %$snmp_result) { + next if ($oid !~ /^$mapping->{name}->{oid}\.(.*)$/); + my $oid_path = $1; + + my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $oid_path); + + $results->{$oid_path} = { + name => $result->{name}, + location => $result->{location}, + model => $result->{model} + }; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach my $oid_path (sort keys %$results) { + $self->{output}->output_add( + long_msg => sprintf( + '[oid_path: %s] [name: %s] [location: %s] [model: %s]', + $oid_path, + $results->{$oid_path}->{name}, + $results->{$oid_path}->{location}, + $results->{$oid_path}->{model} + ) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List aps' + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name','location','model']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach my $oid_path (sort keys %$results) { + $self->{output}->add_disco_entry( + name => $results->{$oid_path}->{name}, + location => $results->{$oid_path}->{location}, + model => $results->{$oid_path}->{model} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List wireless name. + +=over 8 + +=back + +=cut diff --git a/src/centreon/common/aruba/snmp/mode/license.pm b/src/centreon/common/aruba/snmp/mode/license.pm index 5376db5e7..8152e8186 100644 --- a/src/centreon/common/aruba/snmp/mode/license.pm +++ b/src/centreon/common/aruba/snmp/mode/license.pm @@ -24,16 +24,25 @@ use base qw(centreon::plugins::templates::counter); use strict; use warnings; -use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold); +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); use Time::Local; sub custom_status_output { my ($self, %options) = @_; - my $msg = sprintf("Status is '%s', Expires in '%s' [%s]", - $self->{result_values}->{flag}, - $self->{result_values}->{expires_human}, - $self->{result_values}->{expires_date}); + my $msg = "status is '" . $self->{result_values}->{flag} . "'"; + if ($self->{result_values}->{expires_date} =~ /never/i) { + $msg .= ', never expires'; + } elsif ($self->{result_values}->{expires_date} =~ /expired/i) { + $msg .= ', expired'; + } else { + $msg .= sprintf( + "expires in %s [%s]", + $self->{result_values}->{expires_human}, + $self->{result_values}->{expires_date} + ); + } + return $msg; } @@ -45,36 +54,21 @@ sub custom_status_calc { $self->{result_values}->{service} = $options{new_datas}->{$self->{instance} . '_sysExtLicenseService'}; $self->{result_values}->{expires} = $options{new_datas}->{$self->{instance} . '_sysExtLicenseExpires'}; $self->{result_values}->{expires_date} = $options{new_datas}->{$self->{instance} . '_sysExtLicenseExpires'}; - $self->{result_values}->{expires_human} = 'Never'; - - if ($self->{result_values}->{expires} !~ /Never/) { + + $self->{result_values}->{expires_human} = 'never'; + if ($self->{result_values}->{expires_date} =~ /Expired/) { + $self->{result_values}->{expires_human} = 'expired'; + $self->{result_values}->{expires} = 0; + } + + if ($self->{result_values}->{expires_date} !~ /Never|Expired/) { my ($year, $mon, $mday, $hour, $min, $sec) = split(/[\s\-:]+/, $self->{result_values}->{expires}); $self->{result_values}->{expires} = timelocal($sec, $min, $hour, $mday, $mon - 1, $year) - time(); $self->{result_values}->{expires_human} = centreon::plugins::misc::change_seconds(value => $self->{result_values}->{expires}); $self->{result_values}->{expires_human} = $self->{result_values}->{expires} = 0 if ($self->{result_values}->{expires} < 0); } - return 0; -} -sub set_counters { - my ($self, %options) = @_; - - $self->{maps_counters_type} = [ - { name => 'license', type => 1, cb_prefix_output => 'prefix_output', - message_multiple => 'All licenses status are ok' }, - ]; - - $self->{maps_counters}->{license} = [ - { label => 'status', threshold => 0, set => { - key_values => [ { name => 'sysExtLicenseKey' }, { name => 'sysExtLicenseFlags' }, - { name => 'sysExtLicenseService' }, { name => 'sysExtLicenseExpires' } ], - 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 => \&catalog_status_threshold, - } - }, - ]; + return 0; } sub prefix_output { @@ -83,27 +77,39 @@ sub prefix_output { return "License '" . $options{instance_value}->{sysExtLicenseService} . "' "; } +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'license', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All licenses status are ok' } + ]; + + $self->{maps_counters}->{license} = [ + { label => 'status', type => 2, critical_default => '%{flag} !~ /enabled/i || (%{expires} ne "Never" && %{expires} < 86400)', set => { + key_values => [ + { name => 'sysExtLicenseKey' }, { name => 'sysExtLicenseFlags' }, + { name => 'sysExtLicenseService' }, { name => 'sysExtLicenseExpires' } + ], + 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 => \&catalog_status_threshold_ng + } + } + ]; +} + sub new { my ($class, %options) = @_; my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); bless $self, $class; - + $options{options}->add_options(arguments => { - "warning-status:s" => { name => 'warning_status' }, - "critical-status:s" => { name => 'critical_status', - default => '%{flag} !~ /enabled/i || (%{expires} ne "Never" && %{expires} < 86400)' }, }); return $self; } -sub check_options { - my ($self, %options) = @_; - $self->SUPER::check_options(%options); - - $self->change_macros(macros => ['warning_status', 'critical_status']); -} - my %map_flags = ( 'E' => 'enabled', 'A' => 'auto-generated', 'R' => 'reboot-required' ); @@ -115,7 +121,7 @@ my $mapping = { sysExtLicenseInstalled => { oid => '.1.3.6.1.4.1.14823.2.2.1.2.1.20.1.3' }, sysExtLicenseExpires => { oid => '.1.3.6.1.4.1.14823.2.2.1.2.1.20.1.4' }, sysExtLicenseFlags => { oid => '.1.3.6.1.4.1.14823.2.2.1.2.1.20.1.5', map => \%map_flags }, - sysExtLicenseService => { oid => '.1.3.6.1.4.1.14823.2.2.1.2.1.20.1.6' }, + sysExtLicenseService => { oid => '.1.3.6.1.4.1.14823.2.2.1.2.1.20.1.6' } }; sub manage_selection { @@ -133,7 +139,6 @@ sub manage_selection { } $self->{license} = {}; - foreach my $oid (keys %{$snmp_result}) { next if ($oid !~ /^$mapping->{sysExtLicenseKey}->{oid}\.(.*)/); my $instance = $1; @@ -146,7 +151,7 @@ sub manage_selection { $self->{license}->{$result->{sysExtLicenseService}} = { %{$result} }; } - + if (scalar(keys %{$self->{license}}) <= 0) { $self->{output}->add_option_msg(short_msg => "No license found."); $self->{output}->option_exit(); diff --git a/src/centreon/common/cisco/standard/snmp/mode/bgp.pm b/src/centreon/common/cisco/standard/snmp/mode/bgp.pm index f550ec884..54020811e 100644 --- a/src/centreon/common/cisco/standard/snmp/mode/bgp.pm +++ b/src/centreon/common/cisco/standard/snmp/mode/bgp.pm @@ -219,7 +219,10 @@ sub manage_selection { } my $local; - if (length($result->{localAddr}) == 4) { + # it can be empty + if (length($result->{localAddr}) == 0) { + $local = '-'; + } elsif (length($result->{localAddr}) == 4) { $local = Socket::inet_ntop(Socket::AF_INET, $result->{localAddr}) . ':' . $result->{localPort}; } else { $local = '[' . Socket::inet_ntop(Socket::AF_INET6, $result->{localAddr}) . ']:' . $result->{localPort}; diff --git a/src/centreon/common/emc/navisphere/custom/custom.pm b/src/centreon/common/emc/navisphere/custom/custom.pm index e064993bb..ad6dcb54f 100644 --- a/src/centreon/common/emc/navisphere/custom/custom.pm +++ b/src/centreon/common/emc/navisphere/custom/custom.pm @@ -117,9 +117,9 @@ sub check_options { command => $self->{option_results}->{naviseccli_command}, command_path => $self->{option_results}->{naviseccli_path} ); - $self->{option_results}->{navicli_command} = 'naviseccli' + $self->{option_results}->{naviseccli_command} = 'naviseccli' if (!defined($self->{option_results}->{naviseccli_command}) || $self->{option_results}->{naviseccli_command} eq ''); - $self->{option_results}->{navicli_path} = '/opt/Navisphere/bin' + $self->{option_results}->{naviseccli_path} = '/opt/Navisphere/bin' if (!defined($self->{option_results}->{naviseccli_path}) || $self->{option_results}->{naviseccli_path} eq ''); if (defined($self->{option_results}->{ssh_address}) && $self->{option_results}->{ssh_address} ne '') { diff --git a/src/centreon/common/redfish/restapi/mode/components/drive.pm b/src/centreon/common/redfish/restapi/mode/components/drive.pm new file mode 100644 index 000000000..42c09556a --- /dev/null +++ b/src/centreon/common/redfish/restapi/mode/components/drive.pm @@ -0,0 +1,84 @@ +# +# Copyright 2023 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 centreon::common::redfish::restapi::mode::components::drive; + +use strict; +use warnings; + +sub check { + my ($self) = @_; + + $self->{output}->output_add(long_msg => 'checking drives'); + $self->{components}->{drive} = { name => 'drives', total => 0, skip => 0 }; + return if ($self->check_filter(section => 'drive')); + + $self->get_storages() if (!defined($self->{storages})); + + foreach my $storage (@{$self->{storages}}) { + $storage->{'@odata.id'} =~ /Systems\/(\d+)\//; + my $system_id = $1; + my $system_name = 'system:' . $1; + + my $storage_name = $storage->{Id}; + + foreach (@{$storage->{Drives}}) { + my $drive = $self->get_drive(drive => $_); + + my $instance = $system_id . '.' . $storage->{Id} . '.' . $drive->{Id}; + + $drive->{Status}->{Health} = defined($drive->{Status}->{Health}) ? $drive->{Status}->{Health} : 'n/a'; + $drive->{Status}->{State} = defined($drive->{Status}->{State}) ? $drive->{Status}->{State} : 'n/a'; + next if ($self->check_filter(section => 'drive', instance => $instance)); + $self->{components}->{drive}->{total}++; + + $self->{output}->output_add( + long_msg => sprintf( + "drive '%s/%s/%s' status is '%s' [instance: %s, state: %s, location: %s]", + $system_name, + $storage_name, + $drive->{Id}, + $drive->{Status}->{Health}, + $instance, + $drive->{Status}->{State}, + $drive->{PhysicalLocation}->{PartLocation}->{ServiceLabel} + ) + ); + + my $exit = $self->get_severity(label => 'state', section => 'drive.state', value => $drive->{Status}->{State}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Drive '%s/%s/%s' state is '%s'", $system_name, $storage_name, $drive->{Id}, $drive->{Status}->{State}) + ); + } + + $exit = $self->get_severity(label => 'status', section => 'drive.status', value => $drive->{Status}->{Health}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Drive '%s/%s/%s' status is '%s'", $system_name, $storage_name, $drive->{Id}, $drive->{Status}->{Health}) + ); + } + } + } +} + +1; diff --git a/src/centreon/common/redfish/restapi/mode/components/sc.pm b/src/centreon/common/redfish/restapi/mode/components/sc.pm new file mode 100644 index 000000000..04896346b --- /dev/null +++ b/src/centreon/common/redfish/restapi/mode/components/sc.pm @@ -0,0 +1,78 @@ +# +# Copyright 2023 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 centreon::common::redfish::restapi::mode::components::sc; + +use strict; +use warnings; + +sub check { + my ($self) = @_; + + $self->{output}->output_add(long_msg => 'checking storage controllers'); + $self->{components}->{sc} = { name => 'sc', total => 0, skip => 0 }; + return if ($self->check_filter(section => 'sc')); + + $self->get_storages() if (!defined($self->{storages})); + + foreach my $storage (@{$self->{storages}}) { + $storage->{'@odata.id'} =~ /Systems\/(\d+)\//; + my $system_id = $1; + my $system_name = 'system:' . $1; + + my $storage_name = $storage->{Id}; + + foreach my $sc (@{$storage->{StorageControllers}}) { + my $instance .= $system_id . '.' . $storage->{Id} . '.' . $sc->{MemberId}; + + my $sc_name = $sc->{MemberId}; + + $sc->{Status}->{Health} = defined($sc->{Status}->{Health}) ? $sc->{Status}->{Health} : 'n/a'; + $sc->{Status}->{State} = defined($sc->{Status}->{State}) ? $sc->{Status}->{State} : 'n/a'; + next if ($self->check_filter(section => 'sc', instance => $instance)); + $self->{components}->{sc}->{total}++; + + $self->{output}->output_add( + long_msg => sprintf( + "storage controller '%s/%s/%s' status is '%s' [instance: %s, state: %s]", + $system_name, $storage_name, $sc_name, $sc->{Status}->{Health}, $instance, $sc->{Status}->{State} + ) + ); + + my $exit = $self->get_severity(label => 'state', section => 'sc.state', value => $sc->{Status}->{State}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Storage controller '%s/%s/%s' state is '%s'", $system_name, $storage_name, $sc_name, $sc->{Status}->{State}) + ); + } + + $exit = $self->get_severity(label => 'status', section => 'sc.status', value => $sc->{Status}->{Health}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Storage controller '%s/%s/%s' status is '%s'", $system_name, $storage_name, $sc_name, $sc->{Status}->{Health}) + ); + } + } + } +} + +1; diff --git a/src/centreon/common/redfish/restapi/mode/components/storage.pm b/src/centreon/common/redfish/restapi/mode/components/storage.pm new file mode 100644 index 000000000..f27bb50fe --- /dev/null +++ b/src/centreon/common/redfish/restapi/mode/components/storage.pm @@ -0,0 +1,73 @@ +# +# Copyright 2023 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 centreon::common::redfish::restapi::mode::components::storage; + +use strict; +use warnings; + +sub check { + my ($self) = @_; + + $self->{output}->output_add(long_msg => 'checking storages'); + $self->{components}->{storage} = { name => 'storages', total => 0, skip => 0 }; + return if ($self->check_filter(section => 'storage')); + + $self->get_storages() if (!defined($self->{storages})); + + foreach my $storage (@{$self->{storages}}) { + $storage->{'@odata.id'} =~ /Systems\/(\d+)\//; + my $system_id = $1; + my $system_name = 'system:' . $1; + + my $storage_name = $storage->{Id}; + my $instance = $system_id . '.' . $storage->{Id}; + + $storage->{Status}->{Health} = defined($storage->{Status}->{HealthRollup}) ? $storage->{Status}->{HealthRollup} : 'n/a'; + $storage->{Status}->{State} = defined($storage->{Status}->{State}) ? $storage->{Status}->{State} : 'n/a'; + next if ($self->check_filter(section => 'storage', instance => $instance)); + $self->{components}->{storage}->{total}++; + + $self->{output}->output_add( + long_msg => sprintf( + "storage '%s/%s' status is '%s' [instance: %s, state: %s]", + $system_name, $storage_name, $storage->{Status}->{HealthRollup}, $instance, $storage->{Status}->{State} + ) + ); + + my $exit = $self->get_severity(label => 'state', section => 'storage.state', value => $storage->{Status}->{State}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Storage '%s/%s' state is '%s'", $system_name, $storage_name, $storage->{Status}->{State}) + ); + } + + $exit = $self->get_severity(label => 'status', section => 'storage.status', value => $storage->{Status}->{HealthRollup}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Storage '%s/%s' status is '%s'", $system_name, $storage_name, $storage->{Status}->{HealthRollup}) + ); + } + } +} + +1; diff --git a/src/centreon/common/redfish/restapi/mode/components/volume.pm b/src/centreon/common/redfish/restapi/mode/components/volume.pm new file mode 100644 index 000000000..29dd7fbf9 --- /dev/null +++ b/src/centreon/common/redfish/restapi/mode/components/volume.pm @@ -0,0 +1,78 @@ +# +# Copyright 2023 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 centreon::common::redfish::restapi::mode::components::volume; + +use strict; +use warnings; + +sub check { + my ($self) = @_; + + $self->{output}->output_add(long_msg => 'checking volumes'); + $self->{components}->{volume} = { name => 'volumes', total => 0, skip => 0 }; + return if ($self->check_filter(section => 'volume')); + + $self->get_storages() if (!defined($self->{storages})); + + foreach my $storage (@{$self->{storages}}) { + $storage->{'@odata.id'} =~ /Systems\/(\d+)\//; + my $system_id = $1; + my $system_name = 'system:' . $1; + + my $storage_name = $storage->{Id}; + + my $volumes = $self->get_volumes(storage => $storage); + + foreach my $volume (@$volumes) { + my $instance = $system_id . '.' . $storage->{Id} . '.' . $volume->{Id}; + + $volume->{Status}->{Health} = defined($volume->{Status}->{Health}) ? $volume->{Status}->{Health} : 'n/a'; + $volume->{Status}->{State} = defined($volume->{Status}->{State}) ? $volume->{Status}->{State} : 'n/a'; + next if ($self->check_filter(section => 'volume', instance => $instance)); + $self->{components}->{volume}->{total}++; + + $self->{output}->output_add( + long_msg => sprintf( + "volume '%s/%s/%s' status is '%s' [instance: %s, state: %s]", + $system_name, $storage_name, $volume->{Id}, $volume->{Status}->{Health}, $instance, $volume->{Status}->{State} + ) + ); + + my $exit = $self->get_severity(label => 'state', section => 'volume.state', value => $volume->{Status}->{State}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Volume '%s/%s/%s' state is '%s'", $system_name, $storage_name, $volume->{Id}, $volume->{Status}->{State}) + ); + } + + $exit = $self->get_severity(label => 'status', section => 'volume.status', value => $volume->{Status}->{Health}); + if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Volume '%s/%s/%s' status is '%s'", $system_name, $storage_name, $volume->{Id}, $volume->{Status}->{Health}) + ); + } + } + } +} + +1; diff --git a/src/centreon/common/redfish/restapi/mode/hardware.pm b/src/centreon/common/redfish/restapi/mode/hardware.pm index b7a46bdaf..ea838db72 100644 --- a/src/centreon/common/redfish/restapi/mode/hardware.pm +++ b/src/centreon/common/redfish/restapi/mode/hardware.pm @@ -51,7 +51,7 @@ sub set_system { $self->{components_exec_load} = 0; $self->{components_path} = 'centreon::common::redfish::restapi::mode::components'; - $self->{components_module} = ['chassis', 'device', 'fan', 'psu', 'temperature']; + $self->{components_module} = ['chassis', 'device', 'drive', 'fan', 'psu', 'sc', 'storage', 'temperature', 'volume']; } sub new { @@ -106,6 +106,43 @@ sub get_chassis { } } +sub get_drive { + my ($self, %options) = @_; + + return {} if (!defined($options{drive}->{'@odata.id'})); + return $self->{custom}->request_api(url_path => $options{drive}->{'@odata.id'}); +} + +sub get_volumes { + my ($self, %options) = @_; + + return [] if (!defined($options{storage}->{Volumes}->{'@odata.id'})); + + my $volumes = $self->{custom}->request_api(url_path => $options{storage}->{Volumes}->{'@odata.id'}); + + my $result = []; + foreach my $volume (@{$volumes->{Members}}) { + my $volume_detailed = $self->{custom}->request_api(url_path => $volume->{'@odata.id'}); + push @$result, $volume_detailed; + } + + return $result; +} + +sub get_storages { + my ($self, %options) = @_; + + $self->{storages} = []; + my $systems = $self->{custom}->request_api(url_path => '/redfish/v1/Systems'); + foreach my $system (@{$systems->{Members}}) { + my $storages = $self->{custom}->request_api(url_path => $system->{'@odata.id'} . '/Storage/'); + foreach my $storage (@{$storages->{Members}}) { + my $storage_detailed = $self->{custom}->request_api(url_path => $storage->{'@odata.id'}); + push @{$self->{storages}}, $storage_detailed; + } + } +} + sub execute_custom { my ($self, %options) = @_; @@ -123,7 +160,7 @@ Check hardware. =item B<--component> Which component to check (Default: '.*'). -Can be: 'chassis', 'device', 'fan', 'psu', 'temperature'. +Can be: 'chassis', 'device', 'drive', 'fan', 'psu', 'sc', 'storage', 'temperature', 'volume'. =item B<--filter> diff --git a/src/centreon/plugins/alternative/Getopt.pm b/src/centreon/plugins/alternative/Getopt.pm index 3f33c66ea..94b8a6f61 100644 --- a/src/centreon/plugins/alternative/Getopt.pm +++ b/src/centreon/plugins/alternative/Getopt.pm @@ -62,6 +62,15 @@ sub GetOptions { if (defined($ARGV[$i]) && $ARGV[$i] =~ /^--(.*?)(?:=|$)((?s).*)/) { my ($option, $value) = ($1, $2); + # The special argument "--" forces an end of option-scanning. + # All arguments placed after are stored in a list with the special option key '_double_dash_'. + if ($option eq '' && $value eq '') { + my @values = splice @ARGV, $i + 1, $num_args - $i - 1; + push @{${$opts{'_double_dash_'}}}, @values; + splice @ARGV, $i, 1; + last; + } + # find type of option if ($search_str !~ /,((?:[^,]*?\|){0,}$option(?:\|.*?){0,}(:.*?){0,1}),/) { warn "Unknown option: $option" if ($warn_message == 1); diff --git a/src/centreon/plugins/dbi.pm b/src/centreon/plugins/dbi.pm index 8a3f0eeb0..85d0f2923 100644 --- a/src/centreon/plugins/dbi.pm +++ b/src/centreon/plugins/dbi.pm @@ -136,8 +136,15 @@ sub check_options { my ($self, %options) = @_; $self->{data_source} = (defined($self->{option_results}->{data_source})) ? shift(@{$self->{option_results}->{data_source}}) : undef; - $self->{username} = (defined($self->{option_results}->{username})) ? shift(@{$self->{option_results}->{username}}) : undef; - $self->{password} = (defined($self->{option_results}->{password})) ? shift(@{$self->{option_results}->{password}}) : undef; + $self->{username} = undef; + if (defined($self->{option_results}->{username})) { + $self->{username} = ref($self->{option_results}->{username}) eq 'ARRAY' ? shift(@{$self->{option_results}->{username}}) : $self->{option_results}->{username}; + } + $self->{password} = undef; + if (defined($self->{option_results}->{password})) { + $self->{password} = ref($self->{option_results}->{password}) eq 'ARRAY' ? shift(@{$self->{option_results}->{password}}) : $self->{option_results}->{password}; + } + $self->{connect_options} = (defined($self->{option_results}->{connect_options})) ? shift(@{$self->{option_results}->{connect_options}}) : undef; $self->{connect_query} = (defined($self->{option_results}->{connect_query})) ? shift(@{$self->{option_results}->{connect_query}}) : undef; $self->{env} = (defined($self->{option_results}->{env})) ? shift(@{$self->{option_results}->{env}}) : undef; diff --git a/src/centreon/plugins/mode.pm b/src/centreon/plugins/mode.pm index 82db8eb8b..8e3d9f2ce 100644 --- a/src/centreon/plugins/mode.pm +++ b/src/centreon/plugins/mode.pm @@ -32,6 +32,7 @@ sub new { $self->{perfdata} = centreon::plugins::perfdata->new(output => $options{output}); %{$self->{option_results}} = (); + @{$self->{option_extras}} = @{$options{options}->{extra_arguments}}; $self->{output} = $options{output}; $self->{output}->use_new_perfdata(value => 1) if (defined($options{force_new_perfdata}) && $options{force_new_perfdata} == 1); diff --git a/src/centreon/plugins/options.pm b/src/centreon/plugins/options.pm index 6ef70f060..28dd8e18c 100644 --- a/src/centreon/plugins/options.pm +++ b/src/centreon/plugins/options.pm @@ -37,6 +37,7 @@ sub new { $self->{options} = {}; @{$self->{pod_package}} = (); $self->{pod_packages_once} = {}; + $self->{extra_arguments} = []; if ($alternative == 0) { require Getopt::Long; @@ -145,6 +146,9 @@ sub parse_options { }; } + # Store all arguments placed after the special argument "--" in the 'extra_arguments' list + $self->{options}->{'_double_dash_'} = \$self->{extra_arguments}; + GetOptions( %{$self->{options}} ); diff --git a/src/centreon/plugins/passwordmgr/hashicorpvault.pm b/src/centreon/plugins/passwordmgr/hashicorpvault.pm index 41fe529af..ca0380ec1 100644 --- a/src/centreon/plugins/passwordmgr/hashicorpvault.pm +++ b/src/centreon/plugins/passwordmgr/hashicorpvault.pm @@ -223,7 +223,7 @@ sub do_map { while ($map =~ /\%\{(.*?)\}/g) { my $sub = ''; $sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1})); - $map =~ s/\%\{$1\}/$sub/g + $map =~ s/\%\{$1\}/$sub/g; } $option =~ s/-/_/g; $options{option_results}->{$option} = $map; diff --git a/src/centreon/plugins/statefile.pm b/src/centreon/plugins/statefile.pm index 30476507b..0cf02546b 100644 --- a/src/centreon/plugins/statefile.pm +++ b/src/centreon/plugins/statefile.pm @@ -113,7 +113,7 @@ sub check_options { } } - $self->{statefile_format} = 'dumper'; + $self->{statefile_format} = 'json'; if (defined($options{option_results}->{statefile_format}) && $options{option_results}->{statefile_format} ne '' && $options{option_results}->{statefile_format} =~ /^(?:dumper|json|storable)$/) { $self->{statefile_format} = $options{option_results}->{statefile_format}; diff --git a/src/cloud/azure/common/storageaccount/filesharequota.pm b/src/cloud/azure/common/storageaccount/filesharequota.pm index 65efed40d..eb35b5895 100644 --- a/src/cloud/azure/common/storageaccount/filesharequota.pm +++ b/src/cloud/azure/common/storageaccount/filesharequota.pm @@ -28,9 +28,8 @@ use warnings; sub custom_usage_perfdata { my ($self, %options) = @_; - my $label = 'capacity-usage'; my $value_perf = $self->{result_values}->{used_space}; - + my %total_options = (); if ($self->{instance_mode}->{option_results}->{units} eq '%') { $total_options{total} = $self->{result_values}->{total_capacity}; @@ -38,12 +37,12 @@ sub custom_usage_perfdata { } $self->{output}->perfdata_add( - label => $label, unit => 'B', - nlabel => 'fileshare.capacity.usage.bytes', + nlabel => 'fileshare.capacity.usage.bytes', + unit => 'B', value => $value_perf, warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, %total_options), critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, %total_options), - min => 0, max => $self->{result_values}->{total_capacity}, + min => 0, max => $self->{result_values}->{total_capacity} ); } @@ -55,9 +54,13 @@ sub custom_usage_threshold { if ($self->{instance_mode}->{option_results}->{units} eq '%') { $threshold_value = $self->{result_values}->{prct_used}; } - $exit = $self->{perfdata}->threshold_check(value => $threshold_value, - threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, - { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' } ]); + $exit = $self->{perfdata}->threshold_check( + value => $threshold_value, + threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' } + ] + ); return $exit; } @@ -66,11 +69,13 @@ sub custom_usage_output { my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total_capacity} ); my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used_space} ); - my $msg = sprintf("Fileshare '%s' from storage account '%s' used capacity: %s (%.2f%%), total size %s", - $self->{result_values}->{fileshare}, - $self->{result_values}->{storageaccount}, - $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, - $total_size_value . " " . $total_size_unit); + my $msg = sprintf( + "Fileshare '%s' from storage account '%s' used capacity: %s (%.2f%%), total size %s", + $self->{result_values}->{fileshare}, + $self->{result_values}->{storageaccount}, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, + $total_size_value . " " . $total_size_unit + ); return $msg; } @@ -89,7 +94,7 @@ sub custom_usage_calc { sub set_counters { my ($self, %options) = @_; - + $self->{maps_counters_type} = [ { name => 'fileshare', type => 0 } ]; @@ -100,7 +105,7 @@ sub set_counters { closure_custom_calc => $self->can('custom_usage_calc'), closure_custom_output => $self->can('custom_usage_output'), closure_custom_perfdata => $self->can('custom_usage_perfdata'), - closure_custom_threshold_check => $self->can('custom_usage_threshold'), + closure_custom_threshold_check => $self->can('custom_usage_threshold') } } ] @@ -111,13 +116,12 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); bless $self, $class; - $options{options}->add_options(arguments => - { - "resource-group:s" => { name => 'resource_group' }, - "storage-account:s" => { name => 'storage_account' }, - "units:s" => { name => 'units', default => '%' }, - "fileshare:s" => { name => 'fileshare' } - }); + $options{options}->add_options(arguments => { + 'resource-group:s' => { name => 'resource_group' }, + 'storage-account:s' => { name => 'storage_account' }, + 'units:s' => { name => 'units', default => '%' }, + 'fileshare:s' => { name => 'fileshare' } + }); return $self; } @@ -140,21 +144,22 @@ sub check_options { $self->{output}->add_option_msg(short_msg => "Need to specify --fileshare "); $self->{output}->option_exit(); } - } sub manage_selection { my ($self, %options) = @_; my $results; - $results = $options{custom}->azure_get_file_share_stats(resource_group => $self->{option_results}->{resource_group}, storage_account => $self->{option_results}->{storage_account}, - fileshare => $self->{option_results}->{fileshare}, api_version => $self->{api_version}); + $results = $options{custom}->azure_get_file_share_stats( + resource_group => $self->{option_results}->{resource_group}, storage_account => $self->{option_results}->{storage_account}, + fileshare => $self->{option_results}->{fileshare}, api_version => $self->{api_version} + ); $self->{fileshare} = { storage_account => $self->{option_results}->{storage_account}, fileshare => $self->{option_results}->{fileshare}, total_capacity => $results->{properties}->{shareQuota}, - used_space => $results->{properties}->{shareUsageBytes} * 1024 ** 3 + used_space => $results->{properties}->{shareUsageBytes} }; if (scalar(keys %{$self->{fileshare}}) <= 0) { diff --git a/src/cloud/azure/custom/api.pm b/src/cloud/azure/custom/api.pm index 9848472c7..85f5f5137 100644 --- a/src/cloud/azure/custom/api.pm +++ b/src/cloud/azure/custom/api.pm @@ -280,7 +280,7 @@ sub convert_iso8601_to_epoch { nanosecond => $7 ); - my $epoch_time = $dt->epoch; + my $epoch_time = $dt->epoch(); return $epoch_time; } @@ -327,8 +327,13 @@ sub azure_get_subscription_cost_management { $encoded_form_post = JSON::XS->new->utf8->encode($options{body_post}); }; - $self->{http}->add_header(key => 'Content-Type', value => 'application/json'); - my $response = $self->request_api(method => 'POST', full_url => $full_url, query_form_post => $encoded_form_post, hostname => ''); + my $response = $self->request_api( + method => 'POST', + full_url => $full_url, + query_form_post => $encoded_form_post, + hostname => '', + header => ['Content-Type: application/json'] + ); return $response->{properties}->{rows}; } @@ -562,23 +567,23 @@ sub azure_list_vms { my $full_response = []; my $full_url = $self->azure_list_vms_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } - - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } + + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } - + return $full_response; } sub azure_list_file_shares_set_url { my ($self, %options) = @_; - my $url = $self->{management_endpoint} . "/subscriptions/" . $self->{subscription} . "/resourceGroups/" . $options{resource_group} . "/providers/Microsoft.Storage/storageAccounts/" - . $options{storage_account} . "/fileServices/default/shares?api-version=" . $options{api_version}; + my $url = $self->{management_endpoint} . "/subscriptions/" . $self->{subscription} . "/resourceGroups/" . $options{resource_group} . "/providers/Microsoft.Storage/storageAccounts/" . + $options{storage_account} . "/fileServices/default/shares?api-version=" . $options{api_version}; return $url; } @@ -766,13 +771,13 @@ sub azure_list_sqlservers { my $full_response = []; my $full_url = $self->azure_list_sqlservers_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; @@ -992,13 +997,13 @@ sub azure_list_nics { my $full_response = []; my $full_url = $self->azure_list_nics_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; @@ -1020,13 +1025,13 @@ sub azure_list_nsgs { my $full_response = []; my $full_url = $self->azure_list_nsgs_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; @@ -1048,13 +1053,13 @@ sub azure_list_publicips { my $full_response = []; my $full_url = $self->azure_list_publicips_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; @@ -1076,13 +1081,13 @@ sub azure_list_route_tables { my $full_response = []; my $full_url = $self->azure_list_route_tables_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; @@ -1104,13 +1109,13 @@ sub azure_list_snapshots { my $full_response = []; my $full_url = $self->azure_list_snapshots_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; @@ -1132,13 +1137,13 @@ sub azure_list_sqlvms { my $full_response = []; my $full_url = $self->azure_list_sqlvms_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; @@ -1160,13 +1165,13 @@ sub azure_list_sqlelasticpools { my $full_response = []; my $full_url = $self->azure_list_sqlelasticpools_set_url(%options); while (1) { - my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); - foreach (@{$response->{value}}) { - push @$full_response, $_; - } + my $response = $self->request_api(method => 'GET', full_url => $full_url, hostname => ''); + foreach (@{$response->{value}}) { + push @$full_response, $_; + } - last if (!defined($response->{nextLink})); - $full_url = $response->{nextLink}; + last if (!defined($response->{nextLink})); + $full_url = $response->{nextLink}; } return $full_response; diff --git a/src/cloud/kubernetes/custom/api.pm b/src/cloud/kubernetes/custom/api.pm index 9e0843a0a..102639144 100644 --- a/src/cloud/kubernetes/custom/api.pm +++ b/src/cloud/kubernetes/custom/api.pm @@ -49,7 +49,8 @@ sub new { 'token:s' => { name => 'token' }, 'timeout:s' => { name => 'timeout' }, 'limit:s' => { name => 'limit' }, - 'config-file:s' => { name => 'config_file' } + 'config-file:s' => { name => 'config_file' }, + 'namespace:s' => { name => 'namespace' } }); } $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); @@ -71,18 +72,20 @@ sub set_defaults {} sub check_options { my ($self, %options) = @_; - $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : undef; + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : ''; $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->{timeout} = (defined($self->{option_results}->{timeout})) && $self->{option_results}->{timeout} =~ /(\d+)/ ? $1 : 10; $self->{token} = (defined($self->{option_results}->{token})) ? $self->{option_results}->{token} : ''; $self->{limit} = (defined($self->{option_results}->{limit})) && $self->{option_results}->{limit} =~ /(\d+)/ ? $1 : 100; - - if (!defined($self->{hostname}) || $self->{hostname} eq '') { + $self->{namespace} = defined($self->{option_results}->{namespace}) && $self->{option_results}->{namespace} ne '' ? + $self->{option_results}->{namespace} : ''; + + if ($self->{hostname} eq '') { $self->{output}->add_option_msg(short_msg => "Need to specify --hostname option."); $self->{output}->option_exit(); } - if (!defined($self->{token}) || $self->{token} eq '') { + if ($self->{token} eq '') { $self->{output}->add_option_msg(short_msg => "Need to specify --token option."); $self->{output}->option_exit(); } @@ -116,10 +119,13 @@ sub settings { sub request_api { my ($self, %options) = @_; - $self->settings; + $self->settings(); - $self->{output}->output_add(long_msg => "URL: '" . $self->{proto} . '://' . $self->{hostname} . - ':' . $self->{port} . $options{url_path} . "'", debug => 1); + $self->{output}->output_add( + long_msg => "URL: '" . $self->{proto} . '://' . $self->{hostname} . + ':' . $self->{port} . $options{url_path} . "'", + debug => 1 + ); my $response = $self->{http}->request(%options); @@ -153,7 +159,7 @@ sub request_api { $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $response"); $self->{output}->option_exit(); } - + return $decoded; } @@ -163,7 +169,7 @@ sub request_api_paginate { my @items; my @get_param = ( 'limit=' . $self->{limit} ); push @get_param, @{$options{get_param}} if (defined($options{get_param})); - + while (1) { my $response = $self->request_api( method => $options{method}, @@ -183,105 +189,141 @@ sub request_api_paginate { sub kubernetes_list_cronjobs { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/apis/batch/v1beta1/cronjobs'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/apis/batch/v1beta1/namespaces/' . $self->{namespace} . '/cronjobs' : '/apis/batch/v1beta1/cronjobs' + ); + return $response; } sub kubernetes_list_daemonsets { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/apis/apps/v1/daemonsets'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/apis/apps/v1/namespaces/' . $self->{namespace} . '/daemonsets' : '/apis/apps/v1/daemonsets' + ); + return $response; } sub kubernetes_list_deployments { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/apis/apps/v1/deployments'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/apis/apps/v1/namespaces/' . $self->{namespace} . '/deployments' : '/apis/apps/v1/deployments' + ); + return $response; } sub kubernetes_list_events { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/api/v1/events'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/api/v1/namespaces/' . $self->{namespace} . '/events' : '/api/v1/events' + ); + return $response; } sub kubernetes_list_ingresses { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/apis/extensions/v1beta1/ingresses'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/apis/extensions/v1beta1/namespaces/' . $self->{namespace} . '/ingresses' : '/apis/extensions/v1beta1/ingresses' + ); + return $response; } sub kubernetes_list_namespaces { my ($self, %options) = @_; - + my $response = $self->request_api_paginate(method => 'GET', url_path => '/api/v1/namespaces'); - + return $response; } sub kubernetes_list_nodes { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/api/v1/nodes'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => '/api/v1/nodes' + ); + return $response; } sub kubernetes_list_rcs { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/api/v1/replicationcontrollers'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/api/v1/namespaces/' . $self->{namespace} . '/replicationcontrollers' : '/api/v1/replicationcontrollers' + ); + return $response; } sub kubernetes_list_replicasets { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/apis/apps/v1/replicasets'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/apis/apps/v1/namespaces/' . $self->{namespace} . '/replicasets' : '/apis/apps/v1/replicasets' + ); + return $response; } sub kubernetes_list_services { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/apis/v1/services'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/api/v1/namespaces/' . $self->{namespace} . '/services' : '/api/v1/services' + ); + return $response; } sub kubernetes_list_statefulsets { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/apis/apps/v1/statefulsets'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/apis/apps/v1/namespaces/' . $self->{namespace} . '/statefulsets' : '/apis/apps/v1/statefulsets' + ); + return $response; } sub kubernetes_list_pods { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/api/v1/pods'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => $self->{namespace} ne '' ? '/api/v1/namespaces/' . $self->{namespace} . '/pods' : '/api/v1/pods' + ); + return $response; } sub kubernetes_list_pvs { my ($self, %options) = @_; - - my $response = $self->request_api_paginate(method => 'GET', url_path => '/api/v1/persistentvolumes'); - + + my $response = $self->request_api_paginate( + method => 'GET', + url_path => '/api/v1/persistentvolumes' + ); + return $response; } @@ -325,6 +367,10 @@ Number of responses to return for each list calls. See https://kubernetes.io/docs/reference/kubernetes-api/common-parameters/common-parameters/#limit +=item B<--namespace> + +Set namespace to get informations. + =back =head1 DESCRIPTION diff --git a/src/cloud/kubernetes/custom/kubectl.pm b/src/cloud/kubernetes/custom/kubectl.pm index 801b6ab60..ff2811a6d 100644 --- a/src/cloud/kubernetes/custom/kubectl.pm +++ b/src/cloud/kubernetes/custom/kubectl.pm @@ -48,6 +48,7 @@ sub new { 'timeout:s' => { name => 'timeout', default => 10 }, 'config-file:s' => { name => 'config_file', default => '~/.kube/config' }, 'context:s' => { name => 'context' }, + 'namespace:s' => { name => 'namespace' }, 'sudo' => { name => 'sudo' }, 'command:s' => { name => 'command', default => '' }, 'command-path:s' => { name => 'command_path' }, @@ -76,6 +77,8 @@ sub check_options { $self->{config_file} = (defined($self->{option_results}->{config_file})) ? $self->{option_results}->{config_file} : ''; $self->{context} = (defined($self->{option_results}->{context})) ? $self->{option_results}->{context} : ''; $self->{timeout} = (defined($self->{option_results}->{timeout})) && $self->{option_results}->{timeout} =~ /(\d+)/ ? $1 : 10; + $self->{namespace_option} = defined($self->{option_results}->{namespace}) && $self->{option_results}->{namespace} ne '' ? + "--namespace='$self->{option_results}->{namespace}'" : '--all-namespaces'; if (!defined($self->{config_file}) || $self->{config_file} eq '') { $self->{output}->add_option_msg(short_msg => "Need to specify --config-file option."); @@ -155,7 +158,7 @@ sub execute { sub kubernetes_list_cronjobs { my ($self, %options) = @_; - my $cmd = "get cronjobs --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get cronjobs $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -167,7 +170,7 @@ sub kubernetes_list_cronjobs { sub kubernetes_list_daemonsets { my ($self, %options) = @_; - my $cmd = "get daemonsets --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get daemonsets $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -179,7 +182,7 @@ sub kubernetes_list_daemonsets { sub kubernetes_list_deployments { my ($self, %options) = @_; - my $cmd = "get deployments --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get deployments $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -191,7 +194,7 @@ sub kubernetes_list_deployments { sub kubernetes_list_events { my ($self, %options) = @_; - my $cmd = "get events --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get events $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -203,7 +206,7 @@ sub kubernetes_list_events { sub kubernetes_list_ingresses { my ($self, %options) = @_; - my $cmd = "get ingresses --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get ingresses $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -227,7 +230,7 @@ sub kubernetes_list_namespaces { sub kubernetes_list_nodes { my ($self, %options) = @_; - my $cmd = "get nodes --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get nodes $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -239,7 +242,7 @@ sub kubernetes_list_nodes { sub kubernetes_list_rcs { my ($self, %options) = @_; - my $cmd = "get replicationcontroller --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get replicationcontroller $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -251,7 +254,7 @@ sub kubernetes_list_rcs { sub kubernetes_list_replicasets { my ($self, %options) = @_; - my $cmd = "get replicasets --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get replicasets $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -263,7 +266,7 @@ sub kubernetes_list_replicasets { sub kubernetes_list_services { my ($self, %options) = @_; - my $cmd = "get services --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get services $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -275,7 +278,7 @@ sub kubernetes_list_services { sub kubernetes_list_statefulsets { my ($self, %options) = @_; - my $cmd = "get statefulsets --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get statefulsets $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -287,7 +290,7 @@ sub kubernetes_list_statefulsets { sub kubernetes_list_pods { my ($self, %options) = @_; - my $cmd = "get pods --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get pods $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -299,7 +302,7 @@ sub kubernetes_list_pods { sub kubernetes_list_pvs { my ($self, %options) = @_; - my $cmd = "get pv --all-namespaces --output='json' --kubeconfig='" . $self->{config_file} . "'" + my $cmd = "get pv $self->{namespace_option} --output='json' --kubeconfig='" . $self->{config_file} . "'" . " --request-timeout='" . $self->{timeout} . "'"; $cmd .= " --context='" . $self->{context} . "'" if (defined($self->{context}) && $self->{context} ne ''); @@ -335,6 +338,10 @@ Kubernetes configuration file path (Default: '~/.kube/config'). Context to use in configuration file. +=item B<--namespace> + +Set namespace to get informations. + =item B<--timeout> Set timeout in seconds (Default: 10). diff --git a/src/cloud/kubernetes/mode/clusterevents.pm b/src/cloud/kubernetes/mode/clusterevents.pm index d97ae3e5f..c16080bd3 100644 --- a/src/cloud/kubernetes/mode/clusterevents.pm +++ b/src/cloud/kubernetes/mode/clusterevents.pm @@ -100,20 +100,20 @@ sub set_counters { $self->{maps_counters}->{global} = [ { label => 'warning', nlabel => 'events.type.warning.count', set => { key_values => [ { name => 'warning' } ], - output_template => 'Warning : %d', + output_template => 'warning: %d', perfdatas => [ - { label => 'warning_events', template => '%d', min => 0 } + { template => '%d', min => 0 } ] } }, { label => 'normal', nlabel => 'events.type.normal.count', set => { key_values => [ { name => 'normal' } ], - output_template => 'Normal : %d', + output_template => 'normal: %d', perfdatas => [ - { label => 'normal_events', template => '%d', min => 0 } + { template => '%d', min => 0 } ] } - }, + } ]; $self->{maps_counters}->{events} = [ @@ -140,31 +140,22 @@ sub new { my ($class, %options) = @_; my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); bless $self, $class; - + $options{options}->add_options(arguments => { - "filter-type:s" => { name => 'filter_type' }, - "filter-namespace:s" => { name => 'filter_namespace' }, + 'filter-type:s' => { name => 'filter_type' }, + 'filter-namespace:s' => { name => 'filter_namespace' } }); - + return $self; } -sub check_options { - my ($self, %options) = @_; - $self->SUPER::check_options(%options); - - $self->change_macros(macros => ['warning_status', 'critical_status']); -} - sub manage_selection { my ($self, %options) = @_; - $self->{events} = {}; - my $results = $options{custom}->kubernetes_list_events(); $self->{global} = { normal => 0, warning => 0 }; - + $self->{events} = {}; foreach my $event (@{$results}) { if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' && $event->{type} !~ /$self->{option_results}->{filter_type}/) { @@ -178,7 +169,7 @@ sub manage_selection { } $self->{global}->{lc($event->{type})}++; - + $self->{events}->{$event->{metadata}->{uid}} = { name => $event->{metadata}->{name}, namespace => $event->{metadata}->{namespace}, diff --git a/src/cloud/kubernetes/mode/cronjobstatus.pm b/src/cloud/kubernetes/mode/cronjobstatus.pm index 418be041b..18fe569db 100644 --- a/src/cloud/kubernetes/mode/cronjobstatus.pm +++ b/src/cloud/kubernetes/mode/cronjobstatus.pm @@ -90,7 +90,8 @@ sub set_counters { { label => 'status', type => 2, set => { key_values => [ { name => 'active' }, { name => 'last_schedule_time' }, { name => 'name' }, - { name => 'namespace' } ], + { name => 'namespace' } + ], closure_custom_calc => $self->can('custom_status_calc'), closure_custom_output => $self->can('custom_status_output'), closure_custom_perfdata => $self->can('custom_status_perfdata'), diff --git a/src/cloud/kubernetes/mode/discoverynodes.pm b/src/cloud/kubernetes/mode/discovery.pm similarity index 53% rename from src/cloud/kubernetes/mode/discoverynodes.pm rename to src/cloud/kubernetes/mode/discovery.pm index eaed4ae7d..9a287bd1a 100644 --- a/src/cloud/kubernetes/mode/discoverynodes.pm +++ b/src/cloud/kubernetes/mode/discovery.pm @@ -18,7 +18,7 @@ # limitations under the License. # -package cloud::kubernetes::mode::discoverynodes; +package cloud::kubernetes::mode::discovery; use base qw(centreon::plugins::mode); @@ -31,7 +31,8 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - "prettify" => { name => 'prettify' }, + 'resource-type:s' => { name => 'resource_type' }, + 'prettify' => { name => 'prettify' } }); return $self; @@ -40,6 +41,60 @@ sub new { sub check_options { my ($self, %options) = @_; $self->SUPER::init(%options); + + if ($self->{option_results}->{resource_type} !~ /^node|namespace$/) { + $self->{output}->add_option_msg(short_msg => 'unknown resource type'); + $self->{output}->option_exit(); + } +} + +sub discovery_node { + my ($self, %options) = @_; + + my $nodes = $options{custom}->kubernetes_list_nodes(); + + my $disco_data = []; + foreach my $node (@$nodes) { + my %entry; + $entry{name} = $node->{metadata}->{name}; + $entry{uid} = $node->{metadata}->{uid}; + $entry{os_image} = $node->{status}->{nodeInfo}->{osImage}; + $entry{kubelet_version} = $node->{status}->{nodeInfo}->{kubeletVersion}; + if (defined($node->{metadata}->{labels}->{'node-role.kubernetes.io/control-plane'})) { + $entry{node_role} = "control-plane"; + } elsif (defined($node->{metadata}->{labels}->{'node-role.kubernetes.io/master'})) { + $entry{node_role} = "master"; + } else { + $entry{node_role} = "worker"; + } + + foreach my $address (@{$node->{status}->{addresses}}) { + $entry{internal_ip} = $address->{address} if ($address->{type} eq "InternalIP"); + $entry{external_ip} = $address->{address} if ($address->{type} eq "ExternalIP"); + $entry{hostname} = $address->{address} if ($address->{type} eq "Hostname"); + } + + push @$disco_data, \%entry; + } + + return $disco_data; +} + +sub discovery_namespace { + my ($self, %options) = @_; + + my $namespaces = $options{custom}->kubernetes_list_namespaces(); + + my $disco_data = []; + foreach my $namespace (@$namespaces) { + my %entry; + $entry{name} = $namespace->{metadata}->{name}; + $entry{uid} = $namespace->{metadata}->{uid}; + + push @$disco_data, \%entry; + } + + return $disco_data; } sub run { @@ -50,35 +105,21 @@ sub run { $disco_stats->{start_time} = time(); - my $nodes = $options{custom}->kubernetes_list_nodes(); - - foreach my $node (@{$nodes}) { - my %node; - $node{name} = $node->{metadata}->{name}; - $node{uid} = $node->{metadata}->{uid}; - $node{os_image} = $node->{status}->{nodeInfo}->{osImage}; - $node{kubelet_version} = $node->{status}->{nodeInfo}->{kubeletVersion}; - if (defined($node->{metadata}->{labels}->{'node-role.kubernetes.io/control-plane'})) { - $node{node_role} = "control-plane"; - } elsif (defined($node->{metadata}->{labels}->{'node-role.kubernetes.io/master'})) { - $node{node_role} = "master"; - } else { - $node{node_role} = "worker"; - } - - foreach my $address (@{$node->{status}->{addresses}}) { - $node{internal_ip} = $address->{address} if ($address->{type} eq "InternalIP"); - $node{external_ip} = $address->{address} if ($address->{type} eq "ExternalIP"); - $node{hostname} = $address->{address} if ($address->{type} eq "Hostname"); - } - - push @disco_data, \%node; + my $results = []; + if ($self->{option_results}->{resource_type} eq 'node') { + $results = $self->discovery_node( + custom => $options{custom} + ); + } elsif ($self->{option_results}->{resource_type} eq 'namespace') { + $results = $self->discovery_namespace( + custom => $options{custom} + ); } $disco_stats->{end_time} = time(); $disco_stats->{duration} = $disco_stats->{end_time} - $disco_stats->{start_time}; - $disco_stats->{discovered_items} = @disco_data; - $disco_stats->{results} = \@disco_data; + $disco_stats->{discovered_items} = scalar(@$results); + $disco_stats->{results} = $results; my $encoded_data; eval { @@ -91,7 +132,7 @@ sub run { if ($@) { $encoded_data = '{"code":"encode_error","message":"Cannot encode discovered data into JSON format"}'; } - + $self->{output}->output_add(short_msg => $encoded_data); $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1); $self->{output}->exit(); @@ -107,6 +148,10 @@ Nodes discovery. =over 8 +=item B<--resource-type> + +Choose the type of resources to discover (Can be: 'node', 'namespace'). + =item B<--prettify> Prettify JSON output. diff --git a/src/cloud/kubernetes/mode/listdeployments.pm b/src/cloud/kubernetes/mode/listdeployments.pm index 6e23978f2..fc468c40e 100644 --- a/src/cloud/kubernetes/mode/listdeployments.pm +++ b/src/cloud/kubernetes/mode/listdeployments.pm @@ -31,8 +31,8 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - "filter-name:s" => { name => 'filter_name' }, - "filter-namespace:s" => { name => 'filter_namespace' }, + 'filter-name:s' => { name => 'filter_name' }, + 'filter-namespace:s' => { name => 'filter_namespace' } }); return $self; @@ -63,7 +63,7 @@ sub manage_selection { $self->{deployments}->{$deployment->{metadata}->{uid}} = { uid => $deployment->{metadata}->{uid}, name => $deployment->{metadata}->{name}, - namespace => $deployment->{metadata}->{namespace}, + namespace => $deployment->{metadata}->{namespace} } } } @@ -73,14 +73,20 @@ sub run { $self->manage_selection(%options); foreach my $deployment (sort keys %{$self->{deployments}}) { - $self->{output}->output_add(long_msg => sprintf("[uid = %s] [name = %s] [namespace = %s]", - $self->{deployments}->{$deployment}->{uid}, - $self->{deployments}->{$deployment}->{name}, - $self->{deployments}->{$deployment}->{namespace})); + $self->{output}->output_add( + long_msg => sprintf( + '[uid: %s] [name: %s] [namespace: %s]', + $self->{deployments}->{$deployment}->{uid}, + $self->{deployments}->{$deployment}->{name}, + $self->{deployments}->{$deployment}->{namespace} + ) + ); } - $self->{output}->output_add(severity => 'OK', - short_msg => 'List deployments:'); + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List deployments:' + ); $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); $self->{output}->exit(); } @@ -99,7 +105,7 @@ sub disco_show { $self->{output}->add_disco_entry( uid => $self->{deployments}->{$deployment}->{uid}, name => $self->{deployments}->{$deployment}->{name}, - namespace => $self->{deployments}->{$deployment}->{namespace}, + namespace => $self->{deployments}->{$deployment}->{namespace} ); } } diff --git a/src/cloud/kubernetes/mode/listingresses.pm b/src/cloud/kubernetes/mode/listingresses.pm index 650449791..00f733e8f 100644 --- a/src/cloud/kubernetes/mode/listingresses.pm +++ b/src/cloud/kubernetes/mode/listingresses.pm @@ -73,14 +73,20 @@ sub run { $self->manage_selection(%options); foreach my $ingress (sort keys %{$self->{ingresses}}) { - $self->{output}->output_add(long_msg => sprintf("[uid = %s] [name = %s] [namespace = %s]", - $self->{ingresses}->{$ingress}->{uid}, - $self->{ingresses}->{$ingress}->{name}, - $self->{ingresses}->{$ingress}->{namespace})); + $self->{output}->output_add( + long_msg => sprintf( + '[uid: %s] [name: %s] [namespace: %s]', + $self->{ingresses}->{$ingress}->{uid}, + $self->{ingresses}->{$ingress}->{name}, + $self->{ingresses}->{$ingress}->{namespace} + ) + ); } - $self->{output}->output_add(severity => 'OK', - short_msg => 'List ingresses:'); + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List ingresses:' + ); $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); $self->{output}->exit(); } @@ -99,7 +105,7 @@ sub disco_show { $self->{output}->add_disco_entry( uid => $self->{ingresses}->{$ingress}->{uid}, name => $self->{ingresses}->{$ingress}->{name}, - namespace => $self->{ingresses}->{$ingress}->{namespace}, + namespace => $self->{ingresses}->{$ingress}->{namespace} ); } } diff --git a/src/cloud/kubernetes/mode/listnamespaces.pm b/src/cloud/kubernetes/mode/listnamespaces.pm index 3f82a2a01..d67951c7d 100644 --- a/src/cloud/kubernetes/mode/listnamespaces.pm +++ b/src/cloud/kubernetes/mode/listnamespaces.pm @@ -54,9 +54,9 @@ sub manage_selection { next; } - $self->{namespaces}->{$namespace->{metadata}->{uid}} = { + $self->{namespaces}->{ $namespace->{metadata}->{uid} } = { uid => $namespace->{metadata}->{uid}, - name => $namespace->{metadata}->{name}, + name => $namespace->{metadata}->{name} } } } @@ -66,13 +66,19 @@ sub run { $self->manage_selection(%options); foreach my $namespace (sort keys %{$self->{namespaces}}) { - $self->{output}->output_add(long_msg => sprintf("[uid = %s] [name = %s]", - $self->{namespaces}->{$namespace}->{uid}, - $self->{namespaces}->{$namespace}->{name})); + $self->{output}->output_add( + long_msg => sprintf( + '[uid: %s] [name: %s]', + $self->{namespaces}->{$namespace}->{uid}, + $self->{namespaces}->{$namespace}->{name} + ) + ); } - $self->{output}->output_add(severity => 'OK', - short_msg => 'List namespaces:'); + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List namespaces:' + ); $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); $self->{output}->exit(); } @@ -90,7 +96,7 @@ sub disco_show { foreach my $namespace (sort keys %{$self->{namespaces}}) { $self->{output}->add_disco_entry( uid => $self->{namespaces}->{$namespace}->{uid}, - name => $self->{namespaces}->{$namespace}->{name}, + name => $self->{namespaces}->{$namespace}->{name} ); } } diff --git a/src/cloud/kubernetes/plugin.pm b/src/cloud/kubernetes/plugin.pm index b5fe752f6..b2704855e 100644 --- a/src/cloud/kubernetes/plugin.pm +++ b/src/cloud/kubernetes/plugin.pm @@ -29,13 +29,12 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options); bless $self, $class; - $self->{version} = '1.0'; - %{$self->{modes}} = ( + $self->{modes} = { 'cluster-events' => 'cloud::kubernetes::mode::clusterevents', 'cronjob-status' => 'cloud::kubernetes::mode::cronjobstatus', 'daemonset-status' => 'cloud::kubernetes::mode::daemonsetstatus', 'deployment-status' => 'cloud::kubernetes::mode::deploymentstatus', - 'discovery-nodes' => 'cloud::kubernetes::mode::discoverynodes', + 'discovery' => 'cloud::kubernetes::mode::discovery', 'list-cronjobs' => 'cloud::kubernetes::mode::listcronjobs', 'list-daemonsets' => 'cloud::kubernetes::mode::listdaemonsets', 'list-deployments' => 'cloud::kubernetes::mode::listdeployments', @@ -54,11 +53,11 @@ sub new { 'pod-status' => 'cloud::kubernetes::mode::podstatus', 'replicaset-status' => 'cloud::kubernetes::mode::replicasetstatus', 'replicationcontroller-status' => 'cloud::kubernetes::mode::replicationcontrollerstatus', - 'statefulset-status' => 'cloud::kubernetes::mode::statefulsetstatus', - ); + 'statefulset-status' => 'cloud::kubernetes::mode::statefulsetstatus' + }; - $self->{custom_modes}{api} = 'cloud::kubernetes::custom::api'; - $self->{custom_modes}{kubectl} = 'cloud::kubernetes::custom::kubectl'; + $self->{custom_modes}->{api} = 'cloud::kubernetes::custom::api'; + $self->{custom_modes}->{kubectl} = 'cloud::kubernetes::custom::kubectl'; return $self; } diff --git a/src/contrib/collection/README.md b/src/contrib/collection/README.md new file mode 100644 index 000000000..566653343 --- /dev/null +++ b/src/contrib/collection/README.md @@ -0,0 +1,18 @@ +# Centren Collection modes +Centreon developed a method to scrap monitoring indicators, and It’s called **Collection**. + +This kind of mode is handy if you are in one of the following situations: +* You can't find an existing plugin to monitor what you want or fulfill your particular need. +* You need to gather data from a in-house, black box, or third-party application and transform it to be easier to analyze. +* Writing (or asking for) a dedicated plugin appears overkill because you simply want to monitor values and apply thresholds over them. + +You can find more information on [The Watch](https://thewatch.centreon.com/data-collection-6/centreon-plugins-discover-collection-modes-131), the Centreon User Community. + +Currently, Collection modes are available for: +* SNMP (check the [tutorial](https://thewatch.centreon.com/product-how-to-21/snmp-collection-tutorial-132)) +* SQL (check the [tutorial](https://thewatch.centreon.com/product-how-to-21/sql-collection-tutorial-134)) + +Feel free to share here the file that you have developed. + +* SNMP + * [moxa-iologik-collection.json](snmp/moxa-iologik-collection.json): to monitor the DI channel status (OID [diStatus](http://www.circitor.fr/Mibs/Html/M/MOXA-IO-E2210-MIB.php#DiEntry) .1.3.6.1.4.1.8691.10.2210.10.1.1.4 of MOXA ioLogik device \ No newline at end of file diff --git a/src/contrib/collection/snmp/moxa-iologik-collection.json b/src/contrib/collection/snmp/moxa-iologik-collection.json new file mode 100644 index 000000000..1c51ae50b --- /dev/null +++ b/src/contrib/collection/snmp/moxa-iologik-collection.json @@ -0,0 +1,46 @@ +{ + "mapping": { + "diStatus": { + "0": "Off", + "1": "On" + } + }, + "constants": { + "criticalStatus": "Off" + }, + "snmp": { + "tables": [ + { + "name": "diEntry", + "oid": ".1.3.6.1.4.1.8691.10.2210.10.1.1", + "used_instance": "\\.(\\d+)$", + "entries": [ + { "name": "diIndex", "oid": ".1.3.6.1.4.1.8691.10.2210.10.1.1.1"}, + { "name": "diStatus", "oid": ".1.3.6.1.4.1.8691.10.2210.10.1.1.4" , "map": "diStatus"} + ] + } + ] + }, + "selection_loop": [ + { + "name": "DI Channel identification", + "source": "%(snmp.tables.diEntry)", + "expand_tables": { + "diEntry": "%(snmp.tables.diEntry.[%(diEntry.instance)])" + }, + "critical": "%(diEntry.diStatus) =~ /%(constants.criticalStatus)/", + "formatting": { + "printf_msg": "Device '%s' status is '%s'", + "printf_var": [ + "%(diEntry.diIndex)", + "%(diEntry.diStatus)" + ], + "display_ok": true + } + } + ], + "formatting": { + "custom_message_global": "All DI Channels are OK", + "separator": "-" + } +} diff --git a/src/database/postgres/mode/hitratio.pm b/src/database/postgres/mode/hitratio.pm index 02ef471f8..643a9cd7c 100644 --- a/src/database/postgres/mode/hitratio.pm +++ b/src/database/postgres/mode/hitratio.pm @@ -77,18 +77,18 @@ sub run { my $new_datas = {}; $new_datas->{last_timestamp} = time(); my $result = $options{sql}->fetchall_arrayref(); - + $self->{output}->output_add( severity => 'OK', short_msg => "All databases hitratio are ok" ); - + foreach my $row (@{$result}) { - $new_datas->{$$row[2] . '_blks_hit'} = $row->[0]; - $new_datas->{$$row[2] . '_blks_read'} = $row->[1]; + $new_datas->{$row->[2] . '_blks_hit'} = $row->[0]; + $new_datas->{$row->[2] . '_blks_read'} = $row->[1]; if (defined($self->{option_results}->{exclude}) && $row->[2] !~ /$self->{option_results}->{exclude}/) { - $self->{output}->output_add(long_msg => "Skipping database '" . $$row[2] . '"'); + $self->{output}->output_add(long_msg => "Skipping database '" . $row->[2] . '"'); next; } @@ -96,16 +96,16 @@ sub run { my $old_blks_read = $self->{statefile_cache}->get(name => $row->[2] . '_blks_read'); next if (!defined($old_blks_hit) || !defined($old_blks_read)); - $old_blks_hit = 0 if ($$row[0] <= $old_blks_hit); - $old_blks_read = 0 if ($$row[1] <= $old_blks_read); + $old_blks_hit = 0 if ($row->[0] < $old_blks_hit); + $old_blks_read = 0 if ($row->[1] < $old_blks_read); $database_check++; my %prcts = (); - my $total_read_requests = $new_datas->{$$row[2] . '_blks_hit'} - $old_blks_hit; - my $total_read_disk = $new_datas->{$$row[2] . '_blks_read'} - $old_blks_read; + my $total_read_requests = $new_datas->{$row->[2] . '_blks_hit'} - $old_blks_hit; + my $total_read_disk = $new_datas->{$row->[2] . '_blks_read'} - $old_blks_read; $prcts{hitratio_now} = (($total_read_requests + $total_read_disk) == 0) ? 100 : $total_read_requests * 100 / ($total_read_requests + $total_read_disk); - $prcts{hitratio} = (($new_datas->{$$row[2] . '_blks_hit'} + + $new_datas->{$$row[2] . '_blks_read'}) == 0) ? 100 : $new_datas->{$$row[2] . '_blks_hit'} * 100 / ($new_datas->{$$row[2] . '_blks_hit'} + $new_datas->{$$row[2] . '_blks_read'}); - + $prcts{hitratio} = (($new_datas->{$row->[2] . '_blks_hit'} + + $new_datas->{$row->[2] . '_blks_read'}) == 0) ? 100 : $new_datas->{$row->[2] . '_blks_hit'} * 100 / ($new_datas->{$$row[2] . '_blks_hit'} + $new_datas->{$row->[2] . '_blks_read'}); + my $exit_code = $self->{perfdata}->threshold_check(value => $prcts{'hitratio' . ((defined($self->{option_results}->{lookback})) ? '' : '_now' )}, threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]); $self->{output}->output_add( long_msg => sprintf( @@ -113,7 +113,7 @@ sub run { $row->[2], $prcts{'hitratio' . ((defined($self->{option_results}->{lookback})) ? '' : '_now')} ) ); - + if (!$self->{output}->is_status(value => $exit_code, compare => 'ok', litteral => 1)) { $self->{output}->output_add( severity => $exit_code, @@ -139,7 +139,7 @@ sub run { min => 0, max => 100 ); } - + $self->{statefile_cache}->write(data => $new_datas); if (!defined($old_timestamp)) { $self->{output}->output_add( diff --git a/src/database/postgres/mode/querytime.pm b/src/database/postgres/mode/querytime.pm index e116ef2bf..97a1f2611 100644 --- a/src/database/postgres/mode/querytime.pm +++ b/src/database/postgres/mode/querytime.pm @@ -93,25 +93,24 @@ sub run { ); my $dbquery = {}; while ((my $row = $options{sql}->fetchrow_hashref())) { - if (defined($self->{option_results}->{exclude}) && $row->{datname} !~ /$self->{option_results}->{exclude}/) { - next; - } + next if (defined($self->{option_results}->{exclude}) && $self->{option_results}->{exclude} ne '' && $row->{datname} =~ /$self->{option_results}->{exclude}/); if (!defined($dbquery->{$row->{datname}})) { $dbquery->{ $row->{datname} } = { total => 0, code => {} }; } next if (!defined($row->{datid}) || $row->{datid} eq ''); # No joint - if (defined($self->{option_results}->{exclude_user}) && $row->{usename} !~ /$self->{option_results}->{exclude_user}/) { - next; - } + next if (defined($self->{option_results}->{exclude_user}) && $self->{option_results}->{exclude_user} ne '' && $row->{usename} =~ /$self->{option_results}->{exclude_user}/); my $exit_code = $self->{perfdata}->threshold_check(value => $row->{seconds}, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]); if (!$self->{output}->is_status(value => $exit_code, compare => 'ok', litteral => 1)) { $self->{output}->output_add( long_msg => sprintf( - "Request from client '%s' too long (%d sec) on database '%s': %s", - $row->{client_addr}, $row->{seconds}, $row->{datname}, $row->{current_query} + "Request from client '%s' too long (%s sec) on database '%s': %s", + defined($row->{client_addr}) ? $row->{client_addr} : 'unknown', + defined($row->{seconds}) ? $row->{seconds} : '-', + defined($row->{datname}) ? $row->{datname} : '-', + defined($row->{current_query}) ? $row->{current_query} : '-' ) ); $dbquery->{ $row->{datname} }->{total}++; diff --git a/src/hardware/pdu/raritan/snmp/mode/components/resources.pm b/src/hardware/pdu/raritan/snmp/mode/components/resources.pm index 1009d0a14..f86e6087b 100644 --- a/src/hardware/pdu/raritan/snmp/mode/components/resources.pm +++ b/src/hardware/pdu/raritan/snmp/mode/components/resources.pm @@ -67,52 +67,66 @@ my %map_state = ( 22 => 'i1OpenFault', 23 => 'i1ShortFault', 24 => 'i2OpenFault', 25 => 'i2ShortFault', 26 => 'fault', 27 => 'warning', 28 => 'critical', - 29 => 'selfTest', + 29 => 'selfTest' ); $mapping = { + external_label => { + Label => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.4' } # externalSensorName + }, + external => { + Unit => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.16', map => \%map_units }, # externalSensorUnits + Decimal => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.17' }, # externalSensorDecimalDigits + LowerCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.31' }, # externalSensorLowerCriticalThreshold + LowerWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.32' }, # externalSensorLowerWarningThreshold + UpperCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.33' }, # externalSensorUpperCriticalThreshold + UpperWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.34' }, # externalSensorUpperWarningThreshold + EnabledThresholds => { oid => '.1.3.6.1.4.1.13742.6.3.6.3.1.35' }, # externalSensorEnabledThresholds + State => { oid => '.1.3.6.1.4.1.13742.6.5.5.3.1.3', map => \%map_state }, # measurementsExternalSensorState + Value => { oid => '.1.3.6.1.4.1.13742.6.5.5.3.1.4' } # measurementsExternalSensorValue + }, inlet_label => { - Label => { oid => '.1.3.6.1.4.1.13742.6.3.3.3.1.2' }, # inletLabel + Label => { oid => '.1.3.6.1.4.1.13742.6.3.3.3.1.2' } # inletLabel }, inlet => { Unit => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.6', map => \%map_units }, # inletSensorUnits Decimal => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.7' }, # inletSensorDecimalDigits - EnabledThresholds => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.25' }, # inletSensorEnabledThresholds LowerCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.21' }, # inletSensorLowerCriticalThreshold LowerWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.22' }, # inletSensorLowerWarningThreshold UpperCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.23' }, # inletSensorUpperCriticalThreshold UpperWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.24' }, # inletSensorUpperWarningThreshold + EnabledThresholds => { oid => '.1.3.6.1.4.1.13742.6.3.3.4.1.25' }, # inletSensorEnabledThresholds State => { oid => '.1.3.6.1.4.1.13742.6.5.2.3.1.3', map => \%map_state }, # measurementsInletSensorState - Value => { oid => '.1.3.6.1.4.1.13742.6.5.2.3.1.4' }, # measurementsInletSensorValue + Value => { oid => '.1.3.6.1.4.1.13742.6.5.2.3.1.4' } # measurementsInletSensorValue }, outlet_label => { - Label => { oid => '.1.3.6.1.4.1.13742.6.3.5.3.1.2' }, # outletLabel + Label => { oid => '.1.3.6.1.4.1.13742.6.3.5.3.1.2' } # outletLabel }, outlet => { Unit => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.6', map => \%map_units }, # outletSensorUnits Decimal => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.7' }, # outletSensorDecimalDigits - EnabledThresholds => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.25' }, # outletSensorEnabledThresholds LowerCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.21' }, # outletSensorLowerCriticalThreshold LowerWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.22' }, # outletSensorLowerWarningThreshold UpperCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.23' }, # outletSensorUpperCriticalThreshold UpperWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.24' }, # outletSensorUpperWarningThreshold + EnabledThresholds => { oid => '.1.3.6.1.4.1.13742.6.3.5.4.1.25' }, # outletSensorEnabledThresholds State => { oid => '.1.3.6.1.4.1.13742.6.5.4.3.1.3', map => \%map_state }, # measurementsOutletSensorState - Value => { oid => '.1.3.6.1.4.1.13742.6.5.4.3.1.4' }, # measurementsOutletSensorValue + Value => { oid => '.1.3.6.1.4.1.13742.6.5.4.3.1.4' } # measurementsOutletSensorValue }, ocprot_label => { - Label => { oid => '.1.3.6.1.4.1.13742.6.3.4.3.1.2' }, # overCurrentProtectorLabel + Label => { oid => '.1.3.6.1.4.1.13742.6.3.4.3.1.2' } # overCurrentProtectorLabel }, ocprot => { Unit => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.6', map => \%map_units }, # overCurrentProtectorSensorUnits Decimal => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.7' }, # overCurrentProtectorSensorDecimalDigits - EnabledThresholds => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.25' }, # overCurrentProtectorSensorEnabledThresholds LowerCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.21' }, # overCurrentProtectorSensorLowerCriticalThreshold LowerWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.22' }, # overCurrentProtectorSensorLowerWarningThreshold UpperCriticalThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.23' }, # overCurrentProtectorSensorUpperCriticalThreshold UpperWarningThreshold => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.24' }, # overCurrentProtectorSensorUpperWarningThreshold + EnabledThresholds => { oid => '.1.3.6.1.4.1.13742.6.3.4.4.1.25' }, # overCurrentProtectorSensorEnabledThresholds State => { oid => '.1.3.6.1.4.1.13742.6.5.3.3.1.3', map => \%map_state }, # measurementsOverCurrentProtectorSensorState - Value => { oid => '.1.3.6.1.4.1.13742.6.5.3.3.1.4' }, # measurementsOverCurrentProtectorSensorValue - }, + Value => { oid => '.1.3.6.1.4.1.13742.6.5.3.3.1.4' } # measurementsOverCurrentProtectorSensorValue + } }; %raritan_type = ( @@ -130,7 +144,7 @@ $mapping = { inletPhaseSyncAngle => 38, inletPhaseSync => 39, operatingState => 40, activeInlet => 41, illuminance => 42, doorContact => 43, tamperDetection => 44, motionDetection => 45, i1smpsStatus => 46, - i2smpsStatus => 47, switchStatus => 48, + i2smpsStatus => 47, switchStatus => 48 ); %map_type = ( @@ -187,55 +201,55 @@ $thresholds = { ['belowLowerCritical', 'CRITICAL'], ['belowLowerWarning', 'WARNING'], ['aboveUpperWarning', 'WARNING'], - ['aboveUpperCritical', 'CRITICAL'], + ['aboveUpperCritical', 'CRITICAL'] ], onoff => [ ['unavailable', 'UNKNOWN'], ['on', 'OK'], - ['off', 'OK'], + ['off', 'OK'] ], contact => [ ['unavailable', 'UNKNOWN'], ['open', 'OK'], - ['closed', 'OK'], + ['closed', 'OK'] ], alarm => [ ['unavailable', 'UNKNOWN'], ['normal', 'OK'], ['alarmed', 'CRITICAL'], ['selfTest', 'OK'], - ['fail', 'CRITICAL'], + ['fail', 'CRITICAL'] ], fault => [ ['unavailable', 'UNKNOWN'], ['ok', 'OK'], - ['fault', 'CRITICAL'], + ['fault', 'CRITICAL'] ], powerQuality => [ ['unavailable', 'UNKNOWN'], ['normal', 'OK'], ['warning', 'WARNING'], - ['critical', 'CRITICAL'], + ['critical', 'CRITICAL'] ], inletPhaseSync => [ ['unavailable', 'UNKNOWN'], ['inSync', 'OK'], - ['outOfSync', 'CRITICAL'], + ['outOfSync', 'CRITICAL'] ], operatingState => [ ['unavailable', 'UNKNOWN'], ['normal', 'OK'], ['standby', 'OK'], - ['off', 'CRITICAL'], + ['off', 'CRITICAL'] ], activeInlet => [ ['unavailable', 'UNKNOWN'], ['one', 'OK'], ['two', 'OK'], - ['none', 'WARNING'], + ['none', 'WARNING'] ], motionDetection => [ - ['unavailable', 'UNKNOWN'], + ['unavailable', 'UNKNOWN'] ], switchStatus => [ ['unavailable', 'UNKNOWN'], @@ -243,8 +257,8 @@ $thresholds = { ['i1OpenFault', 'WARNING'], ['i1ShortFault', 'WARNING'], ['i2OpenFault', 'WARNING'], - ['i2ShortFault', 'WARNING'], - ], + ['i2ShortFault', 'WARNING'] + ] }; 1; diff --git a/src/hardware/pdu/raritan/snmp/mode/components/sensor.pm b/src/hardware/pdu/raritan/snmp/mode/components/sensor.pm index bae094cce..09b96fa0b 100644 --- a/src/hardware/pdu/raritan/snmp/mode/components/sensor.pm +++ b/src/hardware/pdu/raritan/snmp/mode/components/sensor.pm @@ -42,22 +42,38 @@ sub load { sub check { my ($self, %options) = @_; - + foreach my $component (sort keys %raritan_type) { my $long_msg = 0; next if ($component !~ /$options{component}/); - $self->{components}->{$component} = {name => $component, total => 0, skip => 0}; + $self->{components}->{$component} = { name => $component, total => 0, skip => 0 }; next if ($self->check_filter(section => $component)); - + my $instance_type = $raritan_type{$component}; my $value_type = $map_type{$instance_type}; foreach my $oid ($self->{snmp}->oid_lex_sort(keys %{$self->{results}})) { - next if ($oid !~ /^$mapping->{$options{type}}->{State}->{oid}\.(\d+)\.(\d+)\.$instance_type$/); - my $instance = $1 . '.' . $2 . '.' . $instance_type; - my $result = $self->{snmp}->map_instance(mapping => $mapping->{$options{type}}, results => $self->{results}, instance => $instance); - my $result2 = $self->{snmp}->map_instance(mapping => $mapping->{$options{type} . '_label'}, results => $self->{results}, instance => $1 . '.' . $2); - $instance = defined($result2->{Label}) && $result2->{Label} ne '' ? $result2->{Label} : $1 . '.' . $2; + my ($pduId, $fullSensorId, $result); + if ($options{type} eq 'external') { + next if ($oid !~ /^$mapping->{ $options{type} }->{State}->{oid}\./); + $oid =~ /^$mapping->{ $options{type} }->{State}->{oid}\.(\d+)\.(\d+)$/; + $pduId = $1; + $fullSensorId = $1 . '.' . $2; + next if ($self->{externalSensorType}->{$fullSensorId} != $instance_type); + + $result = $self->{snmp}->map_instance(mapping => $mapping->{$options{type}}, results => $self->{results}, instance => $fullSensorId); + } else { + next if ($oid !~ /^$mapping->{ $options{type} }->{State}->{oid}\.(\d+)\.(\d+)\.$instance_type$/); + + $pduId = $1; + $fullSensorId = $1 . '.' . $2; + my $fullInstanceId = $1 . '.' . $2 . '.' . $instance_type; + $result = $self->{snmp}->map_instance(mapping => $mapping->{$options{type}}, results => $self->{results}, instance => $fullInstanceId); + } + + my $pduName = $self->{pduNames}->{$pduId}; + my $result2 = $self->{snmp}->map_instance(mapping => $mapping->{$options{type} . '_label'}, results => $self->{results}, instance => $fullSensorId); + my $instance = defined($result2->{Label}) && $result2->{Label} ne '' ? $result2->{Label} : $fullSensorId; next if ($self->check_filter(section => $component, instance => $instance)); @@ -74,9 +90,15 @@ sub check { } $self->{output}->output_add( long_msg => sprintf( - "'%s' %s state is '%s' [instance: %s, value: %s, unit: %s, label: %s]", - $instance, $component, $result->{State}, - $instance, $value, $result->{Unit}->{unit}, $result2->{Label} + "'%s' %s state is '%s' [instance: %s, value: %s, unit: %s, label: %s, pdu: %s]", + $instance, + $component, + $result->{State}, + $instance, + $value, + $result->{Unit}->{unit}, + $result2->{Label}, + $pduName ) ); my $exit = $self->get_severity( @@ -93,55 +115,56 @@ sub check { ); } - if ($value =~ /[0-9]/) { - next if ($value =~ /^0$/ && $result->{Unit}->{unit} eq ''); - my ($exit2, $warn, $crit, $checked) = $self->get_severity_numeric(section => $component, instance => $instance, value => $value); - if ($checked == 0) { - $result->{EnabledThresholds} = oct("0b". unpack('b*', $result->{EnabledThresholds})); - my $warn_th; - $warn_th = ($result->{LowerWarningThreshold} * 10 ** -int($result->{Decimal})) . ':' if (($result->{EnabledThresholds} & (1 << 1))); - if (($result->{EnabledThresholds} & (1 << 2))) { - if (defined($warn_th)) { - $warn_th .= ($result->{UpperWarningThreshold} * 10 ** -int($result->{Decimal})); - } else { - $warn_th = '~:' . ($result->{UpperWarningThreshold} * 10 ** -int($result->{Decimal})); - } - } - my $crit_th; - $crit_th = ($result->{LowerCriticalThreshold} * 10 ** -int($result->{Decimal})) . ':' if (($result->{EnabledThresholds} & (1 << 0))); - if (($result->{EnabledThresholds} & (1 << 3))) { - if (defined($crit_th)) { - $crit_th .= ($result->{UpperCriticalThreshold} * 10 ** -int($result->{Decimal})); - } else { - $crit_th = '~:' . ($result->{UpperCriticalThreshold} * 10 ** -int($result->{Decimal})); - } - } - $self->{perfdata}->threshold_validate(label => 'warning-' . $component . '-instance-' . $instance, value => $warn_th); - $self->{perfdata}->threshold_validate(label => 'critical-' . $component . '-instance-' . $instance, value => $crit_th); - $warn = $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $component . '-instance-' . $instance); - $crit = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $component . '-instance-' . $instance); - } - if (!$self->{output}->is_status(value => $exit2, compare => 'ok', litteral => 1)) { - $self->{output}->output_add( - severity => $exit2, - short_msg => sprintf( - "'%s' %s value is %s %s", - $instance, $component, $value, $result->{Unit}->{unit} - ) - ); - } + next if ($value !~ /[0-9]/); + next if ($value =~ /^0$/ && $result->{Unit}->{unit} eq ''); - my $nunit = (defined($result->{Unit}->{nunit}) ? $result->{Unit}->{nunit} : lc($result->{Unit}->{unit})); + my ($exit2, $warn, $crit, $checked) = $self->get_severity_numeric(section => $component, instance => $instance, value => $value); + if ($checked == 0) { + $result->{EnabledThresholds} = oct('0b'. unpack('b*', $result->{EnabledThresholds})); + my $warn_th; + $warn_th = ($result->{LowerWarningThreshold} * 10 ** -int($result->{Decimal})) . ':' if (($result->{EnabledThresholds} & (1 << 1))); + if (($result->{EnabledThresholds} & (1 << 2))) { + if (defined($warn_th)) { + $warn_th .= ($result->{UpperWarningThreshold} * 10 ** -int($result->{Decimal})); + } else { + $warn_th = '~:' . ($result->{UpperWarningThreshold} * 10 ** -int($result->{Decimal})); + } + } + my $crit_th; + $crit_th = ($result->{LowerCriticalThreshold} * 10 ** -int($result->{Decimal})) . ':' if (($result->{EnabledThresholds} & (1 << 0))); + if (($result->{EnabledThresholds} & (1 << 3))) { + if (defined($crit_th)) { + $crit_th .= ($result->{UpperCriticalThreshold} * 10 ** -int($result->{Decimal})); + } else { + $crit_th = '~:' . ($result->{UpperCriticalThreshold} * 10 ** -int($result->{Decimal})); + } + } + $self->{perfdata}->threshold_validate(label => 'warning-' . $component . '-instance-' . $instance, value => $warn_th); + $self->{perfdata}->threshold_validate(label => 'critical-' . $component . '-instance-' . $instance, value => $crit_th); + $warn = $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $component . '-instance-' . $instance); + $crit = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $component . '-instance-' . $instance); + } - $self->{output}->perfdata_add( - label => $component, unit => $result->{Unit}->{unit}, - nlabel => 'hardware.sensor.' . $options{type} . '.' . lc($component) . '.' . $nunit, - instances => $instance, - value => $value, - warning => $warn, - critical => $crit + if (!$self->{output}->is_status(value => $exit2, compare => 'ok', litteral => 1)) { + $self->{output}->output_add( + severity => $exit2, + short_msg => sprintf( + "'%s' %s value is %s %s", + $instance, $component, $value, $result->{Unit}->{unit} + ) ); } + + my $nunit = (defined($result->{Unit}->{nunit}) ? $result->{Unit}->{nunit} : lc($result->{Unit}->{unit})); + + $self->{output}->perfdata_add( + nlabel => 'hardware.sensor.' . $options{type} . '.' . lc($component) . '.' . $nunit, + unit => $result->{Unit}->{unit}, + instances => [$pduName, $instance], + value => $value, + warning => $warn, + critical => $crit + ); } } } diff --git a/src/hardware/pdu/raritan/snmp/mode/externalsensors.pm b/src/hardware/pdu/raritan/snmp/mode/externalsensors.pm new file mode 100644 index 000000000..419244cb1 --- /dev/null +++ b/src/hardware/pdu/raritan/snmp/mode/externalsensors.pm @@ -0,0 +1,147 @@ +# +# Copyright 2023 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 hardware::pdu::raritan::snmp::mode::externalsensors; + +use base qw(centreon::plugins::templates::hardware); + +use strict; +use warnings; +use hardware::pdu::raritan::snmp::mode::components::resources qw($thresholds %raritan_type); + +sub set_system { + my ($self, %options) = @_; + + $self->{cb_threshold_numeric_check_section_option} = 'check_numeric_section_option'; + + $self->{cb_hook2} = 'snmp_execute'; + + $self->{thresholds} = $thresholds; + + $self->{components_path} = 'hardware::pdu::raritan::snmp::mode::components'; +} + +sub snmp_execute { + my ($self, %options) = @_; + + $self->{snmp} = $options{snmp}; + $self->{results} = $self->{snmp}->get_multiple_table(oids => $self->{request}, return_type => 1); + + my $oid_pduName = '.1.3.6.1.4.1.13742.6.3.2.2.1.13'; + my $snmp_result = $self->{snmp}->get_table(oid => $oid_pduName, return_type => 1); + $self->{pduNames} = {}; + foreach (keys %$snmp_result) { + /\.(\d+)$/; + $self->{pduNames}->{$1} = $snmp_result->{$_}; + } + + my $oid_externalSensorType = '.1.3.6.1.4.1.13742.6.3.6.3.1.2'; + $snmp_result = $self->{snmp}->get_table(oid => $oid_externalSensorType, return_type => 1); + $self->{externalSensorType} = {}; + foreach (keys %$snmp_result) { + /\.(\d+\.\d+)$/; + $self->{externalSensorType}->{$1} = $snmp_result->{$_}; + } +} + +sub check_numeric_section_option { + my ($self, %options) = @_; + + if (!defined($raritan_type{$options{section}})) { + $self->{output}->add_option_msg(short_msg => "Wrong $options{option_name} option '" . $options{option_value} . "'."); + $self->{output}->option_exit(); + } +} + +sub load_components { + my ($self, %options) = @_; + + my $mod_name = $self->{components_path} . "::sensor"; + centreon::plugins::misc::mymodule_load( + output => $self->{output}, module => $mod_name, + error_msg => "Cannot load module '$mod_name'." + ); + my $func = $mod_name->can('load'); + $func->($self, type => 'external'); + + $self->{loaded} = 1; +} + +sub exec_components { + my ($self, %options) = @_; + + my $mod_name = $self->{components_path} . "::sensor"; + my $func = $mod_name->can('check'); + $func->($self, component => $self->{option_results}->{component}, type => 'external'); +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, no_absent => 1, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +1; + +__END__ + +=head1 MODE + +Check external sensors. + +=over 8 + +=item B<--component> + +Which component to check (Default: '.*'). + +=item B<--filter> + +Exclude some parts (comma seperated list) (Example: --filter=airPressure --filter=rmsVoltage) +Can also exclude specific instance: --filter=rmsVoltage,I1 + +=item B<--no-component> + +Return an error if no compenents are checked. +If total (with skipped) is 0. (Default: 'critical' returns). + +=item B<--threshold-overload> + +Set to overload default threshold values (syntax: section,[instance,]status,regexp) +It used before default thresholds (order stays). +Example: --threshold-overload='powerQuality,CRITICAL,^(?!(normal)$)' + +=item B<--warning> + +Set warning threshold for temperatures (syntax: type,instance,threshold) +Example: --warning='powerQuality,.*,30' + +=item B<--critical> + +Set critical threshold for temperatures (syntax: type,instance,threshold) +Example: --critical='powerQuality,.*,40' + +=back + +=cut diff --git a/src/hardware/pdu/raritan/snmp/mode/inletsensors.pm b/src/hardware/pdu/raritan/snmp/mode/inletsensors.pm index 6dcfb6708..585e41d57 100644 --- a/src/hardware/pdu/raritan/snmp/mode/inletsensors.pm +++ b/src/hardware/pdu/raritan/snmp/mode/inletsensors.pm @@ -43,6 +43,14 @@ sub snmp_execute { $self->{snmp} = $options{snmp}; $self->{results} = $self->{snmp}->get_multiple_table(oids => $self->{request}, return_type => 1); + + my $oid_pduName = '.1.3.6.1.4.1.13742.6.3.2.2.1.13'; + my $snmp_result = $self->{snmp}->get_table(oid => $oid_pduName, return_type => 1); + $self->{pduNames} = {}; + foreach (keys %$snmp_result) { + /\.(\d+)$/; + $self->{pduNames}->{$1} = $snmp_result->{$_}; + } } sub check_numeric_section_option { @@ -58,8 +66,10 @@ sub load_components { my ($self, %options) = @_; my $mod_name = $self->{components_path} . "::sensor"; - centreon::plugins::misc::mymodule_load(output => $self->{output}, module => $mod_name, - error_msg => "Cannot load module '$mod_name'."); + centreon::plugins::misc::mymodule_load( + output => $self->{output}, module => $mod_name, + error_msg => "Cannot load module '$mod_name'." + ); my $func = $mod_name->can('load'); $func->($self, type => 'inlet'); @@ -76,12 +86,10 @@ sub exec_components { sub new { my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options, no_absent => 1); + my $self = $class->SUPER::new(package => __PACKAGE__, %options, no_absent => 1, force_new_perfdata => 1); bless $self, $class; - $options{options}->add_options(arguments => - { - }); + $options{options}->add_options(arguments => {}); return $self; } @@ -128,4 +136,4 @@ Example: --critical='powerQuality,.*,40' =back -=cut \ No newline at end of file +=cut diff --git a/src/hardware/pdu/raritan/snmp/mode/ocprotsensors.pm b/src/hardware/pdu/raritan/snmp/mode/ocprotsensors.pm index d1f4fa6a9..acaeb3764 100644 --- a/src/hardware/pdu/raritan/snmp/mode/ocprotsensors.pm +++ b/src/hardware/pdu/raritan/snmp/mode/ocprotsensors.pm @@ -43,6 +43,14 @@ sub snmp_execute { $self->{snmp} = $options{snmp}; $self->{results} = $self->{snmp}->get_multiple_table(oids => $self->{request}, return_type => 1); + + my $oid_pduName = '.1.3.6.1.4.1.13742.6.3.2.2.1.13'; + my $snmp_result = $self->{snmp}->get_table(oid => $oid_pduName, return_type => 1); + $self->{pduNames} = {}; + foreach (keys %$snmp_result) { + /\.(\d+)$/; + $self->{pduNames}->{$1} = $snmp_result->{$_}; + } } sub check_numeric_section_option { @@ -58,8 +66,10 @@ sub load_components { my ($self, %options) = @_; my $mod_name = $self->{components_path} . "::sensor"; - centreon::plugins::misc::mymodule_load(output => $self->{output}, module => $mod_name, - error_msg => "Cannot load module '$mod_name'."); + centreon::plugins::misc::mymodule_load( + output => $self->{output}, module => $mod_name, + error_msg => "Cannot load module '$mod_name'." + ); my $func = $mod_name->can('load'); $func->($self, type => 'ocprot'); @@ -76,11 +86,10 @@ sub exec_components { sub new { my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options, no_absent => 1); + my $self = $class->SUPER::new(package => __PACKAGE__, %options, no_absent => 1, force_new_perfdata => 1); bless $self, $class; - $options{options}->add_options(arguments => { - }); + $options{options}->add_options(arguments => {}); return $self; } diff --git a/src/hardware/pdu/raritan/snmp/mode/outletsensors.pm b/src/hardware/pdu/raritan/snmp/mode/outletsensors.pm index 976a5b4e8..69d3b4296 100644 --- a/src/hardware/pdu/raritan/snmp/mode/outletsensors.pm +++ b/src/hardware/pdu/raritan/snmp/mode/outletsensors.pm @@ -43,6 +43,14 @@ sub snmp_execute { $self->{snmp} = $options{snmp}; $self->{results} = $self->{snmp}->get_multiple_table(oids => $self->{request}, return_type => 1); + + my $oid_pduName = '.1.3.6.1.4.1.13742.6.3.2.2.1.13'; + my $snmp_result = $self->{snmp}->get_table(oid => $oid_pduName, return_type => 1); + $self->{pduNames} = {}; + foreach (keys %$snmp_result) { + /\.(\d+)$/; + $self->{pduNames}->{$1} = $snmp_result->{$_}; + } } sub check_numeric_section_option { @@ -58,8 +66,10 @@ sub load_components { my ($self, %options) = @_; my $mod_name = $self->{components_path} . "::sensor"; - centreon::plugins::misc::mymodule_load(output => $self->{output}, module => $mod_name, - error_msg => "Cannot load module '$mod_name'."); + centreon::plugins::misc::mymodule_load( + output => $self->{output}, module => $mod_name, + error_msg => "Cannot load module '$mod_name'." + ); my $func = $mod_name->can('load'); $func->($self, type => 'outlet'); @@ -76,11 +86,10 @@ sub exec_components { sub new { my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options); + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); bless $self, $class; - $options{options}->add_options(arguments => { - }); + $options{options}->add_options(arguments => {}); return $self; } diff --git a/src/hardware/pdu/raritan/snmp/plugin.pm b/src/hardware/pdu/raritan/snmp/plugin.pm index 49b8ffaa2..146bee466 100644 --- a/src/hardware/pdu/raritan/snmp/plugin.pm +++ b/src/hardware/pdu/raritan/snmp/plugin.pm @@ -29,12 +29,12 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options); bless $self, $class; - $self->{version} = '1.0'; - %{$self->{modes}} = ( + $self->{modes} = { + 'external-sensors' => 'hardware::pdu::raritan::snmp::mode::externalsensors', 'inlet-sensors' => 'hardware::pdu::raritan::snmp::mode::inletsensors', 'outlet-sensors' => 'hardware::pdu::raritan::snmp::mode::outletsensors', - 'ocprot-sensors' => 'hardware::pdu::raritan::snmp::mode::ocprotsensors', - ); + 'ocprot-sensors' => 'hardware::pdu::raritan::snmp::mode::ocprotsensors' + }; return $self; } diff --git a/src/hardware/server/dell/vxm/restapi/custom/api.pm b/src/hardware/server/dell/vxm/restapi/custom/api.pm index 8c10159f0..f7da90bd2 100644 --- a/src/hardware/server/dell/vxm/restapi/custom/api.pm +++ b/src/hardware/server/dell/vxm/restapi/custom/api.pm @@ -43,6 +43,7 @@ sub new { $options{options}->add_options(arguments => { 'api-username:s' => { name => 'api_username' }, 'api-password:s' => { name => 'api_password' }, + 'api-version:s' => { name => 'api_version' }, 'hostname:s' => { name => 'hostname' }, 'port:s' => { name => 'port' }, 'proto:s' => { name => 'proto' }, @@ -77,6 +78,7 @@ sub check_options { $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 50; $self->{api_username} = (defined($self->{option_results}->{api_username})) ? $self->{option_results}->{api_username} : ''; $self->{api_password} = (defined($self->{option_results}->{api_password})) ? $self->{option_results}->{api_password} : ''; + $self->{api_version} = defined($self->{option_results}->{api_version}) && $self->{option_results}->{api_version} ne '' ? $self->{option_results}->{api_version} : '2'; $self->{unknown_http_status} = (defined($self->{option_results}->{unknown_http_status})) ? $self->{option_results}->{unknown_http_status} : '%{http_code} < 200 or %{http_code} >= 300'; $self->{warning_http_status} = (defined($self->{option_results}->{warning_http_status})) ? $self->{option_results}->{warning_http_status} : ''; $self->{critical_http_status} = (defined($self->{option_results}->{critical_http_status})) ? $self->{option_results}->{critical_http_status} : ''; @@ -138,7 +140,7 @@ sub request { $self->settings(); my $content = $self->{http}->request( - url_path => $options{endpoint}, + url_path => '/rest/vxm/v' . $self->{api_version} . $options{endpoint}, unknown_status => $self->{unknown_http_status}, warning_status => $self->{warning_http_status}, critical_status => $self->{critical_http_status} @@ -195,6 +197,10 @@ API username. API password. +=item B<--api-version> + +API version (Default: 2). + =item B<--timeout> Set timeout in seconds (Default: 50). diff --git a/src/hardware/server/dell/vxm/restapi/mode/chassis.pm b/src/hardware/server/dell/vxm/restapi/mode/chassis.pm index 875effc3d..8d64d2cb5 100644 --- a/src/hardware/server/dell/vxm/restapi/mode/chassis.pm +++ b/src/hardware/server/dell/vxm/restapi/mode/chassis.pm @@ -128,7 +128,7 @@ sub new { sub manage_selection { my ($self, %options) = @_; - my $results = $options{custom}->request(endpoint => '/rest/vxm/v1/chassis'); + my $results = $options{custom}->request(endpoint => '/chassis'); $self->{global} = { num_chassis => 0, unhealthy => 0 }; $self->{chassis} = {}; diff --git a/src/hardware/server/dell/vxm/restapi/mode/discovery.pm b/src/hardware/server/dell/vxm/restapi/mode/discovery.pm index f1547eb5b..a0f354e91 100644 --- a/src/hardware/server/dell/vxm/restapi/mode/discovery.pm +++ b/src/hardware/server/dell/vxm/restapi/mode/discovery.pm @@ -55,7 +55,7 @@ sub check_options { sub discovery_host { my ($self, %options) = @_; - my $hosts = $options{custom}->request(endpoint => '/rest/vxm/v1/hosts'); + my $hosts = $options{custom}->request(endpoint => '/hosts'); my $disco_data = []; foreach my $host (@$hosts) { diff --git a/src/hardware/server/dell/vxm/restapi/mode/hosts.pm b/src/hardware/server/dell/vxm/restapi/mode/hosts.pm index ae7012a96..1fc446aa6 100644 --- a/src/hardware/server/dell/vxm/restapi/mode/hosts.pm +++ b/src/hardware/server/dell/vxm/restapi/mode/hosts.pm @@ -165,7 +165,7 @@ sub new { sub manage_selection { my ($self, %options) = @_; - my $results = $options{custom}->request(endpoint => '/rest/vxm/v1/hosts'); + my $results = $options{custom}->request(endpoint => '/hosts'); $self->{global} = { num_hosts => 0, unhealthy => 0 }; $self->{hosts} = {}; diff --git a/src/hardware/server/hp/ilo/restapi/custom/api.pm b/src/hardware/server/hp/ilo/restapi/custom/api.pm index ee2bd25de..f8523a0f2 100644 --- a/src/hardware/server/hp/ilo/restapi/custom/api.pm +++ b/src/hardware/server/hp/ilo/restapi/custom/api.pm @@ -209,6 +209,7 @@ sub request_api { $self->{output}->add_option_msg(short_msg => "Error while retrieving data (add --debug option for detailed message)"); $self->{output}->option_exit(); } + if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { $self->{output}->add_option_msg(short_msg => 'api request error: ' . (defined($decoded->{type}) ? $decoded->{type} : 'unknown')); $self->{output}->option_exit(); diff --git a/src/network/cisco/wlc/snmp/plugin.pm b/src/network/cisco/wlc/snmp/plugin.pm index 763bb8de1..43836dc36 100644 --- a/src/network/cisco/wlc/snmp/plugin.pm +++ b/src/network/cisco/wlc/snmp/plugin.pm @@ -39,6 +39,7 @@ sub new { 'discovery' => 'centreon::common::airespace::snmp::mode::discovery', 'hardware' => 'centreon::common::airespace::snmp::mode::hardware', 'interfaces' => 'snmp_standard::mode::interfaces', + 'list-aps' => 'centreon::common::airespace::snmp::mode::listaps', 'list-groups' => 'centreon::common::airespace::snmp::mode::listgroups', 'list-interfaces' => 'snmp_standard::mode::listinterfaces', 'list-radius-acc-servers' => 'centreon::common::airespace::snmp::mode::listradiusaccservers', @@ -59,4 +60,4 @@ __END__ Check Cisco Wireless Lan Controller in SNMP. -=cut +=cut \ No newline at end of file diff --git a/src/network/extreme/snmp/mode/cpu.pm b/src/network/extreme/snmp/mode/cpu.pm index bcb049eb7..54b880afa 100644 --- a/src/network/extreme/snmp/mode/cpu.pm +++ b/src/network/extreme/snmp/mode/cpu.pm @@ -37,10 +37,10 @@ sub set_counters { key_values => [ { name => 'total' } ], output_template => 'Total CPU Usage : %.2f %%', perfdatas => [ - { label => 'cpu_total', value => 'total', template => '%.2f', min => 0, max => 100, unit => '%' }, - ], + { label => 'cpu_total', template => '%.2f', min => 0, max => 100, unit => '%' } + ] } - }, + } ]; $self->{maps_counters}->{cpu} = [ @@ -48,47 +48,47 @@ sub set_counters { key_values => [ { name => 'extremeCpuMonitorSystemUtilization5secs' }, { name => 'num' }, ], output_template => '5 seconds : %.2f %%', perfdatas => [ - { label => 'cpu_5secs', value => 'extremeCpuMonitorSystemUtilization5secs', template => '%.2f', - min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' }, - ], + { label => 'cpu_5secs', template => '%.2f', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' } + ] } }, { label => '10secs', set => { key_values => [ { name => 'extremeCpuMonitorSystemUtilization10secs' }, { name => 'num' }, ], output_template => '10 seconds : %.2f %%', perfdatas => [ - { label => 'cpu_10secs', value => 'extremeCpuMonitorSystemUtilization10secs', template => '%.2f', - min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' }, - ], + { label => 'cpu_10secs', template => '%.2f', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' } + ] } }, { label => '30secs', set => { key_values => [ { name => 'extremeCpuMonitorSystemUtilization30secs' }, { name => 'num' }, ], output_template => '30 seconds : %.2f %%', perfdatas => [ - { label => 'cpu_30secs', value => 'extremeCpuMonitorSystemUtilization30secs', template => '%.2f', - min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' }, - ], + { label => 'cpu_30secs', template => '%.2f', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' } + ] } }, { label => '1min', set => { key_values => [ { name => 'extremeCpuMonitorSystemUtilization1min' }, { name => 'num' }, ], output_template => '1 minute : %.2f %%', perfdatas => [ - { label => 'cpu_1min', value => 'extremeCpuMonitorSystemUtilization1min', template => '%.2f', - min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' }, - ], + { label => 'cpu_1min', template => '%.2f', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' } + ] } }, { label => '5min', set => { key_values => [ { name => 'extremeCpuMonitorSystemUtilization5mins' }, { name => 'num' }, ], output_template => '5 minutes : %.2f %%', perfdatas => [ - { label => 'cpu_5min', value => 'extremeCpuMonitorSystemUtilization5mins', template => '%.2f', - min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' }, - ], + { label => 'cpu_5min', template => '%.2f', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'num' } + ] } - }, + } ]; } @@ -141,11 +141,11 @@ sub manage_selection { next if ($oid !~ /^$mapping->{extremeCpuMonitorSystemUtilization1min}->{oid}\.(.*)$/); my $instance = $1; my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result->{$oid_extremeCpuMonitorSystemEntry}, instance => $instance); - + foreach (keys %{$mapping}) { $result->{$_} = undef if (defined($result->{$_}) && $result->{$_} =~ /n\/a/i); } - + $self->{cpu}->{$instance} = { num => $instance, %$result }; } @@ -170,12 +170,12 @@ Example: --filter-counters='^(1min|5min)$' =item B<--warning-*> Threshold warning. -Can be: 'total', '5sec', '10sec', '30sec, '1min', '5min'. +Can be: 'total', '5secs', '10secs', '30secs', '1min', '5min'. =item B<--critical-*> Threshold critical. -Can be: 'total', '5sec', '10sec', '30sec, '1min', '5min'. +Can be: 'total', '5secs', '10secs', '30secs', '1min', '5min'. =back diff --git a/src/network/nortel/standard/snmp/mode/memory.pm b/src/network/nortel/standard/snmp/mode/memory.pm index 2ed20a650..08138cf02 100644 --- a/src/network/nortel/standard/snmp/mode/memory.pm +++ b/src/network/nortel/standard/snmp/mode/memory.pm @@ -52,7 +52,7 @@ sub set_counters { ]; $self->{maps_counters}->{memory} = [ - { label => 'usage', display_ok => 0, nlabel => 'memory.usage.bytes', set => { + { label => 'usage', nlabel => 'memory.usage.bytes', set => { key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'display' } ], closure_custom_output => $self->can('custom_usage_output'), perfdatas => [ @@ -60,7 +60,7 @@ sub set_counters { ] } }, - { label => 'usage-free', display_ok => 0, nlabel => 'memory.free.bytes', set => { + { label => 'usage-free', display_ok => 0, nlabel => 'memory.free.bytes', display_ok => 0, set => { key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'display' } ], closure_custom_output => $self->can('custom_usage_output'), perfdatas => [ @@ -68,9 +68,9 @@ sub set_counters { ] } }, - { label => 'usage-prct', nlabel => 'memory.usage.percentage', set => { - key_values => [ { name => 'prct_used' }, { name => 'display' } ], - output_template => 'used: %.2f %%', + { label => 'usage-prct', nlabel => 'memory.usage.percentage', display_ok => 0, set => { + key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'display' } ], + closure_custom_output => $self->can('custom_usage_output'), perfdatas => [ { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 } ] @@ -113,13 +113,13 @@ sub check_khi { my $instance = $1; my $result = $options{snmp}->map_instance(mapping => $mapping_khi, results => $snmp_result, instance => $instance); - my $total = $result->{rcKhiSlotMemUsed} * 1024 + $result->{rcKhiSlotMemFree} * 1024; + my $total = ($result->{rcKhiSlotMemUsed} * 1024) + ($result->{rcKhiSlotMemFree} * 1024); $self->{memory}->{'slot_' . $1} = { display => 'slot_' . $1, used => $result->{rcKhiSlotMemUsed} * 1024, free => $result->{rcKhiSlotMemFree} * 1024, - prct_used => $result->{rcKhiSlotMemUsed} * 1024 / $total, - prct_free => $result->{rcKhiSlotMemFree} * 1024 / $total, + prct_used => ($result->{rcKhiSlotMemUsed} * 1024 * 100 / $total), + prct_free => ($result->{rcKhiSlotMemFree} * 1024 * 100 / $total), total => $total }; } diff --git a/src/network/watchguard/snmp/mode/ipsectunnel.pm b/src/network/watchguard/snmp/mode/ipsectunnel.pm index e504cc7f0..54468523a 100644 --- a/src/network/watchguard/snmp/mode/ipsectunnel.pm +++ b/src/network/watchguard/snmp/mode/ipsectunnel.pm @@ -237,8 +237,7 @@ Example: --filter-counters='tunnels-total' =item B<--warning-*> B<--critical-*> Thresholds. -Can be: 'tunnels-total', 'tunnels-traffic-in', 'tunnels-traffic-out', -'tunnel-traffic-in', 'tunnel-traffic-out'. +Can be: 'tunnels-total', 'tunnel-traffic-in', 'tunnel-traffic-out'. =back diff --git a/src/storage/dell/powerstore/restapi/custom/api.pm b/src/storage/dell/powerstore/restapi/custom/api.pm index 9af9a1a13..7789b2c94 100644 --- a/src/storage/dell/powerstore/restapi/custom/api.pm +++ b/src/storage/dell/powerstore/restapi/custom/api.pm @@ -133,7 +133,7 @@ sub get_port { return $self->{port}; } -sub get_metrics_by_clusters { +sub get_performance_metrics_by_clusters { my ($self, %options) = @_; my $clusters = $self->request_api(endpoint => '/api/rest/cluster'); @@ -160,6 +160,33 @@ sub get_metrics_by_clusters { return $results; } +sub get_space_metrics_by_appliance { + my ($self, %options) = @_; + + my $appliances = $self->request_api(endpoint => '/api/rest/appliance'); + + my $results = {}; + foreach (@$appliances) { + my $post_json = JSON::XS->new->utf8->encode( + { + entity => 'space_metrics_by_appliance', + entity_id => $_->{id}, + interval => 'Five_Mins' + } + ); + + my $perfs = $self->request_api( + method => 'POST', + endpoint => '/api/rest/metrics/generate', + headers => ['Content-Type: application/json'], + query_form_post => $post_json + ); + $results->{ $_->{id} } = $perfs; + } + + return $results; +} + sub request_api { my ($self, %options) = @_; diff --git a/src/storage/dell/powerstore/restapi/mode/clusters.pm b/src/storage/dell/powerstore/restapi/mode/clusters.pm index e84e4cf4c..09128a2b1 100644 --- a/src/storage/dell/powerstore/restapi/mode/clusters.pm +++ b/src/storage/dell/powerstore/restapi/mode/clusters.pm @@ -170,7 +170,7 @@ sub new { sub manage_selection { my ($self, %options) = @_; - my $clusters = $options{custom}->get_metrics_by_clusters(); + my $clusters = $options{custom}->get_performance_metrics_by_clusters(); $self->{global} = { detected => 0 }; $self->{clusters} = {}; diff --git a/src/storage/dell/powerstore/restapi/mode/memory.pm b/src/storage/dell/powerstore/restapi/mode/memory.pm new file mode 100644 index 000000000..9d0de78a3 --- /dev/null +++ b/src/storage/dell/powerstore/restapi/mode/memory.pm @@ -0,0 +1,136 @@ +# +# Copyright 2023 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 storage::dell::powerstore::restapi::mode::memory; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub custom_usage_output { + my ($self, %options) = @_; + + return sprintf( + 'memory total: %s %s used: %s %s (%.2f%%) free: %s %s (%.2f%%)', + $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}), + $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}), + $self->{result_values}->{prct_used}, + $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}), + $self->{result_values}->{prct_free} + ); +} + +sub prefix_appliance_output { + my ($self, %options) = @_; + + return "Appliance '" . $options{instance} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'memory', type => 1, cb_prefix_output => 'prefix_appliance_output', message_multiple => 'All appliances memory usage are ok' }, + ]; + + $self->{maps_counters}->{memory} = [ + { label => 'usage', nlabel => 'memory.usage.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1 } + ] + } + }, + { label => 'usage-free', display_ok => 0, nlabel => 'memory.free.bytes', set => { + key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1 } + ] + } + }, + { label => 'usage-prct', display_ok => 0, nlabel => 'memory.usage.percentage', set => { + key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' } ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-appliance-id:s' => { name => 'filter_appliance_id' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $appliances = $options{custom}->get_space_metrics_by_appliance(); + + $self->{memory} = {}; + foreach my $appliance_id (keys %$appliances) { + next if (defined($self->{option_results}->{filter_appliance_id}) && $self->{option_results}->{filter_appliance_id} ne '' && + $appliance_id !~ /$self->{option_results}->{filter_appliance_id}/); + + my $free = $appliances->{$appliance_id}->{last_physical_total} - $appliances->{$appliance_id}->{last_physical_used}; + $self->{memory}->{ $appliance_id } = { + total => $appliances->{$appliance_id}->{last_physical_total}, + used => $appliances->{$appliance_id}->{last_physical_used}, + free => $free, + prct_used => $appliances->{$appliance_id}->{last_physical_used} * 100 / $appliances->{$appliance_id}->{last_physical_total}, + prct_free => $free * 100 / $appliances->{$appliance_id}->{last_physical_total} + }; + } +} + +1; + +__END__ + +=head1 MODE + +Check appliances memory usage. + +=over 8 + +=item B<--filter-appliance-id> + +Filter appliance ID. + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'usage' (B), 'usage-free' (B), 'usage-prct' (%). + +=back + +=cut diff --git a/src/storage/dell/powerstore/restapi/plugin.pm b/src/storage/dell/powerstore/restapi/plugin.pm index 1dc66a246..313aa451e 100644 --- a/src/storage/dell/powerstore/restapi/plugin.pm +++ b/src/storage/dell/powerstore/restapi/plugin.pm @@ -32,7 +32,8 @@ sub new { $self->{modes} = { 'alerts' => 'storage::dell::powerstore::restapi::mode::alerts', 'clusters' => 'storage::dell::powerstore::restapi::mode::clusters', - 'hardware' => 'storage::dell::powerstore::restapi::mode::hardware' + 'hardware' => 'storage::dell::powerstore::restapi::mode::hardware', + 'memory' => 'storage::dell::powerstore::restapi::mode::memory' }; $self->{custom_modes}->{api} = 'storage::dell::powerstore::restapi::custom::api'; diff --git a/src/storage/netapp/ontap/restapi/mode/quotas.pm b/src/storage/netapp/ontap/restapi/mode/quotas.pm index 1655c9d78..0379d9475 100644 --- a/src/storage/netapp/ontap/restapi/mode/quotas.pm +++ b/src/storage/netapp/ontap/restapi/mode/quotas.pm @@ -278,14 +278,14 @@ sub manage_selection { $qtree !~ /$self->{option_results}->{filter_qtree}/); next if ($volume ne '' && defined($self->{option_results}->{filter_volume}) && $self->{option_results}->{filter_volume} ne '' && $volume !~ /$self->{option_results}->{filter_volume}/); - next if ($vserver ne '' &&defined($self->{option_results}->{filter_vserver}) && $self->{option_results}->{filter_vserver} ne '' && + next if ($vserver ne '' && defined($self->{option_results}->{filter_vserver}) && $self->{option_results}->{filter_vserver} ne '' && $vserver !~ /$self->{option_results}->{filter_vserver}/); my $path = $vserver . $volume . $qtree; $self->{duplicated}->{$path} = 0 if (!defined($self->{duplicated}->{$path})); $self->{duplicated}->{$path}++; - $self->{quotas}->{$index} = { + $self->{quotas}->{$path . $index} = { index => $index, qtree => $qtree, volume => $volume, diff --git a/src/storage/wd/nas/snmp/mode/hardware.pm b/src/storage/wd/nas/snmp/mode/hardware.pm new file mode 100644 index 000000000..bbb063a42 --- /dev/null +++ b/src/storage/wd/nas/snmp/mode/hardware.pm @@ -0,0 +1,221 @@ +# +# Copyright 2023 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 storage::wd::nas::snmp::mode::hardware; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub prefix_fan_output { + my ($self, %options) = @_; + + return "fan '" . $options{instance} . "' "; +} + +sub prefix_drive_output { + my ($self, %options) = @_; + + return "drive '" . $options{instance} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0 }, + { name => 'fans', type => 1, cb_prefix_output => 'prefix_fan_output', skipped_code => { -10 => 1 } }, + { name => 'drives', type => 1, cb_prefix_output => 'prefix_drive_output', skipped_code => { -10 => 1 } } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'system-temperature', nlabel => 'hardware.temperature.celsius', set => { + key_values => [ { name => 'temperature' } ], + output_template => 'system temperature: %s C', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'C', + instances => 'system', + value => $self->{result_values}->{temperature}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}) + ); + } + } + } + ]; + + $self->{maps_counters}->{fans} = [ + { label => 'fan-status', type => 2, critical_default => '%{status} ne "running"', set => { + key_values => [ { name => 'status' } ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + $self->{maps_counters}->{drives} = [ + { label => 'drive-temperature', nlabel => 'hardware.temperature.celsius', set => { + key_values => [ { name => 'temperature' }, { name => 'serial' } ], + output_template => 'temperature: %s C', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'C', + instances => 'drive:' . $self->{result_values}->{serial}, + value => $self->{result_values}->{temperature}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}) + ); + } + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $nas = { + ex2 => { + system => { + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.7' }, + fanStatus => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.8' } + }, + drive => { + serial => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.10.1.4' }, + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.10.1.5' } + }, + driveTable => '.1.3.6.1.4.1.5127.1.1.1.2.1.10.1' + }, + ex2ultra => { + system => { + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.7' }, + fanStatus => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.8' } + }, + drive => { + serial => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.10.1.4' }, + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.10.1.5' } + }, + driveTable => '.1.3.6.1.4.1.5127.1.1.1.8.1.10.1' + }, + pr2100 => { + system => { + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.7' }, + fanStatus => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.8' } + }, + drive => { + serial => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.10.1.4' }, + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.10.1.5' } + }, + driveTable => '.1.3.6.1.4.1.5127.1.1.1.9.1.10.1' + }, + pr4100 => { + system => { + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.7' }, + fanStatus => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.8' } + }, + drive => { + serial => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.10.1.4' }, + temperature => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.10.1.5' } + }, + driveTable => '.1.3.6.1.4.1.5127.1.1.1.10.1.10.1' + } + }; + + my $snmp_result = $options{snmp}->get_leef( + oids => [ map({ $nas->{$_}->{system}->{temperature}->{oid} . '.0', $nas->{$_}->{system}->{fanStatus}->{oid} . '.0' } keys(%$nas)) ], + nothing_quit => 1 + ); + + $self->{global} = {}; + $self->{fans} = {}; + $self->{drives} = {}; + foreach my $type (keys %$nas) { + next if (!defined($snmp_result->{ $nas->{$type}->{system}->{temperature}->{oid} . '.0' })); + + my $result = $options{snmp}->map_instance(mapping => $nas->{$type}->{system}, results => $snmp_result, instance => 0); + + $self->{global}->{temperature} = $1 if ($result->{temperature} =~ /Centigrade:(\d+)/i); + while ($result->{fanStatus} =~ /fan(\d+):\s*(\S+)/g) { + $self->{fans}->{$1} = { status => $2 }; + } + + $snmp_result = $options{snmp}->get_table( + oid => $nas->{$type}->{driveTable}, + start => $nas->{$type}->{drive}->{serial}->{oid}, + end => $nas->{$type}->{drive}->{temperature}->{oid} + ); + + foreach (keys %$snmp_result) { + next if (! /^$nas->{$type}->{drive}->{serial}->{oid}\.(\d+)$/); + $result = $options{snmp}->map_instance(mapping => $nas->{$type}->{drive}, results => $snmp_result, instance => $1); + $self->{drives}->{ $result->{serial} } = { serial => $result->{serial} }; + $self->{drives}->{ $result->{serial} }->{temperature} = $1 if ($result->{temperature} =~ /Centigrade:(\d+)/i); + } + } +} + +1; + +__END__ + +=head1 MODE + +Check hardware. + +=over 8 + +=item B<--warning-fan-status> + +Set warning threshold for status (Default : '%{status} ne "running"'). +Can used special variables like: %{status} + +=item B<--critical-fan-status> + +Set critical threshold for status. +Can used special variables like: %{status} + +=item B<--warning-*> B<--critical-*> + +Thresholds. Can be: +'system-temperature', 'drive-temperature'. + +=back + +=cut diff --git a/src/storage/wd/nas/snmp/mode/listvolumes.pm b/src/storage/wd/nas/snmp/mode/listvolumes.pm new file mode 100644 index 000000000..bafff378a --- /dev/null +++ b/src/storage/wd/nas/snmp/mode/listvolumes.pm @@ -0,0 +1,151 @@ +# +# Copyright 2023 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 storage::wd::nas::snmp::mode::listvolumes; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $nas = { + ex2 => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.2.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.9.1.2' }, + type => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.9.1.3' } + } + }, + ex2ultra => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.8.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.9.1.2' }, + type => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.9.1.3' } + } + }, + pr2100 => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.9.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.9.1.2' }, + type => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.9.1.3' } + } + }, + pr4100 => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.10.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.9.1.2' }, + type => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.9.1.3' } + } + } + }; + + my $snmp_result = $options{snmp}->get_multiple_table( + oids => [ + { oid => $nas->{ex2}->{volumeTable} }, + { oid => $nas->{ex2ultra}->{volumeTable} }, + { oid => $nas->{pr2100}->{volumeTable} }, + { oid => $nas->{pr4100}->{volumeTable} } + ] + ); + + my $volumes = {}; + foreach my $type (keys %$nas) { + next if (scalar(keys %{$snmp_result->{ $nas->{$type}->{volumeTable} }}) <= 0); + foreach (keys %{$snmp_result->{ $nas->{$type}->{volumeTable} }}) { + next if (! /^$nas->{$type}->{volume}->{name}->{oid}\.(\d+)$/); + + $volumes->{$1} = $options{snmp}->map_instance(mapping => $nas->{$type}->{volume}, results => $snmp_result->{ $nas->{$type}->{volumeTable} }, instance => $1); + } + } + + return $volumes; +} + +sub run { + my ($self, %options) = @_; + + my $volumes = $self->manage_selection(%options); + foreach (sort keys %$volumes) { + $self->{output}->output_add( + long_msg => sprintf( + '[name: %s] [type: %s]', + $volumes->{$_}->{name}, + $volumes->{$_}->{type} + ) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List volumes:' + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'type']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $volumes = $self->manage_selection(%options); + foreach (sort keys %$volumes) { + $self->{output}->add_disco_entry( + %{$volumes->{$_}} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List volumes. + +=over 8 + +=back + +=cut diff --git a/src/storage/wd/nas/snmp/mode/volumes.pm b/src/storage/wd/nas/snmp/mode/volumes.pm new file mode 100644 index 000000000..9b35b409f --- /dev/null +++ b/src/storage/wd/nas/snmp/mode/volumes.pm @@ -0,0 +1,197 @@ +# +# Copyright 2023 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 storage::wd::nas::snmp::mode::volumes; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::misc; + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + return sprintf( + "space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} + ); +} + +sub prefix_volume_output { + my ($self, %options) = @_; + + return sprintf( + "volume '%s' ", + $options{instance_value}->{name} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'volumes', type => 1, cb_prefix_output => 'prefix_volume_output', message_multiple => 'All volumes are ok' } + ]; + + $self->{maps_counters}->{volumes} = [ + { label => 'space-usage', nlabel => 'volume.space.usage.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } + ] + } + }, + { label => 'space-usage-free', display_ok => 0, nlabel => 'volume.space.free.bytes', set => { + key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } + ] + } + }, + { label => 'space-usage-prct', display_ok => 0, nlabel => 'volume.space.usage.percentage', set => { + key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'name' } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-name:s' => { name => 'filter_name' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $nas = { + ex2 => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.2.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.9.1.2' }, + total => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.9.1.5' }, + free => { oid => '.1.3.6.1.4.1.5127.1.1.1.2.1.9.1.6' } + } + }, + ex2ultra => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.8.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.9.1.2' }, + total => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.9.1.5' }, + free => { oid => '.1.3.6.1.4.1.5127.1.1.1.8.1.9.1.6' } + } + }, + pr2100 => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.9.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.9.1.2' }, + total => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.9.1.5' }, + free => { oid => '.1.3.6.1.4.1.5127.1.1.1.9.1.9.1.6' } + } + }, + pr4100 => { + volumeTable => '.1.3.6.1.4.1.5127.1.1.1.10.1.9.1', + volume => { + name => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.9.1.2' }, + total => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.9.1.5' }, + free => { oid => '.1.3.6.1.4.1.5127.1.1.1.10.1.9.1.6' } + } + } + }; + + my $snmp_result = $options{snmp}->get_multiple_table( + oids => [ + { oid => $nas->{ex2}->{volumeTable} }, + { oid => $nas->{ex2ultra}->{volumeTable} }, + { oid => $nas->{pr2100}->{volumeTable} }, + { oid => $nas->{pr4100}->{volumeTable} } + ], + nothing_quit => 1 + ); + + $self->{volumes} = {}; + foreach my $type (keys %$nas) { + next if (scalar(keys %{$snmp_result->{ $nas->{$type}->{volumeTable} }}) <= 0); + + foreach (keys %{$snmp_result->{ $nas->{$type}->{volumeTable} }}) { + next if (! /^$nas->{$type}->{volume}->{name}->{oid}\.(\d+)$/); + + my $result = $options{snmp}->map_instance(mapping => $nas->{$type}->{volume}, results => $snmp_result->{ $nas->{$type}->{volumeTable} }, instance => $1); + next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $result->{name} !~ /$self->{option_results}->{filter_name}/); + + $result->{total} =~ /^([0-9\.]+)\s*(\S+)/; + $result->{total} = centreon::plugins::misc::convert_bytes(value => $1, unit => $2 . 'B'); + + $result->{free} =~ /^([0-9\.]+)\s*(\S+)/; + $result->{free} = centreon::plugins::misc::convert_bytes(value => $1, unit => $2 . 'B'); + + $self->{volumes}->{ $result->{name} } = { + name => $result->{name}, + total => $result->{total}, + free => $result->{free}, + used => $result->{total} - $result->{free}, + prct_used => ($result->{total} - $result->{free}) * 100 / $result->{total}, + prct_free => $result->{free} * 100 / $result->{total} + }; + } + } +} + +1; + +__END__ + +=head1 MODE + +Check volumes. + +=over 8 + +=item B<--filter-name> + +Filter volumes by name (can be a regexp). + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'space-usage-prct', 'space-usage', 'space-usage-free'. + +=back + +=cut diff --git a/src/storage/wd/nas/snmp/plugin.pm b/src/storage/wd/nas/snmp/plugin.pm new file mode 100644 index 000000000..8d5f1ae41 --- /dev/null +++ b/src/storage/wd/nas/snmp/plugin.pm @@ -0,0 +1,49 @@ +# +# Copyright 2023 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 storage::wd::nas::snmp::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_snmp); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{modes} = { + 'hardware' => 'storage::wd::nas::snmp::mode::hardware', + 'list-volumes' => 'storage::wd::nas::snmp::mode::listvolumes', + 'volumes' => 'storage::wd::nas::snmp::mode::volumes' + }; + + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check WD (Western Digital) NAS in SNMP. + +=cut