From c13abfc077bcb789bcc19f6cf8f44711cebacdcd Mon Sep 17 00:00:00 2001 From: qgarnier Date: Tue, 22 Feb 2022 14:14:44 +0100 Subject: [PATCH] (plugin) storage::netapp::ontap::oncommandapi - refactoring for volumes (#3499) --- .../netapp/ontap/oncommandapi/custom/api.pm | 78 +-- .../netapp/ontap/oncommandapi/mode/listsvm.pm | 112 +++++ .../ontap/oncommandapi/mode/listvolumes.pm | 79 +-- .../ontap/oncommandapi/mode/volumeio.pm | 223 --------- .../netapp/ontap/oncommandapi/mode/volumes.pm | 458 +++++++++++++++++ .../ontap/oncommandapi/mode/volumestatus.pm | 142 ------ .../ontap/oncommandapi/mode/volumeusage.pm | 470 ------------------ storage/netapp/ontap/oncommandapi/plugin.pm | 12 +- 8 files changed, 656 insertions(+), 918 deletions(-) create mode 100644 storage/netapp/ontap/oncommandapi/mode/listsvm.pm delete mode 100644 storage/netapp/ontap/oncommandapi/mode/volumeio.pm create mode 100644 storage/netapp/ontap/oncommandapi/mode/volumes.pm delete mode 100644 storage/netapp/ontap/oncommandapi/mode/volumestatus.pm delete mode 100644 storage/netapp/ontap/oncommandapi/mode/volumeusage.pm diff --git a/storage/netapp/ontap/oncommandapi/custom/api.pm b/storage/netapp/ontap/oncommandapi/custom/api.pm index 6a03daac6..c6e9c16b8 100644 --- a/storage/netapp/ontap/oncommandapi/custom/api.pm +++ b/storage/netapp/ontap/oncommandapi/custom/api.pm @@ -24,7 +24,6 @@ use strict; use warnings; use centreon::plugins::http; use JSON::XS; -use URI::Encode; sub new { my ($class, %options) = @_; @@ -42,13 +41,13 @@ sub new { if (!defined($options{noptions})) { $options{options}->add_options(arguments => { - 'hostname:s@' => { name => 'hostname' }, - 'url-path:s@' => { name => 'url_path' }, - 'port:s@' => { name => 'port' }, - 'proto:s@' => { name => 'proto' }, - 'username:s@' => { name => 'username' }, - 'password:s@' => { name => 'password' }, - 'timeout:s@' => { name => 'timeout' } + 'hostname:s' => { name => 'hostname' }, + 'url-path:s' => { name => 'url_path' }, + 'port:s' => { name => 'port' }, + 'proto:s' => { name => 'proto' }, + 'username:s' => { name => 'username' }, + 'password:s' => { name => 'password' }, + 'timeout:s' => { name => 'timeout' } }); } $options{options}->add_help(package => __PACKAGE__, sections => 'OnCommand API OPTIONS', once => 1); @@ -70,25 +69,20 @@ sub set_defaults {} sub check_options { my ($self, %options) = @_; - $self->{hostname} = (defined($self->{option_results}->{hostname})) ? shift(@{$self->{option_results}->{hostname}}) : undef; - $self->{url_path} = (defined($self->{option_results}->{url_path})) ? shift(@{$self->{option_results}->{url_path}}) : '/api/4.0/ontap'; - $self->{port} = (defined($self->{option_results}->{port})) ? shift(@{$self->{option_results}->{port}}) : 8443; - $self->{proto} = (defined($self->{option_results}->{proto})) ? shift(@{$self->{option_results}->{proto}}) : 'https'; - $self->{username} = (defined($self->{option_results}->{username})) ? shift(@{$self->{option_results}->{username}}) : ''; - $self->{password} = (defined($self->{option_results}->{password})) ? shift(@{$self->{option_results}->{password}}) : ''; - $self->{timeout} = (defined($self->{option_results}->{timeout})) ? shift(@{$self->{option_results}->{timeout}}) : 10; + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : ''; + $self->{url_path} = (defined($self->{option_results}->{url_path})) ? $self->{option_results}->{url_path} : '/api/4.0/ontap'; + $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 8443; + $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; + $self->{username} = (defined($self->{option_results}->{username})) ? $self->{option_results}->{username} : ''; + $self->{password} = (defined($self->{option_results}->{password})) ? $self->{option_results}->{password} : ''; + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10; - if (!defined($self->{hostname})) { + if ($self->{hostname} eq '') { $self->{output}->add_option_msg(short_msg => "Need to specify hostname option."); $self->{output}->option_exit(); } - - if (!defined($self->{hostname}) || - scalar(@{$self->{option_results}->{hostname}}) == 0) { - return 0; - } - return 1; + return 0; } sub build_options_for_httplib { @@ -115,7 +109,7 @@ sub settings { sub get_connection_info { my ($self, %options) = @_; - + return $self->{hostname} . ":" . $self->{port}; } @@ -125,7 +119,7 @@ sub get_objects { my %objects; my $objects = $self->get(%options); foreach my $object (@{$objects}) { - $objects{$object->{$options{key}}} = $object->{$options{name}}; + $objects{ $object->{ $options{key} } } = $object->{ $options{name} }; } return \%objects; @@ -134,19 +128,15 @@ sub get_objects { sub get_next { my ($self, %options) = @_; - my $encoded_tag = ''; - if (defined($options{nextTag})) { - my $uri = URI::Encode->new({encode_reserved => 1}); - $encoded_tag = "nextTag=" . $uri->encode($options{nextTag}); - } + my $get_param = []; + $get_param = $options{get_param} if (defined($options{get_param})); + push @$get_param, 'nextTag=' . $options{nextTag} if (defined($options{nextTag})); + + my $response = $self->{http}->request( + url_path => $self->{url_path} . $options{path}, + get_param => $get_param + ); - my $url_path = $self->{url_path} . $options{path}; - $url_path .= '?' . $options{args} if (defined($options{args})); - $url_path .= '?' . $encoded_tag if (defined($options{nextTag}) && !defined($options{args})); - $url_path .= '&' . $encoded_tag if (defined($options{nextTag}) && defined($options{args})); - - my $response = $self->{http}->request(url_path => $url_path); - my $content; eval { $content = JSON::XS->new->utf8->decode($response); @@ -160,7 +150,7 @@ sub get_next { $self->{output}->add_option_msg(short_msg => "Cannot get data: " . $content->{errmsg}); $self->{output}->option_exit(); } - + return $content; } @@ -170,9 +160,9 @@ sub get { $self->settings(); my @result; - while(my $content = $self->get_next(%options)) { + while (my $content = $self->get_next(%options)) { push @result, @{$content->{result}->{records}}; - + last if (!defined($content->{result}->{nextTag})); $options{nextTag} = $content->{result}->{nextTag}; } @@ -180,6 +170,16 @@ sub get { return \@result; } +sub get_record_attr { + my ($self, %options) = @_; + + foreach (@{$options{records}}) { + if ($_->{ $options{key} } eq $options{value}) { + return $_->{ $options{attr} }; + } + } +} + 1; __END__ diff --git a/storage/netapp/ontap/oncommandapi/mode/listsvm.pm b/storage/netapp/ontap/oncommandapi/mode/listsvm.pm new file mode 100644 index 000000000..1daecaade --- /dev/null +++ b/storage/netapp/ontap/oncommandapi/mode/listsvm.pm @@ -0,0 +1,112 @@ +# +# 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::netapp::ontap::oncommandapi::mode::listsvm; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $svms = $options{custom}->get(path => '/storage-vms'); + + my $results = []; + foreach (@$svms) { + push @$results, { + key => $_->{key}, + name => $_->{name}, + state => defined($_->{state}) ? $_->{state} : 'none', + type => $_->{type} + } + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->output_add( + long_msg => sprintf( + "[key: %s] [name: %s] [state: %s] [type: %s]", + $_->{key}, + $_->{name}, + $_->{state}, + $_->{type} + ) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List storage virtual machines:' + ); + $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 => ['key', 'name', 'state', 'type']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->add_disco_entry(%$_); + } +} + +1; + +__END__ + +=head1 MODE + +List storage virtual machines. + +=over 8 + +=back + +=cut diff --git a/storage/netapp/ontap/oncommandapi/mode/listvolumes.pm b/storage/netapp/ontap/oncommandapi/mode/listvolumes.pm index 9b6a37ccd..aef3c4463 100644 --- a/storage/netapp/ontap/oncommandapi/mode/listvolumes.pm +++ b/storage/netapp/ontap/oncommandapi/mode/listvolumes.pm @@ -30,9 +30,7 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options); bless $self, $class; - $options{options}->add_options(arguments => { - 'filter-name:s' => { name => 'filter_name' } - }); + $options{options}->add_options(arguments => {}); return $self; } @@ -45,39 +43,53 @@ sub check_options { sub manage_selection { my ($self, %options) = @_; - my $result = $options{custom}->get(path => '/volumes'); + my $svms = $options{custom}->get(path => '/storage-vms'); + my $volumes = $options{custom}->get(path => '/volumes'); - foreach my $volume (@{$result}) { - if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && - $volume->{name} !~ /$self->{option_results}->{filter_name}/) { - $self->{output}->output_add(long_msg => "skipping '" . $volume->{name} . "': no matching filter name.", debug => 1); - next; - } + my $results = []; + foreach my $volume (@$volumes) { + my $svm_name = $options{custom}->get_record_attr(records => $svms, key => 'key', value => $volume->{storage_vm_key}, attr => 'name'); + $svm_name = 'root' if (!defined($svm_name)); - $self->{volumes}->{$volume->{key}} = { + push @$results, { + key => $volume->{key}, name => $volume->{name}, - state => $volume->{state}, + svm => $svm_name, + state => defined($volume->{state}) ? $volume->{state} : 'none', vol_type => $volume->{vol_type}, style => $volume->{style}, is_replica_volume => $volume->{is_replica_volume}, - size_total => $volume->{size_total}, + size_total => $volume->{size_total} } } + + return $results; } sub run { my ($self, %options) = @_; - $self->manage_selection(%options); - foreach my $volume (sort keys %{$self->{volumes}}) { - $self->{output}->output_add(long_msg => sprintf("[name = %s] [state = %s] [vol_type = %s] [style = %s] [is_replica_volume = %s] [size_total = %s]", - $self->{volumes}->{$volume}->{name}, $self->{volumes}->{$volume}->{state}, - $self->{volumes}->{$volume}->{vol_type}, $self->{volumes}->{$volume}->{style}, - $self->{volumes}->{$volume}->{is_replica_volume}, $self->{volumes}->{$volume}->{size_total})); + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->output_add( + long_msg => sprintf( + "[key: %s] [name: %s] [svm: %s] [state: %s] [vol_type: %s] [style: %s] [is_replica_volume: %s] [size_total: %s]", + $_->{key}, + $_->{name}, + $_->{svm}, + $_->{state}, + $_->{vol_type}, + $_->{style}, + $_->{is_replica_volume}, + $_->{size_total} + ) + ); } - $self->{output}->output_add(severity => 'OK', - short_msg => 'List volumes:'); + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List volumes:' + ); $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); $self->{output}->exit(); } @@ -85,23 +97,20 @@ sub run { sub disco_format { my ($self, %options) = @_; - $self->{output}->add_disco_format(elements => ['name', 'state', 'vol_type', 'style', - 'is_replica_volume', 'size_total']); + $self->{output}->add_disco_format( + elements => [ + 'key', 'name', 'svm', 'state', 'vol_type', 'style', + 'is_replica_volume', 'size_total' + ] + ); } sub disco_show { my ($self, %options) = @_; - $self->manage_selection(%options); - foreach my $volume (sort keys %{$self->{volumes}}) { - $self->{output}->add_disco_entry( - name => $self->{volumes}->{$volume}->{name}, - state => $self->{volumes}->{$volume}->{state}, - vol_type => $self->{volumes}->{$volume}->{vol_type}, - style => $self->{volumes}->{$volume}->{style}, - is_replica_volume => $self->{volumes}->{$volume}->{is_replica_volume}, - size_total => $self->{volumes}->{$volume}->{size_total}, - ); + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->add_disco_entry(%$_); } } @@ -115,10 +124,6 @@ List volumes. =over 8 -=item B<--filter-name> - -Filter volume name (can be a regexp). - =back =cut diff --git a/storage/netapp/ontap/oncommandapi/mode/volumeio.pm b/storage/netapp/ontap/oncommandapi/mode/volumeio.pm deleted file mode 100644 index 5d0214c25..000000000 --- a/storage/netapp/ontap/oncommandapi/mode/volumeio.pm +++ /dev/null @@ -1,223 +0,0 @@ -# -# 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::netapp::ontap::oncommandapi::mode::volumeio; - -use base qw(centreon::plugins::templates::counter); - -use strict; -use warnings; - -sub prefix_output { - my ($self, %options) = @_; - - return "Volume '" . $options{instance_value}->{name} . "' "; -} - -sub set_counters { - my ($self, %options) = @_; - - $self->{maps_counters_type} = [ - { name => 'volumes', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All volumes IOs are ok' }, - ]; - - $self->{maps_counters}->{volumes} = [ - { label => 'read-iops', set => { - key_values => [ { name => 'read_ops' }, { name => 'name' } ], - output_template => 'Read IOPS: %.2f ops/s', - perfdatas => [ - { label => 'read_iops', value => 'read_ops', template => '%.2f', - min => 0, unit => 'ops/s', label_extra_instance => 1, instance_use => 'name' }, - ], - } - }, - { label => 'write-iops', set => { - key_values => [ { name => 'write_ops' }, { name => 'name' } ], - output_template => 'Write IOPS: %.2f ops/s', - perfdatas => [ - { label => 'write_iops', value => 'write_ops', template => '%.2f', - min => 0, unit => 'ops/s', label_extra_instance => 1, instance_use => 'name' }, - ], - } - }, - { label => 'other-iops', set => { - key_values => [ { name => 'other_ops' }, { name => 'name' } ], - output_template => 'Other IOPS: %.2f ops/s', - perfdatas => [ - { label => 'other_iops', value => 'other_ops', template => '%.2f', - min => 0, unit => 'ops/s', label_extra_instance => 1, instance_use => 'name' }, - ], - } - }, - { label => 'avg-latency', set => { - key_values => [ { name => 'avg_latency' }, { name => 'name' } ], - output_template => 'Average latency: %.2f ms', - perfdatas => [ - { label => 'avg_latency', value => 'avg_latency', template => '%.2f', - min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name' }, - ], - } - }, - { label => 'read-latency', set => { - key_values => [ { name => 'read_latency' }, { name => 'name' } ], - output_template => 'Read latency: %.2f ms', - perfdatas => [ - { label => 'read_latency', value => 'read_latency', template => '%.2f', - min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name' }, - ], - } - }, - { label => 'write-latency', set => { - key_values => [ { name => 'write_latency' }, { name => 'name' } ], - output_template => 'Write latency: %.2f ms', - perfdatas => [ - { label => 'write_latency', value => 'write_latency', template => '%.2f', - min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name' }, - ], - } - }, - { label => 'other-latency', set => { - key_values => [ { name => 'other_latency' }, { name => 'name' } ], - output_template => 'Other latency: %.2f ms', - perfdatas => [ - { label => 'other_latency', value => 'other_latency', template => '%.2f', - min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name' }, - ], - } - }, - ]; -} - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options); - bless $self, $class; - - $options{options}->add_options(arguments => { - 'filter-name:s' => { name => 'filter_name' }, - 'filter-state:s' => { name => 'filter_state' }, - 'filter-style:s' => { name => 'filter_style' }, - 'filter-type:s' => { name => 'filter_type' } - }); - - return $self; -} - -sub check_options { - my ($self, %options) = @_; - $self->SUPER::check_options(%options); -} - -sub manage_selection { - my ($self, %options) = @_; - - my %names_hash; - my $names = $options{custom}->get(path => '/volumes'); - foreach my $volume (@{$names}) { - $names_hash{$volume->{key}} = { - name => $volume->{name}, - state => $volume->{state}, - style => $volume->{style}, - vol_type => $volume->{vol_type}, - }; - } - - my $args = ''; - my $append = ''; - foreach my $metric ('read_ops', 'write_ops', 'other_ops', 'avg_latency', 'read_latency', 'write_latency', 'other_latency') { - $args .= $append . 'name=' . $metric; - $append = '&'; - } - - my $result = $options{custom}->get(path => '/volumes/metrics', args => $args); - - foreach my $volume (@{$result}) { - if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && - defined($names_hash{$volume->{resource_key}}) && $names_hash{$volume->{resource_key}}->{name} !~ /$self->{option_results}->{filter_name}/) { - $self->{output}->output_add(long_msg => "skipping '" . $names_hash{$volume->{resource_key}}->{name} . "': no matching filter name.", debug => 1); - next; - } - - if (defined($self->{option_results}->{filter_state}) && $self->{option_results}->{filter_state} ne '' && - defined($names_hash{$volume->{resource_key}}) && $names_hash{$volume->{resource_key}}->{state} !~ /$self->{option_results}->{filter_state}/) { - $self->{output}->output_add(long_msg => "skipping '" . $names_hash{$volume->{resource_key}}->{name} . "': no matching filter state : '" . $names_hash{$volume->{resource_key}}->{state} . "'", debug => 1); - next; - } - - if (defined($self->{option_results}->{filter_style}) && $self->{option_results}->{filter_style} ne '' && - defined($names_hash{$volume->{resource_key}}) && $names_hash{$volume->{resource_key}}->{style} !~ /$self->{option_results}->{filter_style}/) { - $self->{output}->output_add(long_msg => "skipping '" . $names_hash{$volume->{resource_key}}->{name} . "': no matching filter style : '" . $names_hash{$volume->{resource_key}}->{style} . "'", debug => 1); - next; - } - - if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' && - defined($names_hash{$volume->{resource_key}}) && $names_hash{$volume->{resource_key}}->{vol_type} !~ /$self->{option_results}->{filter_type}/) { - $self->{output}->output_add(long_msg => "skipping '" . $names_hash{$volume->{resource_key}}->{name} . "': no matching filter type : '" . $names_hash{$volume->{resource_key}}->{vol_type} . "'", debug => 1); - next; - } - - foreach my $metric (@{$volume->{metrics}}) { - $self->{volumes}->{$volume->{resource_key}}->{name} = $names_hash{$volume->{resource_key}}->{name}; - $self->{volumes}->{$volume->{resource_key}}->{read_ops} = ${$metric->{samples}}[0]->{value} if ($metric->{name} eq 'read_ops'); - $self->{volumes}->{$volume->{resource_key}}->{write_ops} = ${$metric->{samples}}[0]->{value} if ($metric->{name} eq 'write_ops'); - $self->{volumes}->{$volume->{resource_key}}->{other_ops} = ${$metric->{samples}}[0]->{value} if ($metric->{name} eq 'other_ops'); - $self->{volumes}->{$volume->{resource_key}}->{avg_latency} = ${$metric->{samples}}[0]->{value} / 1000 if ($metric->{name} eq 'avg_latency'); - $self->{volumes}->{$volume->{resource_key}}->{read_latency} = ${$metric->{samples}}[0]->{value} / 1000 if ($metric->{name} eq 'read_latency'); - $self->{volumes}->{$volume->{resource_key}}->{write_latency} = ${$metric->{samples}}[0]->{value} / 1000 if ($metric->{name} eq 'write_latency'); - $self->{volumes}->{$volume->{resource_key}}->{other_latency} = ${$metric->{samples}}[0]->{value} / 1000 if ($metric->{name} eq 'other_latency'); - } - } - - if (scalar(keys %{$self->{volumes}}) <= 0) { - $self->{output}->add_option_msg(short_msg => "No entry found."); - $self->{output}->option_exit(); - } -} - -1; - -__END__ - -=head1 MODE - -Check NetApp volumes IOs. - -=over 8 - -=item B<--filter-*> - -Filter volume. -Can be: 'name', 'state', 'style', 'type' (can be a regexp). - -=item B<--warning-*> - -Threshold warning. -Can be: 'read-iops', 'write-iops', 'other-iops', -'avg-latency', 'read-latency', 'write-latency', 'other-latency'. - -=item B<--critical-*> - -Threshold critical. -Can be: 'read-iops', 'write-iops', 'other-iops', -'avg-latency', 'read-latency', 'write-latency', 'other-latency'. - -=back - -=cut diff --git a/storage/netapp/ontap/oncommandapi/mode/volumes.pm b/storage/netapp/ontap/oncommandapi/mode/volumes.pm new file mode 100644 index 000000000..69b2f9a48 --- /dev/null +++ b/storage/netapp/ontap/oncommandapi/mode/volumes.pm @@ -0,0 +1,458 @@ +# +# 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::netapp::ontap::oncommandapi::mode::volumes; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub custom_iops_perfdata { + my ($self) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => [ + $self->{result_values}->{svmName}, + $self->{result_values}->{volumeName} + ], + value => $self->{result_values}->{ $self->{key_values}->[0]->{name} }, + 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_latency_perfdata { + my ($self) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => [ + $self->{result_values}->{svmName}, + $self->{result_values}->{volumeName} + ], + unit => 'ms', + value => $self->{result_values}->{ $self->{key_values}->[0]->{name} }, + 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_space_perfdata { + my ($self) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => [ + $self->{result_values}->{svmName}, + $self->{result_values}->{volumeName} + ], + unit => 'B', + value => $self->{result_values}->{ $self->{key_values}->[0]->{name} }, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => $self->{result_values}->{total_space} + ); +} + +sub custom_prct_perfdata { + my ($self) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => [ + $self->{result_values}->{svmName}, + $self->{result_values}->{volumeName} + ], + unit => '%', + value => sprintf('%.2f', $self->{result_values}->{ $self->{key_values}->[0]->{name} }), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => 100 + ); +} + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total_space}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used_space}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free_space}); + return sprintf( + 'space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)', + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used_space}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free_space} + ); +} + +sub volume_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking '%s' [svm: %s]", + $options{instance_value}->{volumeName}, + $options{instance_value}->{svmName} + ); +} + +sub prefix_volume_output { + my ($self, %options) = @_; + + return sprintf( + "volume '%s' [svm: %s] ", + $options{instance_value}->{volumeName}, + $options{instance_value}->{svmName} + ); +} + +sub prefix_iops_output { + my ($self, %options) = @_; + + return 'iops '; +} + +sub prefix_latency_output { + my ($self, %options) = @_; + + return 'latency '; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'volumes', type => 3, cb_prefix_output => 'prefix_volume_output', cb_long_output => 'volume_long_output', + indent_long_output => ' ', message_multiple => 'All volumes are ok', + group => [ + { name => 'status', type => 0, skipped_code => { -10 => 1 } }, + { name => 'space', type => 0, skipped_code => { -10 => 1 } }, + { name => 'inodes', type => 0, skipped_code => { -10 => 1 } }, + { name => 'other', type => 0, skipped_code => { -10 => 1 } }, + { name => 'iops', type => 0, cb_prefix_output => 'prefix_iops_output', skipped_code => { -10 => 1 } }, + { name => 'latency', type => 0, cb_prefix_output => 'prefix_latency_output', skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{status} = [ + { + label => 'status', + type => 2, + critical_default => '%{state} !~ /online/i', + set => { + key_values => [ { name => 'state' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => "state: %s", + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; + + $self->{maps_counters}->{space} = [ + { label => 'space-usage', nlabel => 'volume.space.usage.bytes', set => { + key_values => [ + { name => 'used_space' }, { name => 'free_space' }, { name => 'prct_used_space' }, { name => 'prct_free_space' }, { name => 'total_space' }, + { name => 'volumeName' }, { name => 'svmName' } + ], + closure_custom_output => $self->can('custom_space_usage_output'), + closure_custom_perfdata => $self->can('custom_space_perfdata') + } + }, + { label => 'space-usage-free', nlabel => 'volume.space.free.bytes', display_ok => 0, set => { + key_values => [ + { name => 'free_space' }, { name => 'used_space' }, { name => 'prct_used_space' }, { name => 'prct_free_space' }, { name => 'total_space' }, + { name => 'volumeName' }, { name => 'svmName' } + ], + closure_custom_output => $self->can('custom_space_usage_output'), + closure_custom_perfdata => $self->can('custom_space_perfdata') + } + }, + { label => 'space-usage-prct', nlabel => 'volume.space.usage.percentage', display_ok => 0, set => { + key_values => [ + { name => 'prct_used_space' }, { name => 'used_space' }, { name => 'free_space' }, { name => 'prct_free_space' }, { name => 'total_space' }, + { name => 'volumeName' }, { name => 'svmName' } + ], + closure_custom_output => $self->can('custom_space_usage_output'), + closure_custom_perfdata => $self->can('custom_prct_perfdata') + } + } + ]; + + $self->{maps_counters}->{inodes} = [ + { label => 'inodes-usage-prct', nlabel => 'volume.inodes.usage.percentage', set => { + key_values => [ { name => 'prct_used' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'inodes used %.2f %%', + closure_custom_perfdata => $self->can('custom_prct_perfdata') + } + } + ]; + + $self->{maps_counters}->{other} = [ + { label => 'compression-saved-prct', nlabel => 'volume.space.compression.saved.percentage', set => { + key_values => [ { name => 'compression' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'compression space saved %.2f %%', + closure_custom_perfdata => $self->can('custom_prct_perfdata') + } + }, + { label => 'deduplication-saved-prct', nlabel => 'volume.space.deduplication.saved.percentage', set => { + key_values => [ { name => 'deduplication' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'deduplication space saved %.2f %%', + closure_custom_perfdata => $self->can('custom_prct_perfdata') + } + }, + { label => 'snapshots-reserve-usage-prct', nlabel => 'volume.snapshots.reserve.usage.percentage', set => { + key_values => [ { name => 'snapshot_reserve_used' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'snapshots reserve used %.2f %%', + closure_custom_perfdata => $self->can('custom_prct_perfdata') + } + }, + ]; + + $self->{maps_counters}->{iops} = [ + { label => 'read-iops', nlabel => 'volume.io.read.usage.iops', set => { + key_values => [ { name => 'read' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'read %s', + closure_custom_perfdata => $self->can('custom_iops_perfdata') + } + }, + { label => 'write-iops', nlabel => 'volume.io.write.usage.iops', set => { + key_values => [ { name => 'write' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'write %s', + closure_custom_perfdata => $self->can('custom_iops_perfdata') + } + }, + { label => 'other-iops', nlabel => 'volume.io.other.usage.iops', set => { + key_values => [ { name => 'other' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'other %s', + closure_custom_perfdata => $self->can('custom_iops_perfdata') + } + } + ]; + + $self->{maps_counters}->{latency} = [ + { label => 'average-latency', nlabel => 'volume.io.average.latency.milliseconds', set => { + key_values => [ { name => 'avg' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'average %s ms', + closure_custom_perfdata => $self->can('custom_latency_perfdata') + } + }, + { label => 'read-latency', nlabel => 'volume.io.read.latency.milliseconds', set => { + key_values => [ { name => 'read' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'read %s ms', + closure_custom_perfdata => $self->can('custom_latency_perfdata') + } + }, + { label => 'write-latency', nlabel => 'volume.io.write.latency.milliseconds', set => { + key_values => [ { name => 'write' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'write %s ms', + closure_custom_perfdata => $self->can('custom_latency_perfdata') + } + }, + { label => 'other-latency', nlabel => 'volume.io.other.latency.milliseconds', set => { + key_values => [ { name => 'other' }, { name => 'volumeName' }, { name => 'svmName' } ], + output_template => 'other %s ms', + closure_custom_perfdata => $self->can('custom_latency_perfdata') + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-volume-key:s' => { name => 'filter_volume_key' }, + 'filter-volume-name:s' => { name => 'filter_volume_name' }, + 'filter-volume-state:s' => { name => 'filter_volume_state' }, + 'filter-volume-style:s' => { name => 'filter_volume_style' }, + 'filter-volume-type:s' => { name => 'filter_volume_type' }, + 'filter-svm-name:s' => { name => 'filter_svm_name' }, + 'add-metrics' => { name => 'add_metrics' }, + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $svms = $options{custom}->get(path => '/storage-vms'); + my $volumes = $options{custom}->get(path => '/volumes'); + + $self->{volumes} = {}; + foreach my $volume (@$volumes) { + next if (defined($self->{option_results}->{filter_volume_key}) && $self->{option_results}->{filter_volume_key} ne '' && + $volume->{key} !~ /$self->{option_results}->{filter_volume_key}/); + next if (defined($self->{option_results}->{filter_volume_name}) && $self->{option_results}->{filter_volume_name} ne '' && + $volume->{name} !~ /$self->{option_results}->{filter_volume_name}/); + next if (defined($self->{option_results}->{filter_volume_state}) && $self->{option_results}->{filter_volume_state} ne '' && + $volume->{state} !~ /$self->{option_results}->{filter_volume_state}/); + next if (defined($self->{option_results}->{filter_volume_style}) && $self->{option_results}->{filter_volume_style} ne '' && + $volume->{style} !~ /$self->{option_results}->{filter_volume_style}/); + next if (defined($self->{option_results}->{filter_volume_type}) && $self->{option_results}->{filter_volume_type} ne '' && + $volume->{vol_type} !~ /$self->{option_results}->{filter_volume_type}/); + + my $svm_name = $options{custom}->get_record_attr(records => $svms, key => 'key', value => $volume->{storage_vm_key}, attr => 'name'); + $svm_name = 'root' if (!defined($svm_name)); + + next if (defined($self->{option_results}->{filter_svm_name}) && $self->{option_results}->{filter_svm_name} ne '' && + $svm_name !~ /$self->{option_results}->{filter_svm_name}/); + + $self->{volumes}->{ $volume->{key} } = { + volumeName => $volume->{name}, + svmName => $svm_name, + status => { + volumeName => $volume->{name}, + svmName => $svm_name, + state => defined($volume->{state}) ? $volume->{state} : 'none' + }, + space => { + volumeName => $volume->{name}, + svmName => $svm_name, + total_space => $volume->{size_total}, + used_space => $volume->{size_used}, + free_space => $volume->{size_total} - $volume->{size_used}, + prct_used_space => $volume->{size_used} * 100 / $volume->{size_total}, + prct_free_space => 100 - ($volume->{size_used} * 100 / $volume->{size_total}) + }, + inodes => { + volumeName => $volume->{name}, + svmName => $svm_name, + prct_used => $volume->{inode_files_used} * 100 / $volume->{inode_files_total} + }, + other => { + volumeName => $volume->{name}, + svmName => $svm_name, + compression => $volume->{percentage_compression_space_saved}, + deduplication => $volume->{percentage_deduplication_space_saved}, + snapshot_reserve_used => $volume->{percentage_snapshot_reserve_used} + }, + iops => { + volumeName => $volume->{name}, + svmName => $svm_name + }, + latency => { + volumeName => $volume->{name}, + svmName => $svm_name + } + }; + + if (defined($self->{option_results}->{add_metrics})) { + my $metrics = $options{custom}->get( + path => '/volumes/metrics', + get_param => [ + 'resource_key=' . $volume->{key}, + 'name=read_ops', + 'name=write_ops', + 'name=other_ops', + 'name=avg_latency', + 'name=read_latency', + 'name=write_latency', + 'name=other_latency' + ] + ); + + foreach my $metric (@{$metrics->[0]->{metrics}}) { + $self->{volumes}->{ $volume->{key} }->{iops}->{read} = sprintf('%.2f', $metric->{samples}->[0]->{value}) if ($metric->{name} eq 'read_ops'); + $self->{volumes}->{ $volume->{key} }->{iops}->{write} = sprintf('%.2f', $metric->{samples}->[0]->{value}) if ($metric->{name} eq 'write_ops'); + $self->{volumes}->{ $volume->{key} }->{iops}->{other} = sprintf('%.2f', $metric->{samples}->[0]->{value}) if ($metric->{name} eq 'other_ops'); + + $self->{volumes}->{ $volume->{key} }->{latency}->{avg} = sprintf('%.2f', $metric->{samples}->[0]->{value} / 1000) if ($metric->{name} eq 'avg_latency'); + $self->{volumes}->{ $volume->{key} }->{latency}->{read} = sprintf('%.2f', $metric->{samples}->[0]->{value} / 1000) if ($metric->{name} eq 'read_latency'); + $self->{volumes}->{ $volume->{key} }->{latency}->{write} = sprintf('%.2f', $metric->{samples}->[0]->{value} / 1000) if ($metric->{name} eq 'write_latency'); + $self->{volumes}->{ $volume->{key} }->{latency}->{other} = sprintf('%.2f', $metric->{samples}->[0]->{value} / 1000) if ($metric->{name} eq 'other_latency'); + } + } + } +} + +1; + +__END__ + +=head1 MODE + +Check volumes. + +=over 8 + +=item B<--add-metrics> + +Add IOPS and latency metrics. + +=item B<--filter-volume-key> + +Filter volumes by volume key. + +=item B<--filter-volume-name> + +Filter volumes by volume name. + +=item B<--filter-volume-state> + +Filter volumes by volume state. + +=item B<--filter-volume-style> + +Filter volumes by volume style. + +=item B<--filter-volume-type> + +Filter volumes by volume type. + +=item B<--filter-svm-name> + +Filter volumes by storage virtual machine name. + +=item B<--unknown-status> + +Set unknown threshold for status. +Can used special variables like: %{state}, %{volumeName}, %{svmName} + +=item B<--warning-status> + +Set warning threshold for status. +Can used special variables like: %{state}, %{volumeName}, %{svmName} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{state} !~ /online/i'). +Can used special variables like: %{state}, %{volumeName}, %{svmName} + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'inodes-usage-prct', 'space-usage', 'space-usage-free', 'space-usage-prct', +'read-iops', 'write-iops', 'other-iops', +'average-latency', 'read-latency' 'write-latency,' 'other-latency', +'compression-saved-prct', 'deduplication-saved-prct', 'snapshots-reserve-usage-prct'. + +=back + +=cut diff --git a/storage/netapp/ontap/oncommandapi/mode/volumestatus.pm b/storage/netapp/ontap/oncommandapi/mode/volumestatus.pm deleted file mode 100644 index 1a85d4668..000000000 --- a/storage/netapp/ontap/oncommandapi/mode/volumestatus.pm +++ /dev/null @@ -1,142 +0,0 @@ -# -# 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::netapp::ontap::oncommandapi::mode::volumestatus; - -use base qw(centreon::plugins::templates::counter); - -use strict; -use warnings; -use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold); - -sub custom_status_output { - my ($self, %options) = @_; - - my $msg = sprintf("State is '%s'", - $self->{result_values}->{state}, $self->{result_values}->{mirror_status}); - return $msg; -} - -sub custom_status_calc { - my ($self, %options) = @_; - - $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{state} = $options{new_datas}->{$self->{instance} . '_state'}; - - return 0; -} - -sub prefix_output { - my ($self, %options) = @_; - - return "Volume '" . $options{instance_value}->{name} . "' "; -} - -sub set_counters { - my ($self, %options) = @_; - - $self->{maps_counters_type} = [ - { name => 'volumes', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All volumes status are ok' }, - ]; - - $self->{maps_counters}->{volumes} = [ - { label => 'status', set => { - key_values => [ { name => 'state' }, { name => 'name' } ], - closure_custom_calc => $self->can('custom_status_calc'), - closure_custom_output => $self->can('custom_status_output'), - closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => \&catalog_status_threshold, - } - }, - ]; -} - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options); - bless $self, $class; - - $options{options}->add_options(arguments => { - 'filter-name:s' => { name => 'filter_name' }, - 'warning-status:s' => { name => 'warning_status' }, - 'critical-status:s' => { name => 'critical_status', default => '%{state} !~ /online/i' }, - }); - - return $self; -} - -sub check_options { - my ($self, %options) = @_; - $self->SUPER::check_options(%options); - - $self->change_macros(macros => ['warning_status', 'critical_status']); -} - -sub manage_selection { - my ($self, %options) = @_; - - my $result = $options{custom}->get(path => '/volumes'); - - foreach my $volume (@{$result}) { - if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && - $volume->{name} !~ /$self->{option_results}->{filter_name}/) { - $self->{output}->output_add(long_msg => "skipping '" . $volume->{name} . "': no matching filter name.", debug => 1); - next; - } - - $self->{volumes}->{$volume->{key}} = { - name => $volume->{name}, - state => $volume->{state}, - } - } - - if (scalar(keys %{$self->{volumes}}) <= 0) { - $self->{output}->add_option_msg(short_msg => "No entry found."); - $self->{output}->option_exit(); - } -} - -1; - -__END__ - -=head1 MODE - -Check NetApp volumes status. - -=over 8 - -=item B<--filter-name> - -Filter volume name (can be a regexp). - -=item B<--warning-status> - -Set warning threshold for status (Default: ''). -Can used special variables like: %{state} - -=item B<--critical-status> - -Set critical threshold for status (Default: '%{state} !~ /online/i'). -Can used special variables like: %{state} - -=back - -=cut diff --git a/storage/netapp/ontap/oncommandapi/mode/volumeusage.pm b/storage/netapp/ontap/oncommandapi/mode/volumeusage.pm deleted file mode 100644 index 284d38b32..000000000 --- a/storage/netapp/ontap/oncommandapi/mode/volumeusage.pm +++ /dev/null @@ -1,470 +0,0 @@ -# -# 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::netapp::ontap::oncommandapi::mode::volumeusage; - -use base qw(centreon::plugins::templates::counter); - -use strict; -use warnings; - -sub custom_usage_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - label => 'used', unit => 'B', - instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef, - value => $self->{result_values}->{used}, - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - min => 0, max => $self->{result_values}->{total} - ); -} - -sub custom_usage_threshold { - my ($self, %options) = @_; - - my ($exit, $threshold_value); - $threshold_value = $self->{result_values}->{used}; - $threshold_value = $self->{result_values}->{free} if (defined($self->{instance_mode}->{option_results}->{free})); - if ($self->{instance_mode}->{option_results}->{units} eq '%') { - $threshold_value = $self->{result_values}->{prct_used}; - $threshold_value = $self->{result_values}->{prct_free} if (defined($self->{instance_mode}->{option_results}->{free})); - } - $exit = $self->{perfdata}->threshold_check(value => $threshold_value, - threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, - { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]); - return $exit; -} - -sub custom_usage_output { - my ($self, %options) = @_; - - my ($used_value, $used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); - my ($free_value, $free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); - my ($total_value, $total_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); - - my $msg = sprintf("Total: %s Used: %s (%.2f%%) Free: %s (%.2f%%)", - $total_value . " " . $total_unit, - $used_value . " " . $used_unit, $self->{result_values}->{prct_used}, - $free_value . " " . $free_unit, $self->{result_values}->{prct_free}); - return $msg; -} - -sub custom_usage_calc { - my ($self, %options) = @_; - - $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_size_total'}; - $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_size_used'}; - - if ($self->{result_values}->{total} != 0) { - $self->{result_values}->{free} = $self->{result_values}->{total} - $self->{result_values}->{used}; - $self->{result_values}->{prct_used} = $self->{result_values}->{used} * 100 / $self->{result_values}->{total}; - $self->{result_values}->{prct_free} = 100 - $self->{result_values}->{prct_used}; - } else { - $self->{result_values}->{free} = '0'; - $self->{result_values}->{prct_used} = '0'; - $self->{result_values}->{prct_free} = '0'; - } - - return 0; -} - -sub custom_inode_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - label => 'inodes', unit => '%', - instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef, - value => sprintf("%.2f", $self->{result_values}->{prct_used}), - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), - min => 0, max => 100 - ); -} - -sub custom_inode_threshold { - my ($self, %options) = @_; - - my $exit = $self->{perfdata}->threshold_check(value => $self->{result_values}->{prct_used}, - threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, - { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]); - return $exit; -} - -sub custom_inode_output { - my ($self, %options) = @_; - - my $msg = sprintf("Inodes Used: %.2f%%", $self->{result_values}->{prct_used}); - return $msg; -} - -sub custom_inode_calc { - my ($self, %options) = @_; - - $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_inode_files_used'}; - $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_inode_files_total'}; - - if ($self->{result_values}->{total} != 0) { - $self->{result_values}->{prct_used} = $self->{result_values}->{used} * 100 / $self->{result_values}->{total}; - } else { - $self->{result_values}->{prct_used} = '0'; - } - - return 0; -} - -sub custom_snapshot_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - label => 'snapshot', unit => 'B', - instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef, - value => $self->{result_values}->{used}, - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - min => 0, max => $self->{result_values}->{total} - ); -} - -sub custom_snapshot_threshold { - my ($self, %options) = @_; - - my ($exit, $threshold_value); - $threshold_value = $self->{result_values}->{used}; - $threshold_value = $self->{result_values}->{free} if (defined($self->{instance_mode}->{option_results}->{free})); - if ($self->{instance_mode}->{option_results}->{units} eq '%') { - $threshold_value = $self->{result_values}->{prct_used}; - $threshold_value = $self->{result_values}->{prct_free} if (defined($self->{instance_mode}->{option_results}->{free})); - } - $exit = $self->{perfdata}->threshold_check(value => $threshold_value, - threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, - { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]); - return $exit; -} - -sub custom_snapshot_output { - my ($self, %options) = @_; - - my ($used_value, $used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); - my ($free_value, $free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); - my ($reserved_value, $reserved_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{reserved}); - - my $msg = sprintf("Snapshot Used: %s (%.2f%%) Free: %s (%.2f%%) Reserved: %s", - $used_value . " " . $used_unit, $self->{result_values}->{prct_used}, - $free_value . " " . $free_unit, $self->{result_values}->{prct_free}, - $reserved_value . " " . $reserved_unit); - return $msg; -} - -sub custom_snapshot_calc { - my ($self, %options) = @_; - - $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{free} = $options{new_datas}->{$self->{instance} . '_size_available_for_snapshot'}; - $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_size_used_by_snapshots'}; - $self->{result_values}->{reserved} = $options{new_datas}->{$self->{instance} . '_snapshot_reserve_size'}; - - $self->{result_values}->{total} = $self->{result_values}->{used} + $self->{result_values}->{free}; - $self->{result_values}->{prct_used} = $self->{result_values}->{used} * 100 / $self->{result_values}->{total}; - $self->{result_values}->{prct_free} = 100 - $self->{result_values}->{prct_used}; - - return 0; -} - -sub custom_compression_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - label => 'compresssaved', unit => 'B', - instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef, - value => $self->{result_values}->{used}, - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - min => 0, max => $self->{result_values}->{total}); -} - -sub custom_compression_threshold { - my ($self, %options) = @_; - - my ($exit, $threshold_value); - $threshold_value = $self->{result_values}->{used}; - $threshold_value = $self->{result_values}->{free} if (defined($self->{instance_mode}->{option_results}->{free})); - if ($self->{instance_mode}->{option_results}->{units} eq '%') { - $threshold_value = $self->{result_values}->{prct_used}; - $threshold_value = $self->{result_values}->{prct_free} if (defined($self->{instance_mode}->{option_results}->{free})); - } - $exit = $self->{perfdata}->threshold_check(value => $threshold_value, - threshold => [ { label => 'critical-' . $self->{label}, exit_litteral => 'critical' }, - { label => 'warning-' . $self->{label}, exit_litteral => 'warning' } ]); - return $exit; -} - -sub custom_compression_output { - my ($self, %options) = @_; - - my ($used_value, $used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); - - my $msg = sprintf("Compression Space Saved: %s (%.2f%%)", - $used_value . " " . $used_unit, $self->{result_values}->{prct_used}); - return $msg; -} - -sub custom_compression_calc { - my ($self, %options) = @_; - - $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_size_total'}; - $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_compression_space_saved'}; - - if ($self->{result_values}->{total} != 0) { - $self->{result_values}->{free} = $self->{result_values}->{total} - $self->{result_values}->{used}; - $self->{result_values}->{prct_used} = $self->{result_values}->{used} * 100 / $self->{result_values}->{total}; - $self->{result_values}->{prct_free} = 100 - $self->{result_values}->{prct_used}; - } else { - $self->{result_values}->{free} = '0'; - $self->{result_values}->{prct_used} = '0'; - $self->{result_values}->{prct_free} = '0'; - } - - return 0; -} - -sub custom_deduplication_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - label => 'dedupsaved', unit => 'B', - instances => $self->use_instances(extra_instance => $options{extra_instance}) ? $self->{result_values}->{display} : undef, - value => $self->{result_values}->{used}, - warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, total => $self->{result_values}->{total}, cast_int => 1), - min => 0, max => $self->{result_values}->{total} - ); -} - -sub custom_deduplication_threshold { - my ($self, %options) = @_; - - my ($exit, $threshold_value); - $threshold_value = $self->{result_values}->{used}; - $threshold_value = $self->{result_values}->{free} if (defined($self->{instance_mode}->{option_results}->{free})); - if ($self->{instance_mode}->{option_results}->{units} eq '%') { - $threshold_value = $self->{result_values}->{prct_used}; - $threshold_value = $self->{result_values}->{prct_free} if (defined($self->{instance_mode}->{option_results}->{free})); - } - $exit = $self->{perfdata}->threshold_check(value => $threshold_value, - threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, - { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]); - return $exit; -} - -sub custom_deduplication_output { - my ($self, %options) = @_; - - my ($used_value, $used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); - - my $msg = sprintf("Deduplication Space Saved: %s (%.2f%%)", - $used_value . " " . $used_unit, $self->{result_values}->{prct_used}); - return $msg; -} - -sub custom_deduplication_calc { - my ($self, %options) = @_; - - $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_size_total'}; - $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_deduplication_space_saved'}; - - if ($self->{result_values}->{total} != 0) { - $self->{result_values}->{free} = $self->{result_values}->{total} - $self->{result_values}->{used}; - $self->{result_values}->{prct_used} = $self->{result_values}->{used} * 100 / $self->{result_values}->{total}; - $self->{result_values}->{prct_free} = 100 - $self->{result_values}->{prct_used}; - } else { - $self->{result_values}->{free} = '0'; - $self->{result_values}->{prct_used} = '0'; - $self->{result_values}->{prct_free} = '0'; - } - - return 0; -} - -sub prefix_output { - my ($self, %options) = @_; - - return "Volume '" . $options{instance_value}->{name} . "' "; -} - -sub set_counters { - my ($self, %options) = @_; - - $self->{maps_counters_type} = [ - { name => 'volumes', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All volumes usage are ok' }, - ]; - - $self->{maps_counters}->{volumes} = [ - { label => 'usage', set => { - key_values => [ { name => 'size_used' }, { name => 'size_total' }, { name => 'name' } ], - closure_custom_calc => $self->can('custom_usage_calc'), - closure_custom_output => $self->can('custom_usage_output'), - closure_custom_perfdata => $self->can('custom_usage_perfdata'), - closure_custom_threshold_check => $self->can('custom_usage_threshold'), - } - }, - { label => 'inodes', set => { - key_values => [ { name => 'inode_files_used' }, { name => 'inode_files_total' }, { name => 'name' } ], - closure_custom_calc => $self->can('custom_inode_calc'), - closure_custom_output => $self->can('custom_inode_output'), - closure_custom_perfdata => $self->can('custom_inode_perfdata'), - closure_custom_threshold_check => $self->can('custom_inode_threshold'), - } - }, - { label => 'snapshot', set => { - key_values => [ { name => 'size_available_for_snapshot' }, { name => 'size_used_by_snapshots' }, { name => 'snapshot_reserve_size' }, { name => 'name' } ], - closure_custom_calc => $self->can('custom_snapshot_calc'), - closure_custom_output => $self->can('custom_snapshot_output'), - closure_custom_perfdata => $self->can('custom_snapshot_perfdata'), - closure_custom_threshold_check => $self->can('custom_snapshot_threshold'), - } - }, - { label => 'compresssaved', set => { - key_values => [ { name => 'compression_space_saved' }, { name => 'size_total' }, { name => 'name' } ], - closure_custom_calc => $self->can('custom_compression_calc'), - closure_custom_output => $self->can('custom_compression_output'), - closure_custom_perfdata => $self->can('custom_compression_perfdata'), - closure_custom_threshold_check => $self->can('custom_compression_threshold'), - } - }, - { label => 'dedupsaved', set => { - key_values => [ { name => 'deduplication_space_saved' }, { name => 'size_total' }, { name => 'name' } ], - closure_custom_calc => $self->can('custom_deduplication_calc'), - closure_custom_output => $self->can('custom_deduplication_output'), - closure_custom_perfdata => $self->can('custom_deduplication_perfdata'), - closure_custom_threshold_check => $self->can('custom_deduplication_threshold'), - } - }, - ]; -} - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options); - bless $self, $class; - - $options{options}->add_options(arguments => { - 'filter-name:s' => { name => 'filter_name' }, - 'filter-state:s' => { name => 'filter_state' }, - 'filter-style:s' => { name => 'filter_style' }, - 'filter-type:s' => { name => 'filter_type' }, - 'units:s' => { name => 'units', default => '%' }, - 'free' => { name => 'free' } - }); - - return $self; -} - -sub manage_selection { - my ($self, %options) = @_; - - my $result = $options{custom}->get(path => '/volumes'); - - foreach my $volume (@{$result}) { - if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && - $volume->{name} !~ /$self->{option_results}->{filter_name}/) { - $self->{output}->output_add(long_msg => "skipping '" . $volume->{name} . "': no matching filter name.", debug => 1); - next; - } - - if (defined($self->{option_results}->{filter_state}) && $self->{option_results}->{filter_state} ne '' && - $volume->{state} !~ /$self->{option_results}->{filter_state}/) { - $self->{output}->output_add(long_msg => "skipping '" . $volume->{name} . "': no matching filter state : '" . $volume->{state} . "'", debug => 1); - next; - } - - if (defined($self->{option_results}->{filter_style}) && $self->{option_results}->{filter_style} ne '' && - $volume->{style} !~ /$self->{option_results}->{filter_style}/) { - $self->{output}->output_add(long_msg => "skipping '" . $volume->{name} . "': no matching filter style : '" . $volume->{style} . "'", debug => 1); - next; - } - - if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' && - $volume->{vol_type} !~ /$self->{option_results}->{filter_type}/) { - $self->{output}->output_add(long_msg => "skipping '" . $volume->{name} . "': no matching filter type : '" . $volume->{vol_type} . "'", debug => 1); - next; - } - - $self->{volumes}->{$volume->{key}} = { - name => $volume->{name}, - size_total => $volume->{size_total}, - size_used => $volume->{size_used}, - compression_space_saved => $volume->{compression_space_saved}, - deduplication_space_saved => $volume->{deduplication_space_saved}, - size_available_for_snapshot => $volume->{size_available_for_snapshot}, - size_used_by_snapshots => $volume->{size_used_by_snapshots}, - snapshot_reserve_size => $volume->{snapshot_reserve_size}, - inode_files_used => $volume->{inode_files_used}, - inode_files_total => $volume->{inode_files_total}, - } - } - - if (scalar(keys %{$self->{volumes}}) <= 0) { - $self->{output}->add_option_msg(short_msg => "No entry found."); - $self->{output}->option_exit(); - } -} - -1; - -__END__ - -=head1 MODE - -Check NetApp volumes usage (space, inodes, snapshot, compression and deduplication) - -=over 8 - -=item B<--filter-*> - -Filter volume. -Can be: 'name', 'state', 'style', 'type' (can be a regexp). - -=item B<--warning-*> - -Threshold warning. -Can be: 'usage', 'inodes', 'snapshot', 'compresssaved', 'dedupsaved'. - -=item B<--critical-*> - -Threshold critical. -Can be: 'usage', 'inodes', 'snapshot', 'compresssaved', 'dedupsaved'. - -=item B<--units> - -Units of thresholds (Default: '%') ('%', 'B'). - -=item B<--free> - -Thresholds are on free space left. - -=back - -=cut diff --git a/storage/netapp/ontap/oncommandapi/plugin.pm b/storage/netapp/ontap/oncommandapi/plugin.pm index ac6c4f4ca..acc5487e7 100644 --- a/storage/netapp/ontap/oncommandapi/plugin.pm +++ b/storage/netapp/ontap/oncommandapi/plugin.pm @@ -29,8 +29,7 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options); bless $self, $class; - $self->{version} = '1.0'; - %{$self->{modes}} = ( + $self->{modes} = { 'aggregate-raid-status' => 'storage::netapp::ontap::oncommandapi::mode::aggregateraidstatus', 'aggregate-status' => 'storage::netapp::ontap::oncommandapi::mode::aggregatestatus', 'aggregate-usage' => 'storage::netapp::ontap::oncommandapi::mode::aggregateusage', @@ -46,6 +45,7 @@ sub new { 'list-luns' => 'storage::netapp::ontap::oncommandapi::mode::listluns', 'list-nodes' => 'storage::netapp::ontap::oncommandapi::mode::listnodes', 'list-snapmirrors' => 'storage::netapp::ontap::oncommandapi::mode::listsnapmirrors', + 'list-svm' => 'storage::netapp::ontap::oncommandapi::mode::listsvm', 'list-volumes' => 'storage::netapp::ontap::oncommandapi::mode::listvolumes', 'lun-alignment' => 'storage::netapp::ontap::oncommandapi::mode::lunalignment', 'lun-online' => 'storage::netapp::ontap::oncommandapi::mode::lunonline', @@ -55,12 +55,10 @@ sub new { 'qtree-status' => 'storage::netapp::ontap::oncommandapi::mode::qtreestatus', 'snapmirror-status' => 'storage::netapp::ontap::oncommandapi::mode::snapmirrorstatus', 'snapmirror-usage' => 'storage::netapp::ontap::oncommandapi::mode::snapmirrorusage', - 'volume-io' => 'storage::netapp::ontap::oncommandapi::mode::volumeio', - 'volume-status' => 'storage::netapp::ontap::oncommandapi::mode::volumestatus', - 'volume-usage' => 'storage::netapp::ontap::oncommandapi::mode::volumeusage' - ); + 'volumes' => 'storage::netapp::ontap::oncommandapi::mode::volumes' + }; - $self->{custom_modes}{api} = 'storage::netapp::ontap::oncommandapi::custom::api'; + $self->{custom_modes}->{api} = 'storage::netapp::ontap::oncommandapi::custom::api'; return $self; }