diff --git a/src/storage/stormagic/svsan/snmp/mode/listmirrors.pm b/src/storage/stormagic/svsan/snmp/mode/listmirrors.pm new file mode 100644 index 0000000000..92c26c5911 --- /dev/null +++ b/src/storage/stormagic/svsan/snmp/mode/listmirrors.pm @@ -0,0 +1,141 @@ +# +# Copyright 2026 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::stormagic::svsan::snmp::mode::listmirrors; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use centreon::plugins::misc qw/is_excluded/; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +my @labels = ('mirror_name', 'mirror_iqn', 'mirror_eui_64', 'mirror_nsh', 'mirror_cache_present', 'mirror_mbc_present'); + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +my $map_boolean = { + 0 => 'false', + 1 => 'true' +}; + +sub manage_selection { + my ($self, %options) = @_; + + my $mapping = { + mirrorName => { oid => '.1.3.6.1.4.1.38003.1.4.1.2' },# mirrorName, + mirrorIQN => { oid => '.1.3.6.1.4.1.38003.1.4.1.3' },# mirrorIQN, + mirrorEUI64 => { oid => '.1.3.6.1.4.1.38003.1.4.1.4' },# mirrorEUI64, + mirrorNSH => { oid => '.1.3.6.1.4.1.38003.1.4.1.9' },# mirrorNSH, + mirrorCachePresent => { oid => '.1.3.6.1.4.1.38003.1.4.1.10', map => $map_boolean },# mirrorCachePresent + mirrorMBCPresent => { oid => '.1.3.6.1.4.1.38003.1.4.1.15', map => $map_boolean },# mirrorMBCPresent + }; + + my $snmp_result = $options{snmp}->get_multiple_table( + oids => [ + { oid => $mapping->{mirrorName}->{oid} }, + { oid => $mapping->{mirrorIQN}->{oid} }, + { oid => $mapping->{mirrorEUI64}->{oid} }, + { oid => $mapping->{mirrorNSH}->{oid} }, + { oid => $mapping->{mirrorCachePresent}->{oid} }, + { oid => $mapping->{mirrorMBCPresent}->{oid} } + ], + return_type => 1, nothing_quit => 1 + ); + + my $results = {}; + foreach my $oid (keys %{$snmp_result}) { + next if ($oid !~ /^$mapping->{mirrorName}->{oid}\.(.*)$/); + + my $instance = $1; + my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance); + + $results->{$instance} = { + mirror_name => $result->{mirrorName}, + mirror_iqn => $result->{mirrorIQN}, + mirror_eui_64 => $result->{mirrorEUI64}, + mirror_nsh => $result->{mirrorNSH}, + mirror_cache_present => $result->{mirrorCachePresent}, + mirror_mbc_present => $result->{mirrorMBCPresent} + }; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach my $port (sort keys %$results) { + $self->{output}->output_add(long_msg => + join('', map("[$_: " . $results->{$port}->{$_} . ']', @labels)) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List mirror' + ); + $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 => [ @labels ]); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach (sort keys %$results) { + $self->{output}->add_disco_entry( + %{$results->{$_}} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List mirrors. + +=over 8 + +=back + +=cut diff --git a/src/storage/stormagic/svsan/snmp/mode/listpools.pm b/src/storage/stormagic/svsan/snmp/mode/listpools.pm new file mode 100644 index 0000000000..cb4807734c --- /dev/null +++ b/src/storage/stormagic/svsan/snmp/mode/listpools.pm @@ -0,0 +1,124 @@ +# +# Copyright 2026 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::stormagic::svsan::snmp::mode::listpools; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use centreon::plugins::misc qw/is_excluded/; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +my @labels = ('pool_name', 'pool_capacity'); + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $mapping = { + poolName => { oid => '.1.3.6.1.4.1.38003.1.2.1.2' },# poolName, + poolCapacity => { oid => '.1.3.6.1.4.1.38003.1.2.1.3' },# poolCapacity, + }; + + my $snmp_result = $options{snmp}->get_multiple_table( + oids => [ + { oid => $mapping->{poolName}->{oid} }, + { oid => $mapping->{poolCapacity}->{oid} } + ], + return_type => 1, nothing_quit => 1 + ); + + my $results = {}; + foreach my $oid (keys %{$snmp_result}) { + next if ($oid !~ /^$mapping->{poolName}->{oid}\.(.*)$/); + + my $instance = $1; + my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance); + + $results->{$instance} = { + pool_name => $result->{poolName}, + pool_capacity => $result->{poolCapacity}, + }; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach my $port (sort keys %$results) { + $self->{output}->output_add(long_msg => + join('', map("[$_: " . $results->{$port}->{$_} . ']', @labels)) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List pool' + ); + $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 => [ @labels ]); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(snmp => $options{snmp}); + foreach (sort keys %$results) { + $self->{output}->add_disco_entry( + %{$results->{$_}} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List pools. + +=over 8 + +=back + +=cut diff --git a/src/storage/stormagic/svsan/snmp/mode/mirrors.pm b/src/storage/stormagic/svsan/snmp/mode/mirrors.pm new file mode 100644 index 0000000000..91b9acf9da --- /dev/null +++ b/src/storage/stormagic/svsan/snmp/mode/mirrors.pm @@ -0,0 +1,503 @@ +# +# Copyright 2026 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::stormagic::svsan::snmp::mode::mirrors; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; + +sub custom_mirror_status_output { + my ($self, %options) = @_; + + return sprintf( + "status: %s - online: %s", + $self->{result_values}->{mirror_state}, + $self->{result_values}->{mirror_online} + ); +} + +sub custom_cache_status_output { + my ($self, %options) = @_; + + return sprintf( + "cache status: %s", + $self->{result_values}->{cache_state} + ); +} + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => + $self->{result_values}->{size}); + 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( + "size: %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 mirror_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking mirror '%s'", + $options{instance_value}->{display} + ); +} + +sub prefix_mirror_output { + my ($self, %options) = @_; + + return sprintf( + "mirror '%s' ", + $options{instance_value}->{display} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'mirror', + type => COUNTER_TYPE_MULTIPLE, + cb_prefix_output => 'prefix_mirror_output', + cb_long_output => 'mirror_long_output', + indent_long_output => ' ', + message_multiple => 'All mirrors are ok', + group => + [ + { name => 'status', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { -10 => 1 } }, + { name => 'mbc', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { -10 => 1 } }, + { name => 'cache', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{status} = [ + { + label => 'mirror-status', + type => COUNTER_KIND_TEXT, + unknown_default => '%{mirror_state} =~ /unknown/i', + warning_default => '%{mirror_state} =~ /degraded|missing|migrated/i', + critical_default => '%{mirror_state} =~ /failed|offline/i || %{mirror_online} =~ /no/i', + set => + { + key_values => [ + { name => 'mirror_state' }, + { name => 'mirror_online' }, + ], + closure_custom_output => $self->can('custom_mirror_status_output'), + closure_custom_perfdata => sub {return 0;}, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'mirror-size', display_ok => 1, nlabel => 'mirror.size.bytes', set => { + key_values => + [ { name => 'size' } ], + output_template => 'size: %s %s', + output_change_bytes => 1, + perfdatas => + [ + { + template => '%d', + min => 0, + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + { label => 'resync-progress', display_ok => 1, nlabel => 'mirror.resync.progress.percentage', set => { + key_values => + [ + { name => 'resync_progress' } + ], + output_template => 'resync progress: %s %%', + perfdatas => [ + { + template => '%d', + unit => '%', + min => 0, + max => 100, + label_extra_instance => 1 + } + ] + } + } + ]; + + $self->{maps_counters}->{mbc} = [ + { label => 'mbc-usage', nlabel => 'mirror.mbc.usage.bytes', set => { + key_values => + [ + { name => 'used' }, + { name => 'free' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'size' }, + { name => 'display' } + ], + closure_custom_output => + $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'size', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + { label => 'mbc-usage-free', display_ok => 0, nlabel => 'mirror.mbc.free.bytes', set => { + key_values => + [ + { name => 'free' }, + { name => 'used' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'size' }, + { name => 'display' } + ], + closure_custom_output => + $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'size', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + { label => 'mbc-usage-prct', display_ok => 0, nlabel => 'mirror.mbc.usage.percentage', set => { + key_values => + [ + { name => 'prct_used' }, + { name => 'used' }, + { name => 'free' }, + { name => 'prct_free' }, + { name => 'size' }, + { name => 'display' } + ], + closure_custom_output => + $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%.2f', + min => 0, + max => 100, + unit => '%', + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + } + ]; + + $self->{maps_counters}->{cache} = [ + { + label => 'cache-status', + type => COUNTER_KIND_TEXT, + unknown_default => '%{cache_state} =~ /unknown/i', + critical_default => '%{cache_state} =~ /failed/i', + set => + { + key_values => [ + { name => 'cache_state' } + ], + closure_custom_output => $self->can('custom_cache_status_output'), + closure_custom_perfdata => sub {return 0;}, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'cache-size', nlabel => 'mirror.cache.size.bytes', set => { + key_values => + [ { name => 'size' } ], + output_template => 'cache size: %s %s', + output_change_bytes => 1, + perfdatas => + [ + { + template => '%d', + min => 0, + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + ]; +} + +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 => { + 'include-name:s' => { name => 'include_name' }, + 'exclude-name:s' => { name => 'exclude_name' } + }); + + return $self; +} + +my $map_mirror_state = { + 0 => 'online', 1 => 'offline', 2 => 'degraded', 3 => 'migrated', 4 => 'failed', 5 => 'missing', 6 => 'unknown' +}; + +my $map_mirror_cache_state = { + 0 => 'unknown', 1 => 'disabled', 2 => 'online', 3 => 'flushing', 4 => 'failed' +}; + +my $map_online = { + 0 => 'no', 1 => 'yes' +}; + +my $mapping = { + mirrorName => { oid => '.1.3.6.1.4.1.38003.1.4.1.2' },# mirrorName, + mirrorNSH => { oid => '.1.3.6.1.4.1.38003.1.4.1.9' },# mirrorNSH, + mirrorSize => { oid => '.1.3.6.1.4.1.38003.1.4.1.5' },# mirrorSize, + mirrorState => { oid => '.1.3.6.1.4.1.38003.1.4.1.6', map => $map_mirror_state },# mirrorState, + mirrorOnline => { oid => '.1.3.6.1.4.1.38003.1.4.1.7', map => $map_online },# mirrorOnline + mirrorResyncProg => { oid => '.1.3.6.1.4.1.38003.1.4.1.8' },# mirrorResyncProg, + mirrorCachePresent => { oid => '.1.3.6.1.4.1.38003.1.4.1.10' },# mirrorCachePresent + mirrorCacheState => { oid => '.1.3.6.1.4.1.38003.1.4.1.11', map => $map_mirror_cache_state },# mirrorCacheState + mirrorCacheSize => { oid => '.1.3.6.1.4.1.38003.1.4.1.12' },# mirrorCacheSize + mirrorMBCPresent => { oid => '.1.3.6.1.4.1.38003.1.4.1.15' },# mirrorMBCPresent + mirrorMBCLoaded => { oid => '.1.3.6.1.4.1.38003.1.4.1.16' },# mirrorMBCLoaded + mirrorMBCSize => { oid => '.1.3.6.1.4.1.38003.1.4.1.17' },# mirrorMBCSize + mirrorMBCFree => { oid => '.1.3.6.1.4.1.38003.1.4.1.18' }# mirrorMBCFree +}; + +my $oid_mirrorEntry = '.1.3.6.1.4.1.38003.1.4.1'; + +sub manage_selection { + my ($self, %options) = @_; + + my $snmp_result = $options{snmp}->get_table(oid => $oid_mirrorEntry, nothing_quit => 1); + + foreach my $oid (sort keys %$snmp_result) { + next if ($oid !~ /^$mapping->{mirrorName}->{oid}\.(.*)$/); + + my $instance = $1; + my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance); + + next if is_excluded( + $result->{mirrorName}, + $self->{option_results}->{include_name}, + $self->{option_results}->{exclude_name} + ); + + $self->{mirror}->{$instance} = { + display => $result->{mirrorName} + }; + + $self->{mirror}->{$instance}->{status} = { + mirror_state => $result->{mirrorState}, + mirror_online => $result->{mirrorOnline}, + size => $result->{mirrorSize} * 1024 * 1024, + resync_progress => $result->{mirrorResyncProg} + }; + + if ($result->{mirrorMBCPresent} == 1) { + my $size = defined($result->{mirrorMBCSize}) ? $result->{mirrorMBCSize} * 1024 * 1024 : undef; + my $free = defined($result->{mirrorMBCFree}) ? $result->{mirrorMBCFree} * 1024 * 1024 : undef; + my $used = defined($size) && defined($free) ? $size - $free : undef; + + $self->{mirror}->{$instance}->{mbc} = { + display => $result->{mirrorName}, + free => $free, + size => $size, + used => $used, + prct_used => defined($size) && $size > 0 ? $used * 100 / $size : undef, + prct_free => defined($size) && $size > 0 ? $free * 100 / $size : undef + }; + } else { + $self->{output}->output_add(long_msg => + sprintf("mirror '%s' has no memory cache (MBC)", $result->{mirrorName})); + } + + if ($result->{mirrorCachePresent} == 1) { + $self->{mirror}->{$instance}->{cache} = { + size => $result->{mirrorCacheSize}, + cache_state => $result->{mirrorCacheState}, + }; + } else { + $self->{output}->output_add(long_msg => sprintf("mirror '%s' has no cache", $result->{mirrorName})); + } + } + + if (scalar(keys %{$self->{mirror}}) <= 0) { + $self->{output}->option_exit(short_msg => "No mirror matching with filter found."); + } +} + +1; + +__END__ + +=head1 MODE + +Check mirrors. + +=over 8 + +=item B<--include-name> + +Filter mirrors by name (can be a regexp). + +=item B<--exclude-name> + +Exclude mirrors by name (can be a regexp). + +=item B<--unknown-mirror-status> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: C<%{mirror_state}>, C<%{mirror_online}>, C<%{display}> + +=item B<--warning-mirror-status> + +Define the conditions to match for the status to be WARNING (default: ''). +You can use the following variables: C<%{mirror_state}>, C<%{mirror_online}>, C<%{display}> + +=item B<--critical-mirror-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{mirror_state} !~ /normal/i || %{mirror_online} =~ /offline/i'). +You can use the following variables: C<%{mirror_state}>, C<%{mirror_online}>, C<%{display}> + +=item B<--unknown-cache-status> + +Define the conditions to match for the status to be UNKNOWN (default: '%{cache_state} =~ /unknown/i'). +You can use the following variables: C<%{cache_state}>, C<%{display}> + +=item B<--warning-cache-status> + +Define the conditions to match for the status to be WARNING (default: ''). +You can use the following variables: C<%{cache_state}>, C<%{display}> + +=item B<--critical-cache-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{cache_state} =~ /failed/i'). +You can use the following variables: C<%{cache_state}>, C<%{display}> + +=item B<--warning-space-usage> + +Warning threshold for space usage (B). + +=item B<--critical-space-usage> + +Critical threshold for space usage (B). + +=item B<--warning-space-usage-prct> + +Warning threshold for space usage (%). + +=item B<--critical-space-usage-prct> + +Critical threshold for space usage (%). + +=item B<--warning-space-usage-free> + +Warning threshold for free space (B). + +=item B<--critical-space-usage-free> + +Critical threshold for free space (B). + +=item B<--warning-mirror-size> + +Warning threshold for mirror size (B). + +=item B<--critical-mirror-size> + +Critical threshold for mirror size (B). + +=item B<--warning-cache-size> + +Warning threshold for cache size (B). + +=item B<--critical-cache-size> + +Critical threshold for cache size (B). + +=item B<--warning-mbc-usage> + +Warning threshold for memory cache usage (B). + +=item B<--critical-mbc-usage> + +Critical threshold for memory cache usage (B). + +=item B<--warning-mbc-usage-free> + +Warning threshold for free memory cache (B). + +=item B<--critical-mbc-usage-free> + +Critical threshold for free memory cache (B). + +=item B<--warning-mbc-usage-prct> + +Warning threshold for memory cache usage (%). + +=item B<--critical-mbc-usage-prct> + +Critical threshold for memory cache usage (%). + +=item B<--warning-resync-progress> + +Warning threshold for Resynchronization progress (%). + +=item B<--critical-resync-progress> + +Critical threshold for Resynchronization progress (%). + +=back + +=cut + diff --git a/src/storage/stormagic/svsan/snmp/mode/pools.pm b/src/storage/stormagic/svsan/snmp/mode/pools.pm new file mode 100644 index 0000000000..b04de81de4 --- /dev/null +++ b/src/storage/stormagic/svsan/snmp/mode/pools.pm @@ -0,0 +1,482 @@ +# +# Copyright 2026 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::stormagic::svsan::snmp::mode::pools; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; + +sub custom_pool_status_output { + my ($self, %options) = @_; + + return sprintf( + "status: %s", + $self->{result_values}->{pool_state} + ); +} + +sub custom_cache_status_output { + my ($self, %options) = @_; + + return sprintf( + "cache status: %s", + $self->{result_values}->{cache_state} + ); +} + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => + $self->{result_values}->{capacity}); + 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( + "capacity: %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 custom_cache_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => + $self->{result_values}->{size}); + 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( + "size: %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 pool_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking pool '%s'", + $options{instance_value}->{display} + ); +} + +sub prefix_pool_output { + my ($self, %options) = @_; + + return sprintf( + "pool '%s' ", + $options{instance_value}->{display} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'pool', + type => COUNTER_TYPE_MULTIPLE, + cb_prefix_output => 'prefix_pool_output', + cb_long_output => 'pool_long_output', + indent_long_output => ' ', + message_multiple => 'All pools are ok', + group => + [ + { name => 'status', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { -10 => 1 } }, + { name => 'space', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { -10 => 1 } }, + { name => 'cache', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{status} = [ + { + label => 'pool-status', + type => COUNTER_KIND_TEXT, + critical_default => '%{pool_state} !~ /normal/i || %{pool_online} =~ /offline/i', + set => + { + key_values => [ + { name => 'pool_state' }, + { name => 'pool_online' }, + { name => 'display' } + ], + closure_custom_output => $self->can('custom_pool_status_output'), + closure_custom_perfdata => sub {return 0;}, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + $self->{maps_counters}->{space} = [ + { label => 'space-usage', nlabel => 'pool.space.usage.bytes', set => { + key_values => + [ { name => 'used' }, + { name => 'free' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'capacity' }, + { name => 'display' } ], + closure_custom_output => + $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'capacity', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + { label => 'space-usage-free', display_ok => 0, nlabel => 'pool.space.free.bytes', set => { + key_values => + [ { name => 'free' }, + { name => 'used' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'capacity' }, + { name => 'display' } ], + closure_custom_output => + $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'capacity', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + { label => 'space-usage-prct', display_ok => 0, nlabel => 'pool.space.usage.percentage', set => { + key_values => + [ { name => 'prct_used' }, + { name => 'used' }, + { name => 'free' }, + { name => 'prct_free' }, + { name => 'capacity' }, + { name => 'display' } ], + closure_custom_output => + $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%.2f', + min => 0, + max => 100, + unit => '%', + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + } + ]; + + $self->{maps_counters}->{cache} = [ + { + label => 'cache-status', + type => COUNTER_KIND_TEXT, + unknown_default => '%{cache_state} =~ /unknown/i', + warning_default => '%{cache_state} =~ /recovering/i', + critical_default => '%{cache_state} =~ /failed/i', + set => + { + key_values => [ + { name => 'cache_state' }, + { name => 'display' } + ], + closure_custom_output => $self->can('custom_cache_status_output'), + closure_custom_perfdata => sub {return 0;}, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'cache-usage', nlabel => 'pool.cache.usage.bytes', set => { + key_values => + [ { name => 'used' }, + { name => 'free' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'size' }, + { name => 'display' } ], + closure_custom_output => + $self->can('custom_cache_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'size', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + { label => 'cache-usage-free', display_ok => 0, nlabel => 'pool.cache.free.bytes', set => { + key_values => + [ { name => 'free' }, + { name => 'used' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'size' }, + { name => 'display' } ], + closure_custom_output => + $self->can('custom_cache_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'size', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + }, + { label => 'cache-usage-prct', display_ok => 0, nlabel => 'pool.cache.usage.percentage', set => { + key_values => + [ { name => 'prct_used' }, + { name => 'used' }, + { name => 'free' }, + { name => 'prct_free' }, + { name => 'size' }, + { name => 'display' } ], + closure_custom_output => + $self->can('custom_cache_usage_output'), + perfdatas => + [ + { + template => '%.2f', + min => 0, + max => 100, + unit => '%', + label_extra_instance => 1, + instance_use => 'display' + } + ] + } + } + ]; +} + +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 => { + 'include-name:s' => { name => 'include_name' }, + 'exclude-name:s' => { name => 'exclude_name' }, + 'skip-zero-cache' => { name => 'skip_zero_cache' }, + }); + + return $self; +} + +my $map_cache_state = { + 0 => 'unknown', 1 => 'none', 2 => 'failed', 3 => 'online', 4 => 'flushing', 5 => 'recovering' +}; + +my $map_online = { + 0 => 'offline', 1 => 'online' +}; + +my $mapping = { + poolName => { oid => '.1.3.6.1.4.1.38003.1.2.1.2' },# poolName, + poolCapacity => { oid => '.1.3.6.1.4.1.38003.1.2.1.3' },# poolCapacity, + poolFreeSpace => { oid => '.1.3.6.1.4.1.38003.1.2.1.4' },# poolFreeSpace, + poolState => { oid => '.1.3.6.1.4.1.38003.1.2.1.5' },# poolState, + poolOnline => { oid => '.1.3.6.1.4.1.38003.1.2.1.6', map => $map_online },# poolOnline + poolCacheState => { oid => '.1.3.6.1.4.1.38003.1.2.1.7', map => $map_cache_state },# poolCacheState + poolCacheSize => { oid => '.1.3.6.1.4.1.38003.1.2.1.8' },# poolCacheSize, + poolCacheFree => { oid => '.1.3.6.1.4.1.38003.1.2.1.9' }# poolCacheFree +}; + +my $oid_poolEntry = '.1.3.6.1.4.1.38003.1.2.1'; + +sub manage_selection { + my ($self, %options) = @_; + + my $snmp_result = $options{snmp}->get_table(oid => $oid_poolEntry, nothing_quit => 1); + + foreach my $oid (sort keys %$snmp_result) { + next if ($oid !~ /^$mapping->{poolName}->{oid}\.(.*)$/); + + my $instance = $1; + my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance); + + next if is_excluded( + $result->{poolName}, + $self->{option_results}->{include_name}, + $self->{option_results}->{exclude_name} + ); + + $self->{pool}->{$instance} = { + display => $result->{poolName} + }; + + $self->{pool}->{$instance}->{status} = { + display => $result->{poolName}, + pool_state => $result->{poolState}, + pool_online => $result->{poolOnline}, + }; + + my $free = defined($result->{poolFreeSpace}) ? $result->{poolFreeSpace} * 1024 * 1024 : undef; + my $capacity = defined($result->{poolCapacity}) ? $result->{poolCapacity} * 1024 * 1024 : undef; + my $used = defined($capacity) && defined($free) ? $capacity - $free : 0; + + $self->{pool}->{$instance}->{space} = { + display => $result->{poolName}, + free => $free, + capacity => $capacity, + used => $used, + prct_used => defined($capacity) && $capacity > 0 ? $free * 100 / $capacity : undef, + prct_free => defined($capacity) && $capacity > 0 ? $used * 100 / $capacity : undef, + }; + + if (defined($self->{option_results}->{skip_zero_cache}) + && (!defined($result->{poolCacheSize}) || $result->{poolCacheSize} == 0)) { + $self->{output}->output_add(long_msg => sprintf("pool '%s' has no cache", $result->{poolName})); + } else { + $free = defined($result->{poolCacheFree}) ? $result->{poolCacheFree} * 1024 * 1024 : undef; + my $size = $result->{poolCacheSize} * 1024 * 1024; + $used = defined($size) && defined($free) ? $size - $free : 0; + + $self->{pool}->{$instance}->{cache} = { + display => $result->{poolName}, + cache_state => $result->{poolCacheState}, + free => $free, + size => $size, + used => $used, + prct_used => defined($size) && $size > 0 ? $free * 100 / $size : undef, + prct_free => defined($size) && $size > 0 ? $used * 100 / $size : undef, + } + } + } + + if (scalar(keys %{$self->{pool}}) <= 0) { + $self->{output}->option_exit(short_msg => "No pool matching with filter found."); + } +} + +1; + +__END__ + +=head1 MODE + +Check pools. + +=over 8 + +=item B<--include-name> + +Filter pools by name (can be a regexp). + +=item B<--exclude-name> + +Exclude pools by name (can be a regexp). + +=item B<--skip-zero-cache> + +Skip cache that have zero size. + +=item B<--unknown-pool-status> + +Define the conditions to match for the status to be UNKNOWN (default: ''). +You can use the following variables: %{status}, %{display} + +=item B<--warning-pool-status> + +Define the conditions to match for the status to be WARNING (default: ''). +You can use the following variables: %{status}, %{display} + +=item B<--critical-pool-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{pool_state} !~ /normal/i || %{pool_online} =~ /offline/i'). +You can use the following variables: %{status}, %{display} + +=item B<--unknown-cache-status> + +Define the conditions to match for the status to be UNKNOWN (default: '%{cache_state} =~ /unknown/i'). +You can use the following variables: %{cache_state}, %{display} + +=item B<--warning-cache-status> + +Define the conditions to match for the status to be WARNING (default: '%{cache_state} =~ /recovering/i'). +You can use the following variables: %{cache_state}, %{display} + +=item B<--critical-cache-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{cache_state} =~ /failed/i'). +You can use the following variables: %{cache_state}, %{display} + +=item B<--warning-space-usage> + +Warning threshold for space usage (B). + +=item B<--critical-space-usage> + +Critical threshold for space usage (B). + +=item B<--warning-space-usage-prct> + +Warning threshold for space usage (%). + +=item B<--critical-space-usage-prct> + +Critical threshold for space usage (%). + +=item B<--warning-space-usage-free> + +Warning threshold for free space usage (B). + +=item B<--critical-space-usage-free> + +Critical threshold for free space usage (B). + +=back + +=cut + diff --git a/src/storage/stormagic/svsan/snmp/mode/systemstatus.pm b/src/storage/stormagic/svsan/snmp/mode/systemstatus.pm new file mode 100644 index 0000000000..1908008bfb --- /dev/null +++ b/src/storage/stormagic/svsan/snmp/mode/systemstatus.pm @@ -0,0 +1,160 @@ +# +# Copyright 2026 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::stormagic::svsan::snmp::mode::systemstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; + +sub custom_status_output { + my ($self, %options) = @_; + + return sprintf( + "system '%s' status is '%s'", + $self->{result_values}->{system_name}, + $self->{result_values}->{system_status} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'system', + type => COUNTER_TYPE_INSTANCE, + message_multiple => 'All systems are ok', + skipped_code => { NO_VALUE() => 1 } + } + ]; + + $self->{maps_counters}->{system} = [ + { + label => 'status', + type => COUNTER_KIND_TEXT, + warning_default => '%{system_status} =~ /warning/', + critical_default => '%{system_status} =~ /error/', + set => { + key_values => [ + { name => 'system_status' }, + { name => 'system_name' } + ], + 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 => { + 'include-name:s' => { name => 'include_name' }, + 'exclude-name:s' => { name => 'exclude_name' } + }); + + return $self; +} + +my $map_status = { + 1 => 'warning', 2 => 'error', 3 => 'ok' +}; + +my $mapping = { + systemName => { oid => '.1.3.6.1.4.1.38003.1.1.1.2' },# systemName, + systemStatus => { oid => '.1.3.6.1.4.1.38003.1.1.1.3', map => $map_status },# systemState +}; + +my $oid_systemEntry = '.1.3.6.1.4.1.38003.1.1.1'; + +sub manage_selection { + my ($self, %options) = @_; + + my $snmp_result = $options{snmp}->get_table(oid => $oid_systemEntry, nothing_quit => 1); + + foreach my $oid (sort keys %$snmp_result) { + next if ($oid !~ /^$mapping->{systemName}->{oid}\.(.*)$/); + + my $instance = $1; + my $result = $options{snmp}->map_instance(mapping => $mapping, results => $snmp_result, instance => $instance); + + next if is_excluded( + $result->{systemName}, + $self->{option_results}->{include_name}, + $self->{option_results}->{exclude_name} + ); + + $self->{system}->{$instance} = { + system_name => $result->{systemName}, + system_status => $result->{systemStatus}, + }; + } + + if (scalar(keys %{$self->{system}}) <= 0) { + $self->{output}->option_exit(short_msg => "No system matching with filter found."); + } +} + +1; + +__END__ + +=head1 MODE + +Check the overall status of C. + +=over 8 + +=item B<--include-name> + +Filter systems by name (can be a regexp). + +=item B<--exclude-name> + +Exclude systems by name (can be a regexp). + +=item B<--unknown-status> + +Define the conditions to match for the status to be UNKNOWN (default: ''). +You can use the following variables: %{system_status} + +=item B<--warning-status> + +Define the conditions to match for the status to be WARNING (default: '%{system_status} =~ /warning/'). +You can use the following variables: %{system_status} + +=item B<--critical-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{system_status} =~ /error/'). +You can use the following variables: %{system_status} + +=back + +=cut diff --git a/src/storage/stormagic/svsan/snmp/plugin.pm b/src/storage/stormagic/svsan/snmp/plugin.pm new file mode 100644 index 0000000000..078ee1603e --- /dev/null +++ b/src/storage/stormagic/svsan/snmp/plugin.pm @@ -0,0 +1,57 @@ +# +# Copyright 2026 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::stormagic::svsan::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->{version} = '0.1'; + $self->{modes} = { + 'list-mirrors' => 'storage::stormagic::svsan::snmp::mode::listmirrors', + 'list-pools' => 'storage::stormagic::svsan::snmp::mode::listpools', + 'mirrors' => 'storage::stormagic::svsan::snmp::mode::mirrors', + 'pools' => 'storage::stormagic::svsan::snmp::mode::pools', + 'system-status' => 'storage::stormagic::svsan::snmp::mode::systemstatus', + 'uptime' => 'snmp_standard::mode::uptime', + }; + + $self->{modes_options} = { + 'uptime' => { force_new_perfdata => 1 } + }; + + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check C in SNMP. + +=cut