From 195d364b2aff7785008cfc1d519b34bc6888bdf4 Mon Sep 17 00:00:00 2001 From: qgarnier Date: Tue, 30 Aug 2022 10:00:30 +0200 Subject: [PATCH] (plugin) storage::hp::3par::ssh - add multiple modes (#3862) --- .../storage/hp/3par/ssh/mode/afc.pm | 294 +++++++++++++ .../storage/hp/3par/ssh/mode/cages.pm | 408 ++++++++++++++++++ .../storage/hp/3par/ssh/mode/capacity.pm | 257 +++++++++++ .../hp/3par/ssh/mode/components/battery.pm | 32 +- .../hp/3par/ssh/mode/components/cim.pm | 29 +- .../hp/3par/ssh/mode/components/disk.pm | 22 +- .../hp/3par/ssh/mode/components/node.pm | 22 +- .../hp/3par/ssh/mode/components/port.pm | 20 +- .../hp/3par/ssh/mode/components/psu.pm | 20 +- .../hp/3par/ssh/mode/components/sensor.pm | 31 +- .../hp/3par/ssh/mode/components/wsapi.pm | 29 +- .../storage/hp/3par/ssh/mode/diskusage.pm | 85 ++-- .../storage/hp/3par/ssh/mode/hardware.pm | 18 +- .../storage/hp/3par/ssh/mode/nodes.pm | 214 +++++++++ .../storage/hp/3par/ssh/mode/psu.pm | 322 ++++++++++++++ .../storage/hp/3par/ssh/mode/time.pm | 210 +++++++++ .../storage/hp/3par/ssh/mode/uptime.pm | 197 +++++++++ .../storage/hp/3par/ssh/mode/volumeusage.pm | 34 +- .../storage/hp/3par/ssh/plugin.pm | 16 +- 19 files changed, 2115 insertions(+), 145 deletions(-) create mode 100644 centreon-plugins/storage/hp/3par/ssh/mode/afc.pm create mode 100644 centreon-plugins/storage/hp/3par/ssh/mode/cages.pm create mode 100644 centreon-plugins/storage/hp/3par/ssh/mode/capacity.pm create mode 100644 centreon-plugins/storage/hp/3par/ssh/mode/nodes.pm create mode 100644 centreon-plugins/storage/hp/3par/ssh/mode/psu.pm create mode 100644 centreon-plugins/storage/hp/3par/ssh/mode/time.pm create mode 100644 centreon-plugins/storage/hp/3par/ssh/mode/uptime.pm diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/afc.pm b/centreon-plugins/storage/hp/3par/ssh/mode/afc.pm new file mode 100644 index 000000000..130805064 --- /dev/null +++ b/centreon-plugins/storage/hp/3par/ssh/mode/afc.pm @@ -0,0 +1,294 @@ +# +# Copyright 2022 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::hp::3par::ssh::mode::afc; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +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 node_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking node '%s' afc", + $options{instance_value}->{node_id} + ); +} + +sub prefix_node_output { + my ($self, %options) = @_; + + return sprintf( + "node '%s' afc ", + $options{instance_value}->{node_id} + ); +} + +sub volume_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking volume '%s' afc", + $options{instance_value}->{volume_name} + ); +} + +sub prefix_volume_output { + my ($self, %options) = @_; + + return sprintf( + "volume '%s' afc ", + $options{instance_value}->{volume_name} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'nodes', type => 3, cb_prefix_output => 'prefix_node_output', cb_long_output => 'node_long_output', indent_long_output => ' ', message_multiple => 'All nodes afc are ok', + group => [ + { name => 'global', type => 0 }, + { name => 'space', type => 0 }, + { name => 'node_fmp', type => 0 } + ] + }, + { + name => 'volumes', type => 3, cb_prefix_output => 'prefix_volume_output', cb_long_output => 'volume_long_output', indent_long_output => ' ', message_multiple => 'All volumes afc are ok', + group => [ + { name => 'volume_fmp', type => 0 } + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { + label => 'status', + type => 2, + critical_default => '%{status} !~ /normal/i', + set => { + key_values => [ { name => 'status' }, { name => 'node_id' } ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + $self->{maps_counters}->{space} = [ + { label => 'flashcache-usage', nlabel => 'node.flashcache.usage.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ], + 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 } + ] + } + }, + { label => 'flashcache-usage-free', nlabel => 'node.flashcache.free.bytes', display_ok => 0, set => { + key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ], + 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 } + ] + } + }, + { label => 'flashcache-usage-prct', nlabel => 'node.flashcache.usage.percentage', display_ok => 0, set => { + key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 } + ] + } + } + ]; + + $self->{maps_counters}->{node_fmp} = [ + { label => 'flashcache-node-readhits', nlabel => 'node.flashcache.readhits.percentage', set => { + key_values => [ { name => 'readhits' } ], + output_template => 'read hits: %s%%', + perfdatas => [ + { template => '%s', min => 0, max => 100, unit => '%', label_extra_instance => 1 } + ] + } + } + ]; + + $self->{maps_counters}->{volume_fmp} = [ + { label => 'flashcache-volume-readhits', nlabel => 'volume.flashcache.readhits.percentage', set => { + key_values => [ { name => 'readhits' } ], + output_template => 'read hits: %s%%', + perfdatas => [ + { template => '%s', 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-node-id:s' => { name => 'filter_node_id' }, + 'filter-volume-name:s' => { name => 'filter_volume_name' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my ($content) = $options{custom}->execute_command( + commands => [ + 'echo "===showflashcache==="', + 'showflashcache', + 'echo "===statcachenode==="', + 'statcache -iter 1 -d 1', + 'echo "===statcachevolume==="', + 'statcache -iter 1 -d 1 -v' + ] + ); + + #Node Mode State Size Used% + # 0 SSD normal 393216 49 + # 1 SSD normal 393216 49 + + $self->{nodes} = {}; + if ($content =~ /===showflashcache===(.*?)(?====|\Z$)/msi) { + my $entry = $1; + while ($entry =~ /^\s*(\d+)\s+\S+\s+(\S+)\s+(\S+)\s+(\S+)/mg) { + my ($node_id, $status, $size, $prct_used) = ($1, $2, $3 * 1024 * 1024, $4); + + next if (defined($self->{option_results}->{filter_node_id}) && $self->{option_results}->{filter_node_id} ne '' && + $node_id !~ /$self->{option_results}->{filter_node_id}/); + + $self->{nodes}->{'node' . $node_id} = { + node_id => $node_id, + global => { + node_id => $node_id, + status => $status + }, + space => { + total => $size, + used => $prct_used * $size / 100, + free => $size - ($prct_used * $size / 100), + prct_used => $prct_used, + prct_free => 100 - $prct_used + } + }; + } + } + + if ($content =~ /===statcachenode===(.*?)(?====|\Z$)/msi) { + my $entry = $1; + if ($entry =~ /CMP\s+FMP\s+Total(.*?)Internal\s+Flashcache\s+Activity/ms) { + my $stat = $1; + while ($stat =~ /^\s*(\d+)\s+Read\s+\d+\s+\d+\s+(\d+)/mg) { + my ($node_id, $readhits) = ($1, $2); + next if (!defined($self->{nodes}->{'node' . $node_id})); + $self->{nodes}->{'node' . $node_id}->{node_fmp} = { readhits => $readhits }; + } + } + } + + $self->{volumes} = {}; + if ($content =~ /===statcachevolume===(.*?)(?====|\Z$)/msi) { + my $entry = $1; + if ($entry =~ /CMP\s+FMP\s+Total(.*?)Internal\s+Flashcache\s+Activity/ms) { + my $stat = $1; + while ($stat =~ /^\s*\d+\s+(\S+)\s+Read\s+\d+\s+\d+\s+(\d+)/mg) { + my ($volume_name, $readhits) = ($1, $2); + + next if (defined($self->{option_results}->{filter_volume_name}) && $self->{option_results}->{filter_volume_name} ne '' && + $volume_name !~ /$self->{option_results}->{filter_volume_name}/); + + $self->{volumes}->{$volume_name} = { + volume_name => $volume_name, + volume_fmp => { readhits => $readhits } + }; + } + } + } + + if (scalar(%{$self->{nodes}}) <= 0 && scalar(%{$self->{volumes}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get afc information"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check adaptive flash cache. + +=over 8 + +=item B<--filter-node-id> + +Filter nodes by id (can be a regexp). + +=item B<--filter-volume-name> + +Filter volumes by name (can be a regexp). + +=item B<--unknown-status> + +=item B<--warning-status> + +=item B<--critical-status> + +Set thresholds for status (Default critical: '%{status} !~ /normal/i') + + +Can used special variables like: %{status}, %{node_id} + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'flashcache-usage', 'flashcache-usage-free', 'flashcache-usage-prct'. +'flashcache-node-readhits', 'flashcache-volume-readhit'. + +=back + +=cut diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/cages.pm b/centreon-plugins/storage/hp/3par/ssh/mode/cages.pm new file mode 100644 index 000000000..5e5b61d23 --- /dev/null +++ b/centreon-plugins/storage/hp/3par/ssh/mode/cages.pm @@ -0,0 +1,408 @@ +# +# Copyright 2022 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::hp::3par::ssh::mode::cages; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{label_ref} . '_status'}; + $self->{result_values}->{cage_id} = $options{new_datas}->{$self->{instance} . '_cage_id'}; + $self->{result_values}->{board_id} = $options{new_datas}->{$self->{instance} . '_board_id'}; + + return 0; +} + +sub cage_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking cage '%s'", + $options{instance_value}->{cage_id} + ); +} + +sub prefix_cage_output { + my ($self, %options) = @_; + + return sprintf( + "cage '%s' ", + $options{instance_value}->{cage_id} + ); +} + +sub prefix_board_output { + my ($self, %options) = @_; + + return "board '" . $options{instance_value}->{board_id} . "' "; +} + +sub prefix_psu_output { + my ($self, %options) = @_; + + return "power supply '" . $options{instance_value}->{psu_id} . "' "; +} + +sub prefix_drive_output { + my ($self, %options) = @_; + + return "drive '" . $options{instance_value}->{drive_id} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'cages', type => 3, cb_prefix_output => 'prefix_cage_output', cb_long_output => 'cage_long_output', indent_long_output => ' ', message_multiple => 'All cages are ok', + group => [ + { name => 'global', type => 0 }, + { name => 'boards', display_long => 1, cb_prefix_output => 'prefix_board_output', + message_multiple => 'all boards are ok', type => 1, skipped_code => { -10 => 1 } }, + { name => 'psu', display_long => 1, cb_prefix_output => 'prefix_psu_output', + message_multiple => 'all power supplies are ok', type => 1, skipped_code => { -10 => 1 } }, + { name => 'drives', display_long => 1, cb_prefix_output => 'prefix_drive_output', + message_multiple => 'all drives are ok', type => 1, skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { + label => 'status', + type => 2, + critical_default => '%{status} !~ /Normal/i', + set => { + key_values => [ { name => 'status' }, { name => 'cage_id' } ], + output_template => 'overall status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + $self->{maps_counters}->{boards} = [ + { + label => 'board-firmware-status', + type => 2, + critical_default => '%{status} !~ /Current/i', + set => { + key_values => [ { name => 'firmware_status' }, { name => 'cage_id' }, { name => 'board_id' } ], + closure_custom_calc => $self->can('custom_status_calc'), closure_custom_calc_extra_options => { label_ref => 'firmware' }, + output_template => 'firmware status: %s', + output_use => 'status', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + foreach ('self', 'partner') { + push @{$self->{maps_counters}->{boards}}, + { + label => 'board-' . $_ . '-status', + type => 2, + critical_default => '%{status} !~ /ok/i', + set => { + key_values => [ { name => $_ . '_status' }, { name => 'cage_id' }, { name => 'board_id' } ], + closure_custom_calc => $self->can('custom_status_calc'), closure_custom_calc_extra_options => { label_ref => $_ }, + output_template => $_ . ' status: %s', + output_use => 'status', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }; + } + + $self->{maps_counters}->{psu} = [ + { + label => 'psu-status', + type => 2, + critical_default => '%{status} !~ /ok/i', + set => { + key_values => [ { name => 'status' }, { name => 'cage_id' }, { name => 'psu_id' } ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + foreach ('ac', 'dc', 'fan') { + push @{$self->{maps_counters}->{psu}}, + { + label => 'psu-' . $_ . '-status', + type => 2, + critical_default => '%{status} !~ /ok/i', + set => { + key_values => [ { name => $_ . '_status' }, { name => 'cage_id' }, { name => 'psu_id' } ], + closure_custom_calc => $self->can('custom_status_calc'), closure_custom_calc_extra_options => { label_ref => $_ }, + output_template => $_ . ' status: %s', + output_use => 'status', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }; + } + + $self->{maps_counters}->{drives} = [ + { + label => 'drive-status', + type => 2, + critical_default => '%{status} !~ /normal/i', + set => { + key_values => [ { name => 'status' }, { name => 'cage_id' }, { name => 'drive_id' } ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + foreach ('portA', 'portB') { + push @{$self->{maps_counters}->{drives}}, + { + label => 'drive-' . lc($_) . '-status', + type => 2, + critical_default => '%{status} !~ /ok/i', + set => { + key_values => [ { name => $_ . '_status' }, { name => 'cage_id' }, { name => 'drive_id' } ], + closure_custom_calc => $self->can('custom_status_calc'), closure_custom_calc_extra_options => { label_ref => $_ }, + output_template => $_ . ' status: %s', + output_use => 'status', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }; + } + + push @{$self->{maps_counters}->{drives}}, + { label => 'drive-temperature', nlabel => 'battery.charge.remaining.percent', set => { + key_values => [ { name => 'temperature' }, { name => 'cage_id' }, { name => 'drive_id' } ], + output_template => 'temperature: %s C', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'C', + instances => ['cage' . $self->{result_values}->{cage_id}, 'drive' . $self->{result_values}->{drive_id}], + value => sprintf('%s', $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 => { + 'filter-cage-id:s' => { name => 'filter_cage_id' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my ($content) = $options{custom}->execute_command(commands => ['showcage -d']); + + $self->{cages} = {}; + while ($content =~ /(Cage\s+detail\s+info\s+for\s+cage(\d+).*?)(?=Cage\s+detail\s+info\s+for\s+cage|\Z$)/msig) { + my ($cage_id, $entry) = ($2, $1); + + next if (defined($self->{option_results}->{filter_cage_id}) && $self->{option_results}->{filter_cage_id} ne '' && + $cage_id !~ /$self->{option_results}->{filter_cage_id}/); + + $self->{cages}->{ 'cage' . $cage_id } = { + cage_id => $cage_id, + global => { cage_id => $cage_id }, + boards => {}, + psu => {}, + drives => {} + }; + $self->{cages}->{ 'cage' . $cage_id }->{global}->{status} = $1 if ($entry =~ /^OverallState:\s*(\S+)/msi); + + if ($entry =~ /^Interface\s+Board\s+Info\s+(.*)/m) { + my @boards = split(/\s+/, $1); + my (@firmwares, @states); + @firmwares = split(/\s+/, $1) if ($entry =~ /^Firmware_status\s+(.*)/m); + @states = split(/\s+/, $1) if ($entry =~ /^State\(self,partner\)\s+(.*)/m); + for (my $i = 0; $i < scalar(@boards); $i++) { + next if ($boards[$i] !~ /(\d+)/); + my $board_id = $1; + $self->{cages}->{ 'cage' . $cage_id }->{boards}->{$board_id} = { + cage_id => $cage_id, + board_id => $board_id + }; + $self->{cages}->{ 'cage' . $cage_id }->{boards}->{$board_id}->{firmware_status} = $firmwares[$i]; + my ($itself, $partner) = split(/,/, $states[$i]); + $self->{cages}->{ 'cage' . $cage_id }->{boards}->{$board_id}->{self_status} = $itself; + $self->{cages}->{ 'cage' . $cage_id }->{boards}->{$board_id}->{partner_status} = $partner; + } + } + + while ($entry =~ /^\s*ps(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/mg) { + my ($psu_id, $status, $ac_status, $dc_status, $fan_status) = ($1, $2, $3, $4, $5); + $self->{cages}->{ 'cage' . $cage_id }->{psu}->{$psu_id} = { + cage_id => $cage_id, + psu_id => $psu_id, + status => $status, + ac_status => $ac_status, + dc_status => $dc_status, + fan_status => $fan_status + }; + } + + while ($entry =~ /^\s*(\d+:\d+)\s+\S+\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/mg) { + $self->{cages}->{ 'cage' . $cage_id }->{drives}->{$1} = { + cage_id => $cage_id, + drive_id => $1, + status => $2, + temperature => $3, + portA_status => $4, + portB_status => $5 + }; + } + } + + if (scalar(%{$self->{cages}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get cages information"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check cages. + +=over 8 + +=item B<--filter-cage-id> + +Filter cages by id (can be a regexp). + +=item B<--unknown-status> + +=item B<--warning-status> + +=item B<--critical-status> + +Set thresholds for status (Default critical: '%{status} !~ /Normal/i') + + +Can used special variables like: %{status}, %{cage_id} + + +=item B<--unknown-firmware-status> + +=item B<--warning-firmware-status> + +=item B<--critical-firmware-status> + +Set thresholds for status (Default critical: '%{status} !~ /Current/i') + + +Can used special variables like: %{status}, %{cage_id}, %{board_id} + + +=item B<--unknown-[self|partner]-status> + +=item B<--warning-[self|partner]-status> + +=item B<--critical-[self|partner]-status> + +Set thresholds for status (Default critical: '%{status} !~ /ok/i') + + +Can used special variables like: %{status}, %{cage_id}, %{board_id} + + +=item B<--unknown-psu-status> + +=item B<--warning-psu-status> + +=item B<--critical-psu-status> + +Set thresholds for status (Default critical: '%{status} !~ /ok/i') + + +Can used special variables like: %{status}, %{cage_id}, %{psu_id} + + +=item B<--unknown-psu-[ac|dc|fan]-status> + +=item B<--warning-psu-[ac|dc|fan]-status> + +=item B<--critical-psu-[ac|dc|fan]-status> + +Set thresholds for status (Default critical: '%{status} !~ /ok/i') + + +Can used special variables like: %{status}, %{cage_id}, %{psu_id} + + +=item B<--unknown-drive-status> + +=item B<--warning-drive-status> + +=item B<--critical-drive-status> + +Set thresholds for status (Default critical: '%{status} !~ /normal/i') + + +Can used special variables like: %{status}, %{cage_id}, %{drive_id} + + +=item B<--unknown-drive-[porta|portb]-status> + +=item B<--warning-drive-[porta|portb]-status> + +=item B<--critical-drive-[porta|portb]-status> + +Set thresholds for status (Default critical: '%{status} !~ /ok/i') + + +Can used special variables like: %{status}, %{cage_id}, %{drive_id} + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'drive-temperature'. + +=back + +=cut diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/capacity.pm b/centreon-plugins/storage/hp/3par/ssh/mode/capacity.pm new file mode 100644 index 000000000..6947f263f --- /dev/null +++ b/centreon-plugins/storage/hp/3par/ssh/mode/capacity.pm @@ -0,0 +1,257 @@ +# +# Copyright 2022 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::hp::3par::ssh::mode::capacity; + +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}); + 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 storage_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking storage '%s'", + $options{instance_value}->{type} + ); +} + +sub prefix_storage_output { + my ($self, %options) = @_; + + return sprintf( + "storage '%s' ", + $options{instance_value}->{type} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'storages', type => 3, cb_prefix_output => 'prefix_storage_output', cb_long_output => 'storage_long_output', indent_long_output => ' ', message_multiple => 'All storage capacities are ok', + group => [ + { name => 'space', type => 0 }, + { name => 'efficiency', type => 0, skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{space} = [ + { label => 'space-usage', nlabel => 'storage.space.usage.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ], + 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 } + ] + } + }, + { label => 'space-usage-free', nlabel => 'storage.space.free.bytes', display_ok => 0, set => { + key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ], + 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 } + ] + } + }, + { label => 'space-usage-prct', nlabel => 'storage.space.usage.percentage', display_ok => 0, set => { + key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 } + ] + } + }, + { label => 'space-unavailable', nlabel => 'storage.space.unavailable.bytes', set => { + key_values => [ { name => 'unavailable' } ], + output_template => 'unavailable: %s %s', + output_change_bytes => 1, + perfdatas => [ + { template => '%s', unit => 'B', min => 0, label_extra_instance => 1 } + ] + } + }, + { label => 'space-failed', nlabel => 'storage.space.failed.bytes', set => { + key_values => [ { name => 'failed' } ], + output_template => 'failed: %s %s', + output_change_bytes => 1, + perfdatas => [ + { template => '%s', unit => 'B', min => 0, label_extra_instance => 1 } + ] + } + } + ]; + + $self->{maps_counters}->{efficiency} = [ + { label => 'compaction', nlabel => 'storage.space.compaction.ratio.count', set => { + key_values => [ { name => 'compaction' } ], + output_template => 'compaction: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { label => 'dedup', nlabel => 'storage.space.deduplication.ratio.count', set => { + key_values => [ { name => 'dedup' } ], + output_template => 'deduplication: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { label => 'compression', nlabel => 'storage.space.compression.ratio.count', set => { + key_values => [ { name => 'compression' } ], + output_template => 'compression: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { label => 'data-reduction', nlabel => 'storage.space.data_reduction.ratio.count', set => { + key_values => [ { name => 'data_reduction' } ], + output_template => 'data reduction: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { label => 'overprovisioning', nlabel => 'storage.space.overprovisioning.ratio.count', set => { + key_values => [ { name => 'overprovisioning' } ], + output_template => 'overprovisioning: %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-type:s' => { name => 'filter_type' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my ($content) = $options{custom}->execute_command( + commands => [ + 'echo "===spaceTotal==="', + 'showsys -space', + 'echo "===spaceSSD==="', + 'showsys -space -devtype SSD', + 'echo "===spaceFC==="', + 'showsys -space -devtype SSD', + 'echo "===spaceNL==="', + 'showsys -space -devtype SSD' + ] + ); + + $self->{storages} = {}; + while ($content =~ /(===space(.*?)=.*?)(?====space|\Z$)/msig) { + my ($type, $entry) = ($2, $1); + + next if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' && + $type !~ /$self->{option_results}->{filter_type}/); + + my ($total, $free, $allocated, $unavailable, $failed); + $total = $1 * 1000 * 1000 if ($entry =~ /^Total\s+Capacity\s+:\s*(\d+)/m); + $free = $1 * 1000 * 1000 if ($entry =~ /^ Free\s+:\s*(\d+)/m); + $allocated = $1 * 1000 * 1000 if ($entry =~ /^ Allocated\s+:\s*(\d+)/m); + $unavailable = $1 * 1000 * 1000 if ($entry =~ /^ Unavailable\s+:\s*(\d+)/m); + $failed = $1 * 1000 * 1000 if ($entry =~ /^ Failed\s+:\s*(\d+)/m); + + next if (!defined($total) || $total == 0); + + $self->{storages}->{$type} = { + type => $type, + space => { + total => $total, + free => $free, + used => $total - $free, + prct_used => ($total - $free) * 100 / $total, + prct_free => $free * 100 / $total, + unavailable => $unavailable, + failed => $failed + }, + efficiency => {} + }; + + $self->{storages}->{$type}->{efficiency}->{compaction} = $1 if ($entry =~ /^Compaction\s+:\s*([0-9\.]+)/m); + $self->{storages}->{$type}->{efficiency}->{dedup} = $1 if ($entry =~ /^Dedup\s+:\s*([0-9\.]+)/m); + $self->{storages}->{$type}->{efficiency}->{compression} = $1 if ($entry =~ /^Compression\s+:\s*([0-9\.]+)/m); + $self->{storages}->{$type}->{efficiency}->{data_reduction} = $1 if ($entry =~ /^Data\s+Reduction\s+:\s*([0-9\.]+)/m); + $self->{storages}->{$type}->{efficiency}->{overprovisioning} = $1 if ($entry =~ /^Overprovisioning\s+:\s*([0-9\.]+)/m); + + } + + if (scalar(%{$self->{storages}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get storages information"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check storage capacities. + +=over 8 + +=item B<--filter-type> + +Filter storages by type (can be a regexp). + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'space-usage', 'space-usage-free', 'space-usage-prct', +'space-unavailable', 'space-failed', +'compaction', 'dedup', 'compression', 'data-reduction', 'overprovisioning'. + +=back + +=cut diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/battery.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/battery.pm index 64c7e2c4c..455543c8a 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/battery.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/battery.pm @@ -48,34 +48,44 @@ sub check { next if (!/^\s*\S+\s+(\d+)\s+(\d+)\s+\S+\s+(\S+)\s+(\d+)/); my ($psu_id, $battery_id, $battery_state, $battery_chrg_lvl) = ($1, $2, $3, $4); my $instance = $psu_id . '.' . $battery_id; - + next if ($self->check_filter(section => 'battery', instance => $instance)); $self->{components}->{battery}->{total}++; - $self->{output}->output_add(long_msg => sprintf("battery '%s' on power supply '%s' status is '%s' [instance: %s, charge level: %d%%]", - $psu_id, $battery_id, $battery_state, $instance, $battery_chrg_lvl) - ); + $self->{output}->output_add( + long_msg => sprintf( + "battery '%s' on power supply '%s' status is '%s' [instance: %s, charge level: %d%%]", + $psu_id, $battery_id, $battery_state, $instance, $battery_chrg_lvl + ) + ); my $exit = $self->get_severity(label => 'default', section => 'battery', value => $battery_state); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Battery '%s' on power supply '%s' status is '%s'", - $psu_id, $battery_id, $battery_state, $instance)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "Battery '%s' on power supply '%s' status is '%s'", + $psu_id, $battery_id, $battery_state, $instance + ) + ); } my ($exit2, $warn, $crit, $checked) = $self->get_severity_numeric(section => 'battery.charge', instance => $instance, value => $battery_chrg_lvl); if (!$self->{output}->is_status(value => $exit2, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit2, - short_msg => sprintf("Battery '%s' on power supply '%s' charge level is %s %%", $psu_id, $battery_id, $battery_chrg_lvl)); + $self->{output}->output_add( + severity => $exit2, + short_msg => sprintf("Battery '%s' on power supply '%s' charge level is %s %%", $psu_id, $battery_id, $battery_chrg_lvl) + ); } + $self->{output}->perfdata_add( - label => 'battery_charge', unit => '%', nlabel => 'hardware.battery.charge.percentage', + unit => '%', instances => ['psu' . $psu_id, 'battery' . $battery_id], value => $battery_chrg_lvl, warning => $warn, critical => $crit, - min => 0, max => 100, + min => 0, max => 100 ); } } diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/cim.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/cim.pm index d25aa59e6..9608c7c16 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/cim.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/cim.pm @@ -51,21 +51,32 @@ sub check { next if ($self->check_filter(section => 'cim', instance => $instance)); $self->{components}->{cim}->{total}++; - $self->{output}->output_add(long_msg => sprintf("cim service state is '%s' [status: '%s'] [SLP on port %d is %s] [HTTP on port %d is %s] [HTTPS on port %d is %s] [instance: %s]", - $service_state, $service_status, $slp_port, $slp_state, $http_port, $http_state, $https_port ,$https_state, $instance) - ); + $self->{output}->output_add( + long_msg => sprintf( + "cim service state is '%s' [status: '%s'] [SLP on port %d is %s] [HTTP on port %d is %s] [HTTPS on port %d is %s] [instance: %s]", + $service_state, $service_status, $slp_port, $slp_state, $http_port, $http_state, $https_port ,$https_state, $instance + ) + ); my $exit = $self->get_severity(label => 'default.state', section => 'cim.state', value => $service_state); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("cim service state is '%s' [instance: %s]", - $service_state, $instance)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "cim service state is '%s' [instance: %s]", + $service_state, $instance + ) + ); } $exit = $self->get_severity(label => 'default.status', section => 'cim.status', value => $service_status); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("cim service status is '%s' [instance: %s]", - $service_status, $instance)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "cim service status is '%s' [instance: %s]", + $service_status, $instance + ) + ); } } } diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/disk.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/disk.pm index e9f1080ff..3fb2e5424 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/disk.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/disk.pm @@ -44,7 +44,7 @@ sub check { return if ($self->check_filter(section => 'disk')); return if ($self->{results} !~ /===showdisk===.*?\n(.*?)(===|\Z)/msi); - my @results = split /\n/, $1; + my @results = split(/\n/, $1); foreach (@results) { next if (!/^\s*(\d+)\s+(\S+)/); @@ -53,14 +53,22 @@ sub check { next if ($self->check_filter(section => 'disk', instance => $instance)); $self->{components}->{disk}->{total}++; - $self->{output}->output_add(long_msg => sprintf("disk '%s' state is '%s' [instance: '%s']", - $instance, $state, $instance) - ); + $self->{output}->output_add( + long_msg => sprintf( + "disk '%s' state is '%s' [instance: '%s']", + $instance, $state, $instance + ) + ); + my $exit = $self->get_severity(label => 'default', section => 'disk', value => $state); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Disk '%s' state is '%s'", - $instance, $state)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "Disk '%s' state is '%s'", + $instance, $state + ) + ); } } } diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/node.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/node.pm index 1340e282a..196cca457 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/node.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/node.pm @@ -42,7 +42,7 @@ sub check { return if ($self->check_filter(section => 'node')); return if ($self->{results} !~ /===shownode===.*?\n(.*?)(===|\Z)/msi); - my @results = split /\n/, $1; + my @results = split(/\n/, $1); foreach (@results) { next if (!/^\s*(\d+)\s+(\S+)\s+(\S+)/); @@ -51,14 +51,22 @@ sub check { next if ($self->check_filter(section => 'node', instance => $instance)); $self->{components}->{node}->{total}++; - $self->{output}->output_add(long_msg => sprintf("node '%s' state is '%s' [instance: '%s'] [detailed state: %s]", - $instance, $state, $instance, $detail_state) - ); + $self->{output}->output_add( + long_msg => sprintf( + "node '%s' state is '%s' [instance: '%s'] [detailed state: %s]", + $instance, $state, $instance, $detail_state + ) + ); + my $exit = $self->get_severity(label => 'default', section => 'node', value => $state); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Node '%s' state is '%s'", - $instance, $state)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "Node '%s' state is '%s'", + $instance, $state + ) + ); } } } diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/port.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/port.pm index 0dfd3e8ea..2279fa368 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/port.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/port.pm @@ -59,14 +59,22 @@ sub check { next if ($self->check_filter(section => 'port', instance => $instance)); $self->{components}->{port}->{total}++; - $self->{output}->output_add(long_msg => sprintf("port '%s' state is '%s' [instance: '%s']", - $nsp, $state, $instance) - ); + $self->{output}->output_add( + long_msg => sprintf( + "port '%s' state is '%s' [instance: '%s']", + $nsp, $state, $instance + ) + ); + my $exit = $self->get_severity(section => 'port', value => $state); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("port '%s' state is '%s'", - $nsp, $state)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "port '%s' state is '%s'", + $nsp, $state + ) + ); } } } diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/psu.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/psu.pm index dc2cd0fa0..ec8a9ff50 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/psu.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/psu.pm @@ -51,14 +51,22 @@ sub check { next if ($self->check_filter(section => 'psu', instance => $instance)); $self->{components}->{psu}->{total}++; - $self->{output}->output_add(long_msg => sprintf("power supply '%s' state is '%s' [instance: '%s'] [ac state: %s] [dc state: %s]", - $instance, $psu_state, $instance, $ac_state, $dc_state) - ); + $self->{output}->output_add( + long_msg => sprintf( + "power supply '%s' state is '%s' [instance: '%s'] [ac state: %s] [dc state: %s]", + $instance, $psu_state, $instance, $ac_state, $dc_state + ) + ); + my $exit = $self->get_severity(label => 'default', section => 'psu', value => $psu_state); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Power supply '%s' state is '%s'", - $instance, $psu_state)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "Power supply '%s' state is '%s'", + $instance, $psu_state + ) + ); } } } diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/sensor.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/sensor.pm index d2c8192c5..7307bac75 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/sensor.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/sensor.pm @@ -78,14 +78,17 @@ sub check { my ($name, $reading, $unit, $lo_limit, $hi_limit) = (centreon::plugins::misc::trim($1), $2, $3, $4, $5); my $instance = 'node' . $node_id . '.' . $name; - next if ($self->check_filter(section => 'sensor', instance => $instance)); + next if ($self->check_filter(section => 'sensor', instance => $instance, name => $name)); $self->{components}->{sensor}->{total}++; - $self->{output}->output_add(long_msg => sprintf("sensor '%s' on node '%s' is %s %s [instance: %s]", - $name, $node_id, $reading, $unit, $instance) - ); - - my ($exit, $warn, $crit, $checked) = $self->get_severity_numeric(section => 'sensor', instance => $instance, value => $reading); + $self->{output}->output_add( + long_msg => sprintf( + "sensor '%s' on node '%s' is %s %s [instance: %s]", + $name, $node_id, $reading, $unit, $instance + ) + ); + + my ($exit, $warn, $crit, $checked) = $self->get_severity_numeric(section => 'sensor', instance => $instance, name => $name, value => $reading); $lo_limit = ($lo_limit =~ s/.*?(\d+(\.\d+)?).*/$1/) ? $lo_limit : undef; $hi_limit = ($hi_limit =~ s/.*?(\d+(\.\d+)?).*/$1/) ? $hi_limit : undef; if ($checked == 0 && (defined($lo_limit) || defined($hi_limit))) { @@ -96,19 +99,25 @@ sub check { $exit = $self->{perfdata}->threshold_check( value => $reading, - threshold => [ { label => 'critical-sensor-instance-' . $instance, exit_litteral => 'critical' }, - { label => 'warning-sensor-instance-' . $instance, exit_litteral => 'warning' } ]); + threshold => [ + { label => 'critical-sensor-instance-' . $instance, exit_litteral => 'critical' }, + { label => 'warning-sensor-instance-' . $instance, exit_litteral => 'warning' } + ] + ); $warn = $self->{perfdata}->get_perfdata_for_output(label => 'warning-sensor-instance-' . $instance); $crit = $self->{perfdata}->get_perfdata_for_output(label => 'critical-sensor-instance-' . $instance); } if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("Sensor '%s' on node '%s' is %s %s ", $name, $node_id, $reading, $unit)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf("Sensor '%s' on node '%s' is %s %s ", $name, $node_id, $reading, $unit) + ); } + $self->{output}->perfdata_add( - label => 'sensor', unit => $unit, nlabel => 'hardware.sensor.' . $unit_new_perf->{$unit}, + unit => $unit, instances => ['node' . $node_id, $name], value => $reading, warning => $warn, diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/components/wsapi.pm b/centreon-plugins/storage/hp/3par/ssh/mode/components/wsapi.pm index 76535a5e9..446e05fc2 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/components/wsapi.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/components/wsapi.pm @@ -51,21 +51,32 @@ sub check { next if ($self->check_filter(section => 'wsapi', instance => $instance)); $self->{components}->{wsapi}->{total}++; - $self->{output}->output_add(long_msg => sprintf("wsapi service state is '%s' [status: '%s'] [HTTP on port %d is %s] [HTTPS on port %d is %s] [instance: %s]", - $service_state, $service_status, $http_port, $http_state, $https_port ,$https_state, $instance) - ); + $self->{output}->output_add( + long_msg => sprintf( + "wsapi service state is '%s' [status: '%s'] [HTTP on port %d is %s] [HTTPS on port %d is %s] [instance: %s]", + $service_state, $service_status, $http_port, $http_state, $https_port ,$https_state, $instance + ) + ); my $exit = $self->get_severity(label => 'default.state', section => 'wsapi.state', value => $service_state); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("wsapi service state is '%s' [instance: %s]", - $service_state, $instance)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "wsapi service state is '%s' [instance: %s]", + $service_state, $instance + ) + ); } $exit = $self->get_severity(label => 'default.status', section => 'wsapi.status', value => $service_status); if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) { - $self->{output}->output_add(severity => $exit, - short_msg => sprintf("wsapi service status is '%s' [instance: %s]", - $service_status, $instance)); + $self->{output}->output_add( + severity => $exit, + short_msg => sprintf( + "wsapi service status is '%s' [instance: %s]", + $service_status, $instance + ) + ); } } } diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/diskusage.pm b/centreon-plugins/storage/hp/3par/ssh/mode/diskusage.pm index 1e0df9aec..42df69a45 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/diskusage.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/diskusage.pm @@ -24,13 +24,12 @@ use base qw(centreon::plugins::templates::counter); use strict; use warnings; -use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold catalog_status_calc); +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); sub custom_status_output { my ($self, %options) = @_; - my $msg = 'status : ' . $self->{result_values}->{status}; - return $msg; + return 'status: ' . $self->{result_values}->{status}; } sub custom_usage_output { @@ -39,56 +38,59 @@ sub custom_usage_output { 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}); - my $msg = sprintf("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}); - return $msg; + return sprintf( + "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_disk_output { + my ($self, %options) = @_; + + return "Disk '" . $options{instance_value}->{display} . "' "; } sub set_counters { my ($self, %options) = @_; $self->{maps_counters_type} = [ - { name => 'disk', type => 1, cb_prefix_output => 'prefix_disk_output', message_multiple => 'All disks are ok', sort_method => 'num' }, + { name => 'disk', type => 1, cb_prefix_output => 'prefix_disk_output', message_multiple => 'All disks are ok', sort_method => 'num' } ]; $self->{maps_counters}->{disk} = [ - { label => 'status', threshold => 0, set => { + { label => 'status', type => 2, critical_default => '%{status} !~ /normal/i', set => { key_values => [ { name => 'status' }, { name => 'display' } ], - closure_custom_calc => \&catalog_status_calc, closure_custom_output => $self->can('custom_status_output'), closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => \&catalog_status_threshold, + closure_custom_threshold_check => \&catalog_status_threshold_ng } }, { label => 'usage', nlabel => 'disk.space.usage.bytes', set => { - key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'display' }, ], + 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 => [ - { label => 'used', value => 'used', template => '%d', min => 0, max => 'total', - unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' }, - ], + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' } + ] } }, { label => 'usage-free', display_ok => 0, nlabel => 'disk.space.free.bytes', 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 => [ - { label => 'free', value => 'free', template => '%d', min => 0, max => 'total', - unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' }, - ], - } + 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 => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' } + ] + } }, { label => 'usage-prct', display_ok => 0, nlabel => 'disk.space.usage.percentage', set => { - key_values => [ { name => 'prct_used' }, { name => 'display' } ], - output_template => 'Used : %.2f %%', - perfdatas => [ - { label => 'used_prct', value => 'prct_used', template => '%.2f', min => 0, max => 100, - unit => '%', label_extra_instance => 1, instance_use => 'display' }, - ], - } - }, + key_values => [ { name => 'prct_used' }, { name => 'display' } ], + output_template => 'Used : %.2f %%', + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'display' } + ] + } + } ]; } @@ -98,27 +100,12 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - "filter-name:s" => { name => 'filter_name' }, - "warning-status:s" => { name => 'warning_status', default => '' }, - "critical-status:s" => { name => 'critical_status', default => '%{status} !~ /normal/i' }, + 'filter-name:s' => { name => 'filter_name' } }); return $self; } -sub check_options { - my ($self, %options) = @_; - $self->SUPER::check_options(%options); - - $self->change_macros(macros => ['warning_status', 'critical_status']); -} - -sub prefix_disk_output { - my ($self, %options) = @_; - - return "Disk '" . $options{instance_value}->{display} . "' "; -} - sub manage_selection { my ($self, %options) = @_; @@ -148,10 +135,10 @@ sub manage_selection { used => $total - $free, free => $free, prct_used => ($total - $free) * 100 / $total, - prct_free => 100 - (($total - $free) * 100 / $total), + prct_free => 100 - (($total - $free) * 100 / $total) }; } - + if (scalar(keys %{$self->{disk}}) <= 0) { $self->{output}->add_option_msg(short_msg => "No disk found."); $self->{output}->option_exit(); diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/hardware.pm b/centreon-plugins/storage/hp/3par/ssh/mode/hardware.pm index cdaa0adb7..cc97a8dda 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/hardware.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/hardware.pm @@ -38,21 +38,21 @@ sub set_system { ['ok', 'OK'], ['new', 'OK'], ['degraded', 'WARNING'], - ['failed', 'CRITICAL'], + ['failed', 'CRITICAL'] ], 'default.status' => [ ['enabled', 'OK'], - ['.*', 'CRITICAL'], + ['.*', 'CRITICAL'] ], 'default.state' => [ ['active', 'OK'], - ['.*', 'CRITICAL'], + ['.*', 'CRITICAL'] ], port => [ ['ready', 'OK'], ['loss_sync', 'WARNING'], - ['offline', 'CRITICAL'], - ], + ['offline', 'CRITICAL'] + ] }; $self->{components_path} = 'storage::hp::3par::ssh::mode::components'; @@ -69,7 +69,7 @@ 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 => {}); $self->{commands} = []; @@ -89,13 +89,17 @@ Check components. =item B<--component> Which component to check (Default: '.*'). -Can be: 'battery'. +Can be: 'battery', 'cim', 'port', 'node', 'disk', 'psu', 'sensor', 'wsapi'. =item B<--filter> Exclude some parts (comma seperated list) (Example: --filter=battery --filter=cim) Can also exclude specific instance: --filter=port,free +=item B<--add-name-instance> + +Add literal description for instance value (used in filter and threshold options). + =item B<--no-component> Return an error if no compenents are checked. diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/nodes.pm b/centreon-plugins/storage/hp/3par/ssh/mode/nodes.pm new file mode 100644 index 000000000..5873dac24 --- /dev/null +++ b/centreon-plugins/storage/hp/3par/ssh/mode/nodes.pm @@ -0,0 +1,214 @@ +# +# Copyright 2022 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::hp::3par::ssh::mode::nodes; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub node_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking node '%s'", + $options{instance_value}->{node_id} + ); +} + +sub prefix_node_output { + my ($self, %options) = @_; + + return sprintf( + "node '%s' ", + $options{instance_value}->{node_id} + ); +} + +sub prefix_cpu_output { + my ($self, %options) = @_; + + return "CPU '" . $options{instance_value}->{cpu_id} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'nodes', type => 3, cb_prefix_output => 'prefix_node_output', cb_long_output => 'node_long_output', indent_long_output => ' ', message_multiple => 'All nodes are ok', + group => [ + { name => 'global', type => 0 }, + { name => 'cpu', display_long => 1, cb_prefix_output => 'prefix_cpu_output', + message_multiple => 'all CPUs usage are ok', type => 1, skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { + label => 'status', + type => 2, + critical_default => '%{status} !~ /ok/i', + set => { + key_values => [ { name => 'status' }, { name => 'node_id' } ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + $self->{maps_counters}->{cpu} = [ + { label => 'cpu-utilization', nlabel => 'core.cpu.utilization.percentage', set => { + key_values => [ { name => 'cpu_usage' } ], + output_template => 'usage: %.2f %%', + perfdatas => [ + { template => '%.2f', unit => '%', min => 0, max => 100, 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-node-id:s' => { name => 'filter_node_id' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my ($content) = $options{custom}->execute_command( + commands => [ + 'echo "===statcpu==="', + 'statcpu -iter 1 -d 1', + 'echo "===shownode==="', + 'shownode -s' + ] + ); + #===statcpu=== + #17:34:58 01/27/2022 + #node,cpu user sys idle intr/s ctxt/s + # 0,0 1 0 99 + # 0,1 1 1 98 + # 0,2 0 1 99 + # 0,3 1 1 98 + # 0,4 0 1 99 + # 0,5 0 1 99 + # 0,6 4 1 95 + # 0,7 0 0 100 + # 0,total 1 1 98 7276 9026 + # + # 1,0 0 0 100 + # 1,1 0 0 100 + # 1,2 0 0 100 + # 1,3 0 1 99 + # 1,4 0 0 100 + # 1,5 0 1 99 + # 1,6 0 2 98 + # 1,7 0 0 100 + # 1,total 0 1 99 5312 4799 + #===shownode=== + #Node -State- -Detailed_State- + # 0 OK OK + # 1 OK OK + + $self->{nodes} = {}; + if ($content =~ /===shownode===.*?\n(.*?)(===|\Z)/msi) { + foreach my $line (split(/\n/, $1)) { + next if ($line !~ /^\s*(\d+)\s+(\S+)/); + my ($node_id, $status) = ($1, $2); + + next if (defined($self->{option_results}->{filter_node_id}) && $self->{option_results}->{filter_node_id} ne '' && + $node_id !~ /$self->{option_results}->{filter_node_id}/); + $self->{nodes}->{'node' . $node_id} = { + node_id => $node_id, + global => { node_id => $node_id, status => $status }, + cpu => {} + }; + } + } + + if ($content =~ /===statcpu===.*?\n(.*?)(===|\Z)/msi) { + foreach my $line (split(/\n/, $1)) { + next if ($line !~ /^\s*(\d+),(\d+)\s+\d+\s+\d+\s+(\d+)/); + my ($node_id, $cpu_id, $idle) = ($1, $2, $3); + next if (!defined($self->{nodes}->{'node' . $node_id})); + + $self->{nodes}->{'node' . $node_id}->{cpu}->{'cpu' . $cpu_id} = { + cpu_id => $cpu_id, + cpu_usage => 100 - $idle + }; + } + } + + if (scalar(%{$self->{nodes}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get nodes information"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check nodes. + +=over 8 + +=item B<--filter-node-id> + +Filter nodes by id (can be a regexp). + +=item B<--unknown-status> + +Set unknown threshold for status. +Can used special variables like: %{status}, %{node_id} + +=item B<--warning-status> + +Set warning threshold for status. +Can used special variables like: %{status}, %{node_id} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} !~ /ok/i'). +Can used special variables like: %{status}, %{node_id} + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'cpu-utilization'. + +=back + +=cut diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/psu.pm b/centreon-plugins/storage/hp/3par/ssh/mode/psu.pm new file mode 100644 index 000000000..a9a1b3f6b --- /dev/null +++ b/centreon-plugins/storage/hp/3par/ssh/mode/psu.pm @@ -0,0 +1,322 @@ +# +# Copyright 2022 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::hp::3par::ssh::mode::psu; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub psu_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking node '%s' power supply '%s'", + $options{instance_value}->{node_id}, + $options{instance_value}->{psu_id} + ); +} + +sub prefix_psu_output { + my ($self, %options) = @_; + + return sprintf( + "node '%s' power supply '%s' ", + $options{instance_value}->{node_id}, + $options{instance_value}->{psu_id} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'psu', type => 3, cb_prefix_output => 'prefix_psu_output', cb_long_output => 'psu_long_output', indent_long_output => ' ', message_multiple => 'All power supplies are ok', + group => [ + { name => 'global', type => 0 }, + { name => 'ac', type => 0 }, + { name => 'dc', type => 0 }, + { name => 'fan', type => 0 }, + { name => 'battery', type => 0 } + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { + label => 'status', + type => 2, + critical_default => '%{status} !~ /ok/i', + set => { + key_values => [ { name => 'status' }, { name => 'node_id' }, { name => 'psu_id' } ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + foreach ('ac', 'dc', 'fan', 'battery') { + $self->{maps_counters}->{$_} = [ + { + label => $_ . '-status', + type => 2, + critical_default => '%{status} !~ /ok/i', + set => { + key_values => [ { name => 'status' }, { name => 'node_id' }, { name => 'psu_id' } ], + output_template => $_ . ' status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + } + + push @{$self->{maps_counters}->{battery}}, + { label => 'charge-remaining', nlabel => 'battery.charge.remaining.percent', set => { + key_values => [ { name => 'capacity' }, { name => 'node_id' }, { name => 'psu_id' } ], + output_template => 'remaining capacity: %.2f %%', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => ['node' . $self->{result_values}->{node_id}, 'psu' . $self->{result_values}->{psu_id}], + value => sprintf('%.2f', $self->{result_values}->{capacity}), + 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 + ); + } + } + }, + { label => 'charge-remaining-minutes', nlabel => 'battery.charge.remaining.minutes', set => { + key_values => [ { name => 'time' }, { name => 'node_id' }, { name => 'psu_id' } ], + output_template => 'remaining time: %s minutes', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'm', + instances => ['node' . $self->{result_values}->{node_id}, 'psu' . $self->{result_values}->{psu_id}], + value => sprintf('%s', $self->{result_values}->{time}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); + } + } + }; +} + +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-node-id:s' => { name => 'filter_node_id' }, + 'filter-psu-id:s' => { name => 'filter_psu_id' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my ($content) = $options{custom}->execute_command(commands => ['shownode -ps -verbose']); + #---------------Node 0 PS 0---------------- + #Node ID : 0 + #Power Supply ID : 0 + #Manufacturer : XYRATEX + #Assembly Part : 682372-001 + #Assembly Serial Number : 5CQLQAXXXXXXX1 + #State : OK + #Fan State : OK + #Fan Speed : Low + #AC State : OK + #DC State : OK + #Battery State : OK + #Battery Detail State : normal + #Battery Charge State : FullyCharged + #Battery Charge Level(%) : 100 + #Max Battery Life(mins) : 16 + # + #---------------Node 0 PS 1---------------- + #Node ID : 0 + #Power Supply ID : 1 + #Manufacturer : XYRATEX + #Assembly Part : 682372-001 + #Assembly Serial Number : 5CQLQAXXXXXXX2 + #State : OK + #Fan State : OK + #Fan Speed : Low + #AC State : OK + #DC State : OK + #Battery State : OK + #Battery Detail State : normal + #Battery Charge State : FullyCharged + #Battery Charge Level(%) : 100 + #Max Battery Life(mins) : 16 + + $self->{psu} = {}; + while ($content =~ /^(Node ID.*?)(?=Node ID|\Z$)/msig) { + my $entry = $1; + my ($node_id, $psu_id); + $node_id = $1 if ($entry =~ /^Node\s+ID\s*:\s*(\S+)/msi); + $psu_id = $1 if ($entry =~ /^Power\s+Supply\s+ID\s*:\s*(\S+)/msi); + next if (!defined($node_id)); + + next if (defined($self->{option_results}->{filter_node_id}) && $self->{option_results}->{filter_node_id} ne '' && + $node_id !~ /$self->{option_results}->{filter_node_id}/); + next if (defined($self->{option_results}->{filter_psu_id}) && $self->{option_results}->{filter_psu_id} ne '' && + $psu_id !~ /$self->{option_results}->{filter_psu_id}/); + + $self->{psu}->{ $node_id . ':' . $psu_id } = { + node_id => $node_id, + psu_id => $psu_id, + global => { node_id => $node_id, psu_id => $psu_id }, + ac => { node_id => $node_id, psu_id => $psu_id }, + dc => { node_id => $node_id, psu_id => $psu_id }, + fan => { node_id => $node_id, psu_id => $psu_id }, + battery => { node_id => $node_id, psu_id => $psu_id }, + }; + $self->{psu}->{ $node_id . ':' . $psu_id }->{global}->{status} = $1 if ($entry =~ /^State\s*:\s*(\S+)/msi); + $self->{psu}->{ $node_id . ':' . $psu_id }->{ac}->{status} = $1 if ($entry =~ /^AC\s+State\s*:\s*(\S+)/msi); + $self->{psu}->{ $node_id . ':' . $psu_id }->{dc}->{status} = $1 if ($entry =~ /^DC\s+State\s*:\s*(\S+)/msi); + $self->{psu}->{ $node_id . ':' . $psu_id }->{fan}->{status} = $1 if ($entry =~ /^Fan\s+State\s*:\s*(\S+)/msi); + $self->{psu}->{ $node_id . ':' . $psu_id }->{battery}->{status} = $1 if ($entry =~ /^Battery\s+State\s*:\s*(\S+)/msi); + $self->{psu}->{ $node_id . ':' . $psu_id }->{battery}->{capacity} = $1 if ($entry =~ /^Battery\s+Charge\s+Level.*?:\s*(\S+)/msi); + $self->{psu}->{ $node_id . ':' . $psu_id }->{battery}->{time} = $1 if ($entry =~ /^Max\s+Battery\s+Life.*?:\s*(\S+)/msi); + } + + if (scalar(%{$self->{psu}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get power supplies information"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check power supplies. + +=over 8 + +=item B<--filter-node-id> + +Filter nodes by id (can be a regexp). + +=item B<--filter-psu-id> + +Filter power supplies by id (can be a regexp). + +=item B<--unknown-status> + +Set unknown threshold for status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--warning-status> + +Set warning threshold for status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} !~ /ok/i'). +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--unknown-ac-status> + +Set unknown threshold for AC status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--warning-ac-status> + +Set warning threshold for AC status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--critical-ac-status> + +Set critical threshold for AC status (Default: '%{status} !~ /ok/i'). +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--unknown-dc-status> + +Set unknown threshold for DC status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--warning-dc-status> + +Set warning threshold for DC status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--critical-dc-status> + +Set critical threshold for DC status (Default: '%{status} !~ /ok/i'). +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--unknown-fan-status> + +Set unknown threshold for fan status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--warning-fan-status> + +Set warning threshold for fan status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--critical-fan-status> + +Set critical threshold for fan status (Default: '%{status} !~ /ok/i'). +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--unknown-battery-status> + +Set unknown threshold for battery status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--warning-battery-status> + +Set warning threshold for battery status. +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--critical-battery-status> + +Set critical threshold for battery status (Default: '%{status} !~ /ok/i'). +Can used special variables like: %{status}, %{node_id}, %{psu_id} + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'charge-remaining', 'charge-remaining-minutes. + +=back + +=cut diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/time.pm b/centreon-plugins/storage/hp/3par/ssh/mode/time.pm new file mode 100644 index 000000000..b13e7a75a --- /dev/null +++ b/centreon-plugins/storage/hp/3par/ssh/mode/time.pm @@ -0,0 +1,210 @@ +# +# Copyright 2022 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::hp::3par::ssh::mode::time; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::misc; +use DateTime; + +sub custom_usage_output { + my ($self, %options) = @_; + + return sprintf( + 'time offset %d second(s): %s', + $self->{result_values}->{offset}, + $self->{result_values}->{date} + ); +} + +sub prefix_node_output { + my ($self, %options) = @_; + + return "Node '" . $options{instance_value}->{id} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'nodes', type => 1, cb_prefix_output => 'prefix_node_output', message_multiple => 'All nodes uptime are ok' } + ]; + + $self->{maps_counters}->{nodes} = [ + { label => 'offset', nlabel => 'node.time.offset.seconds', set => { + key_values => [ { name => 'offset' }, { name => 'date' } ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { template => '%d', unit => 's', , 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-node-id:s' => { name => 'filter_node_id' }, + 'ntp-hostname:s' => { name => 'ntp_hostname' }, + 'ntp-port:s' => { name => 'ntp_port', default => 123 }, + 'timezone:s' => { name => 'timezone' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (defined($self->{option_results}->{ntp_hostname})) { + centreon::plugins::misc::mymodule_load( + output => $self->{output}, module => 'Net::NTP', + error_msg => "Cannot load module 'Net::NTP'." + ); + } +} + +sub get_target_time { + my ($self, %options) = @_; + + # 2022-01-25 15:55:37 MSK (Europe/Moscow) + return undef if ($options{date} !~ /^\s*(\d{4})-(\d{2})-(\d{2})\s+(\d+):(\d+):(\d+)\s+\S+\s+\(\S+\)/); + + my $timezone = defined($7) ? $7 : 'UTC'; + if (defined($self->{option_results}->{timezone}) && $self->{option_results}->{timezone} ne '') { + $timezone = $self->{option_results}->{timezone}; + } + + my $tz = centreon::plugins::misc::set_timezone(name => $timezone); + my $dt = DateTime->new( + year => $1, + month => $2, + day => $3, + hour => $4, + minute => $5, + second => $6, + %$tz + ); + + my @remote_date = ($dt->year, $dt->month, $dt->day, $dt->hour, $dt->minute, $dt->second); + return ($dt->epoch(), \@remote_date, $timezone); +} + +sub manage_selection { + my ($self, %options) = @_; + + my ($content) = $options{custom}->execute_command(commands => ['showdate']); + #Node Date + #0 2022-01-25 15:55:37 MSK (Europe/Moscow) + #1 2022-01-25 15:55:36 MSK (Europe/Moscow) + + my $ref_time; + if (defined($self->{option_results}->{ntp_hostname}) && $self->{option_results}->{ntp_hostname} ne '') { + my %ntp; + + eval { + %ntp = Net::NTP::get_ntp_response($self->{option_results}->{ntp_hostname}, $self->{option_results}->{ntp_port}); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Couldn't connect to ntp server: " . $@); + $self->{output}->option_exit(); + } + + $ref_time = $ntp{'Transmit Timestamp'}; + } else { + $ref_time = time(); + } + + $self->{nodes} = {}; + foreach my $line (split(/\n/, $content)) { + next if ($line !~ /^\s*(\d+)\s+(.*)\s*$/); + my ($node_id, $date) = ($1, $2); + + next if (defined($self->{option_results}->{filter_node_id}) && $self->{option_results}->{filter_node_id} ne '' && + $node_id !~ /$self->{option_results}->{filter_node_id}/); + + my ($distant_time, $remote_date, $timezone) = $self->get_target_time(date => $date); + next if (!defined($distant_time)); + + my $remote_date_formated = sprintf( + 'local time: %02d-%02d-%02dT%02d:%02d:%02d (%s)', + $remote_date->[0], $remote_date->[1], $remote_date->[2], + $remote_date->[3], $remote_date->[4], $remote_date->[5], $timezone + ); + my $offset = $distant_time - $ref_time; + + $self->{nodes}->{'node' . $node_id} = { + id => $node_id, + offset => sprintf('%d', $offset), + date => $remote_date_formated + }; + } + + if (scalar(%{$self->{nodes}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get nodes date"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check nodes time offset (use local time if ntp-host option is not set). + +=over 8 + +=item B<--filter-node-id> + +Filter nodes by id (can be a regexp). + +=item B<--ntp-hostname> + +Set the ntp hostname (if not set, localtime is used). + +=item B<--ntp-port> + +Set the ntp port (Default: 123). + +=item B<--timezone> + +Set the timezone for displaying the date (Default: UTC). + +=item B<--warning-offset> + +Time offset warning threshold (in seconds). + +=item B<--critical-offset> + +Time offset critical Threshold (in seconds). + +=back + +=cut diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/uptime.pm b/centreon-plugins/storage/hp/3par/ssh/mode/uptime.pm new file mode 100644 index 000000000..16189586f --- /dev/null +++ b/centreon-plugins/storage/hp/3par/ssh/mode/uptime.pm @@ -0,0 +1,197 @@ +# +# Copyright 2022 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::hp::3par::ssh::mode::uptime; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use DateTime; +use POSIX; +use centreon::plugins::misc; + +my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 }; +my $unitdiv_long = { s => 'seconds', w => 'weeks', d => 'days', h => 'hours', m => 'minutes' }; + +sub custom_uptime_output { + my ($self, %options) = @_; + + return sprintf( + 'uptime is: %s', + centreon::plugins::misc::change_seconds(value => $self->{result_values}->{uptime}, start => 'd') + ); +} + +sub custom_uptime_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => 'node.uptime.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} }, + unit => $self->{instance_mode}->{option_results}->{unit}, + instances => 'node' . $self->{result_values}->{id}, + value => floor($self->{result_values}->{uptime} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); +} + +sub custom_uptime_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => floor($self->{result_values}->{uptime} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }), + threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' }, + { label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' } + ] + ); +} + +sub prefix_node_output { + my ($self, %options) = @_; + + return "Node '" . $options{instance_value}->{id} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'nodes', type => 1, cb_prefix_output => 'prefix_node_output', message_multiple => 'All nodes uptime are ok' } + ]; + + $self->{maps_counters}->{nodes} = [ + { label => 'uptime', set => { + key_values => [ { name => 'uptime' }, { name => 'id' } ], + closure_custom_output => $self->can('custom_uptime_output'), + closure_custom_perfdata => $self->can('custom_uptime_perfdata'), + closure_custom_threshold_check => $self->can('custom_uptime_threshold') + } + } + ]; +} + +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-node-id:s' => { name => 'filter_node_id' }, + 'unit:s' => { name => 'unit', default => 's' }, + 'timezone:s' => { name => 'timezone' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) { + $self->{option_results}->{unit} = 's'; + } + $self->{option_results}->{timezone} = 'UTC' if (!defined($self->{option_results}->{timezone}) || $self->{option_results}->{timezone} eq ''); +} + +sub get_diff_time { + my ($self, %options) = @_; + + # 2021-09-16 10:50:15 MSK + return undef if ($options{date} !~ /^\s*(\d{4})-(\d{2})-(\d{2})\s+(\d+):(\d+):(\d+)/); + + my $tz = centreon::plugins::misc::set_timezone(name => $self->{option_results}->{timezone}); + my $dt = DateTime->new( + year => $1, + month => $2, + day => $3, + hour => $4, + minute => $5, + second => $6, + %$tz + ); + return (time() - $dt->epoch()); +} + +sub manage_selection { + my ($self, %options) = @_; + + my ($content) = $options{custom}->execute_command(commands => ['shownode -uptime']); + + #Node -------Up Since-------- + # 0 2021-09-16 10:50:15 MSK + # 1 2021-09-15 13:21:39 MSK + $self->{nodes} = {}; + foreach my $line (split(/\n/, $content)) { + next if ($line !~ /^\s*(\d+)\s+(.*)\s*$/); + my ($node_id, $date) = ($1, $2); + + next if (defined($self->{option_results}->{filter_node_id}) && $self->{option_results}->{filter_node_id} ne '' && + $node_id !~ /$self->{option_results}->{filter_node_id}/); + my $uptime = $self->get_diff_time(date => $date); + next if (!defined($uptime)); + + $self->{nodes}->{$node_id} = { + id => $node_id, + uptime => $uptime + }; + } + + if (scalar(%{$self->{nodes}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get nodes uptime"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check nodes uptime. + +=over 8 + +=item B<--filter-node-id> + +Filter nodes by id (can be a regexp). + +=item B<--timezone> + +Timezone options. Default is 'UTC'. + +=item B<--unit> + +Select the unit for performance data and thresholds. May be 's' for seconds, 'm' for minutes, +'h' for hours, 'd' for days, 'w' for weeks. Default is seconds + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'uptime'. + +=back + +=cut diff --git a/centreon-plugins/storage/hp/3par/ssh/mode/volumeusage.pm b/centreon-plugins/storage/hp/3par/ssh/mode/volumeusage.pm index 28316360b..261555990 100644 --- a/centreon-plugins/storage/hp/3par/ssh/mode/volumeusage.pm +++ b/centreon-plugins/storage/hp/3par/ssh/mode/volumeusage.pm @@ -31,11 +31,12 @@ sub custom_usage_output { 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}); - my $msg = sprintf("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}); - return $msg; + return sprintf( + "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 set_counters { @@ -50,29 +51,26 @@ sub set_counters { 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 => [ - { label => 'used', value => 'used', template => '%d', min => 0, max => 'total', - unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' }, - ], + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' } + ] } }, { label => '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 => 'display' }, ], closure_custom_output => $self->can('custom_usage_output'), perfdatas => [ - { label => 'free', value => 'free', template => '%d', min => 0, max => 'total', - unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' }, - ], + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' } + ] } }, { label => 'usage-prct', display_ok => 0, nlabel => 'volume.space.usage.percentage', set => { key_values => [ { name => 'prct_used' }, { name => 'display' } ], output_template => 'Used : %.2f %%', perfdatas => [ - { label => 'used_prct', value => 'prct_used', template => '%.2f', min => 0, max => 100, - unit => '%', label_extra_instance => 1, instance_use => 'display' }, - ], + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'display' } + ] } - }, + } ]; } @@ -82,7 +80,7 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - "filter-name:s" => { name => 'filter_name' }, + 'filter-name:s' => { name => 'filter_name' } }); return $self; @@ -125,10 +123,10 @@ sub manage_selection { used => $used, free => ($total - $used) >= 0 ? ($total - $used) : 0, prct_used => $used * 100 / $total, - prct_free => (100 - ($used * 100 / $total) >= 0) ? (100 - ($used * 100 / $total)) : 0, + prct_free => (100 - ($used * 100 / $total) >= 0) ? (100 - ($used * 100 / $total)) : 0 }; } - + if (scalar(keys %{$self->{volume}}) <= 0) { $self->{output}->add_option_msg(short_msg => "No volume found."); $self->{output}->option_exit(); diff --git a/centreon-plugins/storage/hp/3par/ssh/plugin.pm b/centreon-plugins/storage/hp/3par/ssh/plugin.pm index 5b32a8ebd..a4ad96a3f 100644 --- a/centreon-plugins/storage/hp/3par/ssh/plugin.pm +++ b/centreon-plugins/storage/hp/3par/ssh/plugin.pm @@ -29,14 +29,20 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options); bless $self, $class; - $self->{version} = '1.0'; - %{$self->{modes}} = ( + $self->{modes} = { + 'afc' => 'storage::hp::3par::ssh::mode::afc', + 'cages' => 'storage::hp::3par::ssh::mode::cages', + 'capacity' => 'storage::hp::3par::ssh::mode::capacity', 'components' => 'storage::hp::3par::ssh::mode::hardware', 'disk-usage' => 'storage::hp::3par::ssh::mode::diskusage', - 'volume-usage' => 'storage::hp::3par::ssh::mode::volumeusage', - ); + 'nodes' => 'storage::hp::3par::ssh::mode::nodes', + 'psu' => 'storage::hp::3par::ssh::mode::psu', + 'time' => 'storage::hp::3par::ssh::mode::time', + 'uptime' => 'storage::hp::3par::ssh::mode::uptime', + 'volume-usage' => 'storage::hp::3par::ssh::mode::volumeusage' + }; - $self->{custom_modes}{ssh} = 'storage::hp::3par::ssh::custom::custom'; + $self->{custom_modes}->{ssh} = 'storage::hp::3par::ssh::custom::custom'; return $self; }