From 1f4b8340663c2e1163e672274d66fd47f88fedd7 Mon Sep 17 00:00:00 2001 From: cgagnaire Date: Tue, 3 Apr 2018 10:31:55 +0200 Subject: [PATCH] add netapp restapi plugin --- storage/netapp/restapi/custom/restapi.pm | 263 ++++++++++ .../restapi/mode/aggregateraidstatus.pm | 210 ++++++++ .../netapp/restapi/mode/aggregatestatus.pm | 208 ++++++++ storage/netapp/restapi/mode/aggregateusage.pm | 315 +++++++++++ storage/netapp/restapi/mode/clusterio.pm | 149 ++++++ storage/netapp/restapi/mode/clusterstatus.pm | 186 +++++++ storage/netapp/restapi/mode/clusterusage.pm | 148 ++++++ storage/netapp/restapi/mode/diskfailed.pm | 147 ++++++ storage/netapp/restapi/mode/diskspare.pm | 161 ++++++ storage/netapp/restapi/mode/fcportstatus.pm | 191 +++++++ storage/netapp/restapi/mode/listaggregates.pm | 154 ++++++ storage/netapp/restapi/mode/listclusters.pm | 123 +++++ storage/netapp/restapi/mode/listfcports.pm | 125 +++++ storage/netapp/restapi/mode/listluns.pm | 129 +++++ storage/netapp/restapi/mode/listnodes.pm | 123 +++++ .../netapp/restapi/mode/listsnapmirrors.pm | 123 +++++ storage/netapp/restapi/mode/listvolumes.pm | 126 +++++ storage/netapp/restapi/mode/lunalignment.pm | 170 ++++++ storage/netapp/restapi/mode/lunonline.pm | 159 ++++++ storage/netapp/restapi/mode/lunusage.pm | 199 +++++++ .../netapp/restapi/mode/nodefailoverstatus.pm | 204 ++++++++ .../netapp/restapi/mode/nodehardwarestatus.pm | 233 +++++++++ storage/netapp/restapi/mode/qtreestatus.pm | 196 +++++++ .../netapp/restapi/mode/snapmirrorstatus.pm | 181 +++++++ .../netapp/restapi/mode/snapmirrorusage.pm | 144 ++++++ storage/netapp/restapi/mode/volumeio.pm | 225 ++++++++ storage/netapp/restapi/mode/volumestatus.pm | 180 +++++++ storage/netapp/restapi/mode/volumeusage.pm | 487 ++++++++++++++++++ storage/netapp/restapi/plugin.pm | 74 +++ 29 files changed, 5333 insertions(+) create mode 100644 storage/netapp/restapi/custom/restapi.pm create mode 100644 storage/netapp/restapi/mode/aggregateraidstatus.pm create mode 100644 storage/netapp/restapi/mode/aggregatestatus.pm create mode 100644 storage/netapp/restapi/mode/aggregateusage.pm create mode 100644 storage/netapp/restapi/mode/clusterio.pm create mode 100644 storage/netapp/restapi/mode/clusterstatus.pm create mode 100644 storage/netapp/restapi/mode/clusterusage.pm create mode 100644 storage/netapp/restapi/mode/diskfailed.pm create mode 100644 storage/netapp/restapi/mode/diskspare.pm create mode 100644 storage/netapp/restapi/mode/fcportstatus.pm create mode 100644 storage/netapp/restapi/mode/listaggregates.pm create mode 100644 storage/netapp/restapi/mode/listclusters.pm create mode 100644 storage/netapp/restapi/mode/listfcports.pm create mode 100644 storage/netapp/restapi/mode/listluns.pm create mode 100644 storage/netapp/restapi/mode/listnodes.pm create mode 100644 storage/netapp/restapi/mode/listsnapmirrors.pm create mode 100644 storage/netapp/restapi/mode/listvolumes.pm create mode 100644 storage/netapp/restapi/mode/lunalignment.pm create mode 100644 storage/netapp/restapi/mode/lunonline.pm create mode 100644 storage/netapp/restapi/mode/lunusage.pm create mode 100644 storage/netapp/restapi/mode/nodefailoverstatus.pm create mode 100644 storage/netapp/restapi/mode/nodehardwarestatus.pm create mode 100644 storage/netapp/restapi/mode/qtreestatus.pm create mode 100644 storage/netapp/restapi/mode/snapmirrorstatus.pm create mode 100644 storage/netapp/restapi/mode/snapmirrorusage.pm create mode 100644 storage/netapp/restapi/mode/volumeio.pm create mode 100644 storage/netapp/restapi/mode/volumestatus.pm create mode 100644 storage/netapp/restapi/mode/volumeusage.pm create mode 100644 storage/netapp/restapi/plugin.pm diff --git a/storage/netapp/restapi/custom/restapi.pm b/storage/netapp/restapi/custom/restapi.pm new file mode 100644 index 000000000..072efffe8 --- /dev/null +++ b/storage/netapp/restapi/custom/restapi.pm @@ -0,0 +1,263 @@ +# +# Copyright 2018 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::restapi::custom::restapi; + +use strict; +use warnings; +use centreon::plugins::http; +use JSON::XS; +use URI::Encode; + +sub new { + my ($class, %options) = @_; + my $self = {}; + bless $self, $class; + + if (!defined($options{output})) { + print "Class Custom: Need to specify 'output' argument.\n"; + exit 3; + } + if (!defined($options{options})) { + $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); + $options{output}->option_exit(); + } + + if (!defined($options{noptions})) { + $options{options}->add_options(arguments => + { + "hostname:s@" => { name => 'hostname' }, + "url-path:s@" => { name => 'url_path' }, + "port:s@" => { name => 'port' }, + "proto:s@" => { name => 'proto' }, + "username:s@" => { name => 'username' }, + "password:s@" => { name => 'password' }, + "proxyurl:s@" => { name => 'proxyurl' }, + "timeout:s@" => { name => 'timeout' }, + "ssl:s@" => { name => 'ssl' }, + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{mode} = $options{mode}; + $self->{http} = centreon::plugins::http->new(output => $self->{output}); + + return $self; + +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults { + my ($self, %options) = @_; + + foreach (keys %{$options{default}}) { + if ($_ eq $self->{mode}) { + for (my $i = 0; $i < scalar(@{$options{default}->{$_}}); $i++) { + foreach my $opt (keys %{$options{default}->{$_}[$i]}) { + if (!defined($self->{option_results}->{$opt}[$i])) { + $self->{option_results}->{$opt}[$i] = $options{default}->{$_}[$i]->{$opt}; + } + } + } + } + } +} + +sub check_options { + my ($self, %options) = @_; + + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? shift(@{$self->{option_results}->{hostname}}) : undef; + $self->{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->{proxyurl} = (defined($self->{option_results}->{proxyurl})) ? shift(@{$self->{option_results}->{proxyurl}}) : undef; + $self->{ssl} = (defined($self->{option_results}->{ssl})) ? shift(@{$self->{option_results}->{ssl}}) : 'tlsv1'; + + if (!defined($self->{hostname})) { + $self->{output}->add_option_msg(short_msg => "Need to specify hostname option."); + $self->{output}->option_exit(); + } + + if (!defined($self->{hostname}) || + scalar(@{$self->{option_results}->{hostname}}) == 0) { + return 0; + } + + return 1; +} + +sub build_options_for_httplib { + my ($self, %options) = @_; + + $self->{option_results}->{hostname} = $self->{hostname}; + $self->{option_results}->{timeout} = $self->{timeout}; + $self->{option_results}->{port} = $self->{port}; + $self->{option_results}->{proto} = $self->{proto}; + $self->{option_results}->{proxyurl} = $self->{proxyurl}; + $self->{option_results}->{credentials} = 1; + $self->{option_results}->{username} = $self->{username}; + $self->{option_results}->{password} = $self->{password}; + $self->{option_results}->{ssl} = $self->{ssl}; + $self->{option_results}->{warning_status} = ''; + $self->{option_results}->{critical_status} = ''; +} + +sub settings { + my ($self, %options) = @_; + + $self->build_options_for_httplib(); + $self->{http}->set_options(%{$self->{option_results}}); +} + +sub get_connection_info { + my ($self, %options) = @_; + + return $self->{hostname} . ":" . $self->{port}; +} + +sub get_objects { + my ($self, %options) = @_; + + my %objects; + my $objects = $self->get(%options); + foreach my $object (@{$objects}) { + $objects{$object->{$options{key}}} = $object->{$options{name}}; + } + + return \%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 $url_path = $self->{url_path} . $options{path}; + $url_path .= '?' . $options{uri} 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); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); + $self->{output}->option_exit(); + } + + if (defined($content->{errmsg})) { + $self->{output}->add_option_msg(short_msg => "Cannot get data: " . $content->{errmsg}); + $self->{output}->option_exit(); + } + + return $content; +} + +sub get { + my ($self, %options) = @_; + + $self->settings(); + + my @result; + while(my $content = $self->get_next(%options)) { + push @result, @{$content->{result}->{records}}; + + last if (!defined($content->{result}->{nextTag})); + $options{nextTag} = $content->{result}->{nextTag}; + } + + return \@result; +} + +1; + +__END__ + +=head1 NAME + +NetApp OnCommand REST API + +=head1 SYNOPSIS + +NetApp OnCommand Rest API custom mode + +=head1 REST API OPTIONS + +=over 8 + +=item B<--hostname> + +NetApp hostname. + +=item B<--url-path> + +OnCommand API url path (Default: '/api/4.0/ontap') + +=item B<--port> + +OnCommand API port (Default: 8443) + +=item B<--proto> + +Specify https if needed (Default: 'https') + +=item B<--username> + +OnCommand API username. + +=item B<--password> + +OnCommand API password. + +=item B<--proxyurl> + +Proxy URL if any + +=item B<--timeout> + +Set HTTP timeout + +=item B<--ssl> + +SSL version (Default: tlsv1) + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/storage/netapp/restapi/mode/aggregateraidstatus.pm b/storage/netapp/restapi/mode/aggregateraidstatus.pm new file mode 100644 index 000000000..90cc6eeea --- /dev/null +++ b/storage/netapp/restapi/mode/aggregateraidstatus.pm @@ -0,0 +1,210 @@ +# +# Copyright 2018 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::restapi::mode::aggregateraidstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("Raid status is '%s' [type: %s] [size: %s]", + $self->{result_values}->{status}, $self->{result_values}->{type}, $self->{result_values}->{size}); + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_raid_status'}; + $self->{result_values}->{type} = $options{new_datas}->{$self->{instance} . '_raid_type'}; + $self->{result_values}->{size} = $options{new_datas}->{$self->{instance} . '_raid_size'}; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Aggregate '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'aggregates', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All aggregates raid status are ok' }, + ]; + + $self->{maps_counters}->{aggregates} = [ + { label => 'status', set => { + key_values => [ { name => 'raid_status' }, { name => 'raid_type' }, { name => 'raid_size' }, { 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 => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "filter-node:s" => { name => 'filter_node' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status', default => '%{status} !~ /normal/i' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $clusters; + my $nodes; + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '') { + $clusters = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '') { + $nodes = $options{custom}->get_objects(path => '/nodes', key => 'key', name => 'name'); + } + + my $result = $options{custom}->get(path => '/aggregates'); + + foreach my $aggregate (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $aggregate->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter name.", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '' && + defined($nodes->{$aggregate->{node_key}}) && $nodes->{$aggregate->{node_key}} !~ /$self->{option_results}->{filter_node}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter node '" . $nodes->{$aggregate->{node_key}} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($clusters->{$aggregate->{cluster_key}}) && $clusters->{$aggregate->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter cluster '" . $clusters->{$aggregate->{cluster_key}} . "'", debug => 1); + next; + } + + $self->{aggregates}->{$aggregate->{key}} = { + name => $aggregate->{name}, + raid_status => $aggregate->{raid_status}, + raid_type => $aggregate->{raid_type}, + raid_size => $aggregate->{raid_size}, + } + } + + if (scalar(keys %{$self->{aggregates}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp aggregates raid status. + +=over 8 + +=item B<--filter-*> + +Filter volume. +Can be: 'name', 'node', 'cluster' (can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{status}, %{type}, %{size} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} !~ /normal/i'). +Can used special variables like: %{status}, %{type}, %{size} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/aggregatestatus.pm b/storage/netapp/restapi/mode/aggregatestatus.pm new file mode 100644 index 000000000..769da75f0 --- /dev/null +++ b/storage/netapp/restapi/mode/aggregatestatus.pm @@ -0,0 +1,208 @@ +# +# Copyright 2018 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::restapi::mode::aggregatestatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("State is '%s', Mirror status 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'}; + $self->{result_values}->{mirror_status} = $options{new_datas}->{$self->{instance} . '_mirror_status'}; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Aggregate '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'aggregates', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All aggregates status are ok' }, + ]; + + $self->{maps_counters}->{aggregates} = [ + { label => 'status', set => { + key_values => [ { name => 'state' }, { name => 'mirror_status' }, { 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 => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "filter-node:s" => { name => 'filter_node' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status', default => '%{state} !~ /online/i' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $clusters; + my $nodes; + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '') { + $clusters = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '') { + $nodes = $options{custom}->get_objects(path => '/nodes', key => 'key', name => 'name'); + } + + my $result = $options{custom}->get(path => '/aggregates'); + + foreach my $aggregate (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $aggregate->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter name.", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '' && + defined($nodes->{$aggregate->{node_key}}) && $nodes->{$aggregate->{node_key}} !~ /$self->{option_results}->{filter_node}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter node '" . $nodes->{$aggregate->{node_key}} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($clusters->{$aggregate->{cluster_key}}) && $clusters->{$aggregate->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter cluster '" . $clusters->{$aggregate->{cluster_key}} . "'", debug => 1); + next; + } + + $self->{aggregates}->{$aggregate->{key}} = { + name => $aggregate->{name}, + state => $aggregate->{state}, + mirror_status => $aggregate->{mirror_status}, + } + } + + if (scalar(keys %{$self->{aggregates}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp aggregates status. + +=over 8 + +=item B<--filter-*> + +Filter aggregate. +Can be: 'name', 'node', 'cluster' (can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{state}, %{mirror_status} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{state} !~ /online/i'). +Can used special variables like: %{state}, %{mirror_status} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/aggregateusage.pm b/storage/netapp/restapi/mode/aggregateusage.pm new file mode 100644 index 000000000..5daa1291b --- /dev/null +++ b/storage/netapp/restapi/mode/aggregateusage.pm @@ -0,0 +1,315 @@ +# +# Copyright 2018 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::restapi::mode::aggregateusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_usage_perfdata { + my ($self, %options) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'used' . $extra_label, + unit => 'B', + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}, 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($instance_mode->{option_results}->{free})); + if ($instance_mode->{option_results}->{units} eq '%') { + $threshold_value = $self->{result_values}->{prct_used}; + $threshold_value = $self->{result_values}->{prct_free} if (defined($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_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_snapshot_perfdata { + my ($self, %options) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'snapshot' . $extra_label, + unit => 'B', + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}, 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($instance_mode->{option_results}->{free})); + if ($instance_mode->{option_results}->{units} eq '%') { + $threshold_value = $self->{result_values}->{prct_used}; + $threshold_value = $self->{result_values}->{prct_free} if (defined($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_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 ($total_value, $total_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + + my $msg = sprintf("Snapshot 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_snapshot_calc { + my ($self, %options) = @_; + + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; + $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_snapshot_size_total'}; + $self->{result_values}->{used} = $options{new_datas}->{$self->{instance} . '_snapshot_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 prefix_output { + my ($self, %options) = @_; + + return "Aggregate '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'aggregates', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All aggregates usage are ok' }, + ]; + + $self->{maps_counters}->{aggregates} = [ + { 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 => 'snapshot', set => { + key_values => [ { name => 'snapshot_size_used' }, { name => 'snapshot_size_total' }, { 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'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "filter-node:s" => { name => 'filter_node' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + "filter-state:s" => { name => 'filter_state' }, + "filter-type:s" => { name => 'filter_type' }, + "units:s" => { name => 'units', default => '%' }, + "free" => { name => 'free' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $clusters; + my $nodes; + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '') { + $clusters = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '') { + $nodes = $options{custom}->get_objects(path => '/nodes', key => 'key', name => 'name'); + } + + my $result = $options{custom}->get(path => '/aggregates'); + + foreach my $aggregate (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $aggregate->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter name.", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_state}) && $self->{option_results}->{filter_state} ne '' && + $aggregate->{state} !~ /$self->{option_results}->{filter_state}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter state : '" . $aggregate->{state} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' && + $aggregate->{aggregate_type} !~ /$self->{option_results}->{filter_type}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter type : '" . $aggregate->{vol_type} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '' && + defined($nodes->{$aggregate->{node_key}}) && $nodes->{$aggregate->{node_key}} !~ /$self->{option_results}->{filter_node}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter node '" . $nodes->{$aggregate->{node_key}} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($clusters->{$aggregate->{cluster_key}}) && $clusters->{$aggregate->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter cluster '" . $clusters->{$aggregate->{cluster_key}} . "'", debug => 1); + next; + } + + $self->{aggregates}->{$aggregate->{key}} = { + name => $aggregate->{name}, + size_total => $aggregate->{size_total}, + size_used => $aggregate->{size_used}, + snapshot_size_total => $aggregate->{snapshot_size_total}, + snapshot_size_used => $aggregate->{snapshot_size_used}, + } + } + + if (scalar(keys %{$self->{aggregates}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp aggregates usage. + +=over 8 + +=item B<--filter-*> + +Filter volume. +Can be: 'name', 'node', 'cluster', 'state', 'type' (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'usage', 'snapshot'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'usage', 'snapshot'. + +=item B<--units> + +Units of thresholds (Default: '%') ('%', 'B'). + +=item B<--free> + +Thresholds are on free space left. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/clusterio.pm b/storage/netapp/restapi/mode/clusterio.pm new file mode 100644 index 000000000..ef6bf03f8 --- /dev/null +++ b/storage/netapp/restapi/mode/clusterio.pm @@ -0,0 +1,149 @@ +# +# Copyright 2018 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::restapi::mode::clusterio; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub prefix_output { + my ($self, %options) = @_; + + return "Cluster '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'clusters', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All clusters IOs are ok' }, + ]; + + $self->{maps_counters}->{clusters} = [ + { label => 'total-throughput', set => { + key_values => [ { name => 'total_throughput' }, { name => 'name' } ], + output_template => 'Total throughput: %.2f %s/s', + output_change_bytes => 1, + perfdatas => [ + { label => 'total_throughput', value => 'total_throughput_absolute', template => '%.2f', + min => 0, unit => 'B/s', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { label => 'total-ops', set => { + key_values => [ { name => 'total_ops' }, { name => 'name' } ], + output_template => 'Total IOPS: %.2f ops/s', + perfdatas => [ + { label => 'total_ops', value => 'total_ops_absolute', template => '%.2f', + min => 0, unit => 'ops/s', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + 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 => '/clusters'); + foreach my $cluster (@{$names}) { + $names_hash{$cluster->{key}} = { + name => $cluster->{name}, + }; + } + + my $args = ''; + my $append = ''; + foreach my $metric ('total_ops', 'total_throughput') { + $args .= $append . 'name=' . $metric; + $append = '&'; + } + + my $result = $options{custom}->get(path => '/clusters/metrics', args => $args); + + foreach my $cluster (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + defined($names_hash{$cluster->{resource_key}}) && $names_hash{$cluster->{resource_key}}->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $names_hash{$cluster->{resource_key}}->{name} . "': no matching filter name.", debug => 1); + next; + } + + foreach my $metric (@{$cluster->{metrics}}) { + $self->{clusters}->{$cluster->{resource_key}}->{name} = $names_hash{$cluster->{resource_key}}->{name}; + $self->{clusters}->{$cluster->{resource_key}}->{total_ops} = ${$metric->{samples}}[0]->{value} if ($metric->{name} eq 'total_ops'); + $self->{clusters}->{$cluster->{resource_key}}->{total_throughput} = ${$metric->{samples}}[0]->{value} if ($metric->{name} eq 'total_throughput'); + } + } + + if (scalar(keys %{$self->{clusters}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp clusters IOs. + +=over 8 + +=item B<--filter-name> + +Filter snapmirror name (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'total-throughput', 'total-ops'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'total-throughput', 'total-ops'. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/clusterstatus.pm b/storage/netapp/restapi/mode/clusterstatus.pm new file mode 100644 index 000000000..43db04fbb --- /dev/null +++ b/storage/netapp/restapi/mode/clusterstatus.pm @@ -0,0 +1,186 @@ +# +# Copyright 2018 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::restapi::mode::clusterstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("Status is '%s'", $self->{result_values}->{status}); + $msg .= sprintf(", Metro Cluster is '%s' [mode: %s]", + $self->{result_values}->{metro_cluster_configuration_state}, + $self->{result_values}->{metro_cluster_mode}) if ($self->{result_values}->{metro_cluster_configuration_state} !~ /-/); + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; + $self->{result_values}->{metro_cluster_mode} = $options{new_datas}->{$self->{instance} . '_metro_cluster_mode'}; + $self->{result_values}->{metro_cluster_configuration_state} = $options{new_datas}->{$self->{instance} . '_metro_cluster_configuration_state'}; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Cluster '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'clusters', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All clusters status are ok' }, + ]; + + $self->{maps_counters}->{clusters} = [ + { label => 'status', set => { + key_values => [ { name => 'status' }, { name => 'metro_cluster_mode' }, { name => 'metro_cluster_configuration_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 => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status', default => '%{status} !~ /ok/i' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/clusters'); + + foreach my $cluster (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $cluster->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $cluster->{name} . "': no matching filter name.", debug => 1); + next; + } + + $self->{clusters}->{$cluster->{key}} = { + name => $cluster->{name}, + status => $cluster->{status}, + metro_cluster_mode => defined($cluster->{metro_cluster_mode}) ? $cluster->{metro_cluster_mode} : "-", + metro_cluster_configuration_state => defined($cluster->{metro_cluster_configuration_state}) ? $cluster->{metro_cluster_configuration_state} : "-", + } + } + + if (scalar(keys %{$self->{clusters}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp clusters status. + +=over 8 + +=item B<--filter-name> + +Filter snapmirror name (can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{status}, %{metro_cluster_mode}, %{metro_cluster_configuration_state} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} !~ /ok/i'). +Can used special variables like: %{status}, %{metro_cluster_mode}, %{metro_cluster_configuration_state} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/clusterusage.pm b/storage/netapp/restapi/mode/clusterusage.pm new file mode 100644 index 000000000..b46645fce --- /dev/null +++ b/storage/netapp/restapi/mode/clusterusage.pm @@ -0,0 +1,148 @@ +# +# Copyright 2018 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::restapi::mode::clusterusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub prefix_output { + my ($self, %options) = @_; + + return "Cluster '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'clusters', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All clusters usage are ok' }, + ]; + + $self->{maps_counters}->{clusters} = [ + { label => 'max-node-utilization', set => { + key_values => [ { name => 'max_node_utilization' }, { name => 'name' } ], + output_template => 'Node utilization: %.2f %%', + perfdatas => [ + { label => 'max_node_utilization', value => 'max_node_utilization_absolute', template => '%.2f', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { label => 'max-aggregate-utilization', set => { + key_values => [ { name => 'max_aggregate_utilization' }, { name => 'name' } ], + output_template => 'Aggregate utilization: %.2f %%', + perfdatas => [ + { label => 'max_aggregate_utilization', value => 'max_aggregate_utilization_absolute', template => '%.2f', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + 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 => '/clusters'); + foreach my $cluster (@{$names}) { + $names_hash{$cluster->{key}} = { + name => $cluster->{name}, + }; + } + + my $args = ''; + my $append = ''; + foreach my $metric ('max_node_utilization', 'max_aggregate_utilization') { + $args .= $append . 'name=' . $metric; + $append = '&'; + } + + my $result = $options{custom}->get(path => '/clusters/metrics', args => $args); + + foreach my $cluster (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + defined($names_hash{$cluster->{resource_key}}) && $names_hash{$cluster->{resource_key}}->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $names_hash{$cluster->{resource_key}}->{name} . "': no matching filter name.", debug => 1); + next; + } + + foreach my $metric (@{$cluster->{metrics}}) { + $self->{clusters}->{$cluster->{resource_key}}->{name} = $names_hash{$cluster->{resource_key}}->{name}; + $self->{clusters}->{$cluster->{resource_key}}->{max_node_utilization} = ${$metric->{samples}}[0]->{value} if ($metric->{name} eq 'max_node_utilization'); + $self->{clusters}->{$cluster->{resource_key}}->{max_aggregate_utilization} = ${$metric->{samples}}[0]->{value} if ($metric->{name} eq 'max_aggregate_utilization'); + } + } + + if (scalar(keys %{$self->{clusters}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp clusters usage. + +=over 8 + +=item B<--filter-name> + +Filter snapmirror name (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'max-node-utilization', 'max-aggregate-utilization'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'max-node-utilization', 'max-aggregate-utilization'. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/diskfailed.pm b/storage/netapp/restapi/mode/diskfailed.pm new file mode 100644 index 000000000..bcb472a6b --- /dev/null +++ b/storage/netapp/restapi/mode/diskfailed.pm @@ -0,0 +1,147 @@ +# +# Copyright 2018 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::restapi::mode::diskfailed; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'disks', type => 0}, + ]; + + $self->{maps_counters}->{disks} = [ + { label => 'failed', set => { + key_values => [ { name => 'failed' } ], + output_template => 'Failed disks: %d', + perfdatas => [ + { label => 'failed', value => 'failed_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'pre-failed', set => { + key_values => [ { name => 'pre_failed' } ], + output_template => 'Pre-failed disks: %d', + perfdatas => [ + { label => 'pre_failed', value => 'pre_failed_absolute', template => '%d', + min => 0 }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-node:s" => { name => 'filter_node' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $clusters; + my $nodes; + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '') { + $clusters = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '') { + $nodes = $options{custom}->get_objects(path => '/nodes', key => 'key', name => 'name'); + } + + my $result = $options{custom}->get(path => '/disks'); + + $self->{disks}->{failed} = 0; + $self->{disks}->{pre_failed} = 0; + + foreach my $disk (@{$result}) { + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '' && + defined($nodes->{$disk->{owner_node_key}}) && $nodes->{$disk->{owner_node_key}} !~ /$self->{option_results}->{filter_node}/) { + $self->{output}->output_add(long_msg => "skipping '" . $disk->{name} . "': no matching filter node '" . $nodes->{$disk->{owner_node_key}} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($clusters->{$disk->{cluster_key}}) && $clusters->{$disk->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $disk->{name} . "': no matching filter cluster '" . $clusters->{$disk->{cluster_key}} . "'", debug => 1); + next; + } + + if ($disk->{is_failed}) { + $self->{disks}->{failed}++; + $self->{output}->output_add(long_msg => "Disk '" . $disk->{name} . "' from cluster '" . $clusters->{$disk->{cluster_key}} . "' is in 'failed' state (reason: '" . $disk->{failed_reason} . "') [shelf: " . $disk->{shelf} . "] [serial_number: " . $disk->{serial_number} . "] [model: " . $disk->{model} . "]"); + } + if ($disk->{is_prefailed}) { + $self->{disks}->{pre_failed}++; + $self->{output}->output_add(long_msg => "Disk '" . $disk->{name} . "' from cluster '" . $clusters->{$disk->{cluster_key}} . "' is in 'pre-failed' state [shelf: " . $disk->{shelf} . "] [serial_number: " . $disk->{serial_number} . "] [model: " . $disk->{model} . "]"); + } + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp failed disks. + +=over 8 + +=item B<--filter-*> + +Filter disk. +Can be: 'node', 'cluster' (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'failed', 'pre-failed'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'failed', 'pre-failed'. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/diskspare.pm b/storage/netapp/restapi/mode/diskspare.pm new file mode 100644 index 000000000..0a7ddb5eb --- /dev/null +++ b/storage/netapp/restapi/mode/diskspare.pm @@ -0,0 +1,161 @@ +# +# Copyright 2018 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::restapi::mode::diskspare; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'disks', type => 0}, + ]; + + $self->{maps_counters}->{disks} = [ + { label => 'spare', set => { + key_values => [ { name => 'spare' } ], + output_template => 'Spare disks: %d', + perfdatas => [ + { label => 'spare', value => 'spare_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'zeroed', set => { + key_values => [ { name => 'zeroed' } ], + output_template => 'Zeroed disks: %d', + perfdatas => [ + { label => 'zeroed', value => 'zeroed_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'not-zeroed', set => { + key_values => [ { name => 'not_zeroed' } ], + output_template => 'Not zeroed disks: %d', + perfdatas => [ + { label => 'not_zeroed', value => 'not_zeroed_absolute', template => '%d', + min => 0 }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-node:s" => { name => 'filter_node' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $clusters; + my $nodes; + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '') { + $clusters = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '') { + $nodes = $options{custom}->get_objects(path => '/nodes', key => 'key', name => 'name'); + } + + my $result = $options{custom}->get(path => '/disks'); + + $self->{disks}->{spare} = 0; + $self->{disks}->{zeroed} = 0; + $self->{disks}->{not_zeroed} = 0; + + foreach my $disk (@{$result}) { + next if ($disk->{container_type} !~ /spare/i); + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '' && + defined($nodes->{$disk->{owner_node_key}}) && $nodes->{$disk->{owner_node_key}} !~ /$self->{option_results}->{filter_node}/) { + $self->{output}->output_add(long_msg => "skipping '" . $disk->{name} . "': no matching filter node '" . $nodes->{$disk->{owner_node_key}} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($clusters->{$disk->{cluster_key}}) && $clusters->{$disk->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $disk->{name} . "': no matching filter cluster '" . $clusters->{$disk->{cluster_key}} . "'", debug => 1); + next; + } + + $self->{disks}->{spare}++; + $self->{disks}->{zeroed}++ if ($disk->{is_zeroed}); + if (!$disk->{is_zeroed}) { + $self->{disks}->{not_zeroed}++; + + my $long_msg = "Spare disk '" . $disk->{name} . "' is not in 'zeroed' state [shelf: " . $disk->{shelf} . "]"; + $long_msg .= " [node: " . $nodes->{$disk->{owner_node_key}} . "]" if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne ''); + $long_msg .= " [cluster: " . $clusters->{$disk->{cluster_key}} . "]" if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne ''); + $self->{output}->output_add(long_msg => $long_msg); + } + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp spare disks. + +=over 8 + +=item B<--filter-*> + +Filter disk. +Can be: 'node', 'cluster' (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'spare', 'zeroed', 'not-zeroed'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'spare', 'zeroed', 'not-zeroed'. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/fcportstatus.pm b/storage/netapp/restapi/mode/fcportstatus.pm new file mode 100644 index 000000000..02f6b8a20 --- /dev/null +++ b/storage/netapp/restapi/mode/fcportstatus.pm @@ -0,0 +1,191 @@ +# +# Copyright 2018 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::restapi::mode::fcportstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("Status is '%s', State is '%s' [adapter: %s] [switch port: %s] [fabric established: %s]", + $self->{result_values}->{status}, $self->{result_values}->{state}, + $self->{result_values}->{adapter}, $self->{result_values}->{switch_port}, + $self->{result_values}->{fabric_established}); + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_wwpn'}; + $self->{result_values}->{adapter} = $options{new_datas}->{$self->{instance} . '_adapter'}; + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; + $self->{result_values}->{state} = $options{new_datas}->{$self->{instance} . '_state'}; + $self->{result_values}->{switch_port} = $options{new_datas}->{$self->{instance} . '_switch_port'}; + $self->{result_values}->{fabric_established} = ($options{new_datas}->{$self->{instance} . '_fabric_established'}) ? "true" : "false"; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "FC port '" . $options{instance_value}->{wwpn} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'fcports', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All FC ports status are ok' }, + ]; + + $self->{maps_counters}->{fcports} = [ + { label => 'status', set => { + key_values => [ { name => 'wwpn' }, { name => 'status' }, { name => 'state' }, { name => 'switch_port' }, { name => 'fabric_established' }, { name => 'adapter' } ], + closure_custom_calc => $self->can('custom_status_calc'), + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status', default => '%{status} !~ /online/i || %{state} !~ /online/i' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/fc-ports'); + + foreach my $fcport (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $fcport->{wwpn} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $fcport->{wwpn} . "': no matching filter name.", debug => 1); + next; + } + + $self->{fcports}->{$fcport->{key}} = { + wwpn => $fcport->{wwpn}, + adapter => $fcport->{adapter}, + status => $fcport->{status}, + state => $fcport->{state}, + switch_port => $fcport->{switch_port}, + fabric_established => $fcport->{fabric_established}, + } + } + + if (scalar(keys %{$self->{fcports}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp FC ports status. + +=over 8 + +=item B<--filter-*> + +Filter qtree. +Can be: 'name', 'volume' (can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{state}, %{state} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} !~ /online/i || %{state} !~ /online/i'). +Can used special variables like: %{status}, %{state} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/listaggregates.pm b/storage/netapp/restapi/mode/listaggregates.pm new file mode 100644 index 000000000..23ed9882e --- /dev/null +++ b/storage/netapp/restapi/mode/listaggregates.pm @@ -0,0 +1,154 @@ +# +# Copyright 2018 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::restapi::mode::listaggregates; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "filter-node:s" => { name => 'filter_node' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + $self->{clusters} = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + + $self->{nodes} = $options{custom}->get_objects(path => '/nodes', key => 'key', name => 'name'); + + my $result = $options{custom}->get(path => '/aggregates'); + + foreach my $aggregate (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $aggregate->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter name.", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_node}) && $self->{option_results}->{filter_node} ne '' && + defined($self->{nodes}->{$aggregate->{node_key}}) && $self->{nodes}->{$aggregate->{node_key}} !~ /$self->{option_results}->{filter_node}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter node '" . $self->{nodes}->{$aggregate->{node_key}} . "'", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($self->{clusters}->{$aggregate->{cluster_key}}) && $self->{clusters}->{$aggregate->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $aggregate->{name} . "': no matching filter cluster '" . $self->{clusters}->{$aggregate->{cluster_key}} . "'", debug => 1); + next; + } + + $self->{aggregates}->{$aggregate->{key}} = { + name => $aggregate->{name}, + state => $aggregate->{state}, + mirror_status => $aggregate->{mirror_status}, + raid_type => $aggregate->{raid_type}, + aggregate_type => $aggregate->{aggregate_type}, + snaplock_type => $aggregate->{snaplock_type}, + cluster => $self->{clusters}->{$aggregate->{cluster_key}}, + node => $self->{nodes}->{$aggregate->{node_key}}, + } + } +} + +sub run { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $aggregate (sort keys %{$self->{aggregates}}) { + $self->{output}->output_add(long_msg => sprintf("[name = %s] [state = %s] [mirror_status = %s] [raid_type = %s] [aggregate_type = %s] [snaplock_type = %s] [cluster = %s] [node = %s]", + $self->{aggregates}->{$aggregate}->{name}, + $self->{aggregates}->{$aggregate}->{state}, + $self->{aggregates}->{$aggregate}->{mirror_status}, + $self->{aggregates}->{$aggregate}->{raid_type}, + $self->{aggregates}->{$aggregate}->{aggregate_type}, + $self->{aggregates}->{$aggregate}->{snaplock_type}, + $self->{aggregates}->{$aggregate}->{cluster}, + $self->{aggregates}->{$aggregate}->{node})); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List aggregates:'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'state', 'mirror_status', + 'raid_type', 'aggregate_type', 'snaplock_type']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $aggregate (sort keys %{$self->{aggregates}}) { + $self->{output}->add_disco_entry( + name => $self->{aggregates}->{$aggregate}->{name}, + state => $self->{aggregates}->{$aggregate}->{state}, + mirror_status => $self->{aggregates}->{$aggregate}->{mirror_status}, + raid_type => $self->{aggregates}->{$aggregate}->{raid_type}, + aggregate_type => $self->{aggregates}->{$aggregate}->{aggregate_type}, + snaplock_type => $self->{aggregates}->{$aggregate}->{snaplock_type}, + cluster => $self->{aggregates}->{$aggregate}->{cluster}, + node => $self->{aggregates}->{$aggregate}->{node}, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List aggregates. + +=over 8 + +=item B<--filter-*> + +Filter aggregates. +Can be: 'name', 'node', 'cluster' (can be a regexp). + +=back + +=cut diff --git a/storage/netapp/restapi/mode/listclusters.pm b/storage/netapp/restapi/mode/listclusters.pm new file mode 100644 index 000000000..411caa071 --- /dev/null +++ b/storage/netapp/restapi/mode/listclusters.pm @@ -0,0 +1,123 @@ +# +# Copyright 2018 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::restapi::mode::listclusters; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/clusters'); + + foreach my $cluster (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $cluster->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $cluster->{name} . "': no matching filter name.", debug => 1); + next; + } + + $self->{clusters}->{$cluster->{key}} = { + name => $cluster->{name}, + status => $cluster->{status}, + metro_cluster_mode => defined($cluster->{metro_cluster_mode}) ? $cluster->{metro_cluster_mode} : "-", + metro_cluster_configuration_state => defined($cluster->{metro_cluster_configuration_state}) ? $cluster->{metro_cluster_configuration_state} : "-", + } + } +} + +sub run { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $cluster (sort keys %{$self->{clusters}}) { + $self->{output}->output_add(long_msg => sprintf("[name = %s] [status = %s] [metro_cluster_mode = %s] [metro_cluster_configuration_state = %s]", + $self->{clusters}->{$cluster}->{name}, + $self->{clusters}->{$cluster}->{status}, + $self->{clusters}->{$cluster}->{metro_cluster_mode}, + $self->{clusters}->{$cluster}->{metro_cluster_configuration_state})); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List clusters:'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'status', 'metro_cluster_mode', + 'metro_cluster_configuration_state']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $cluster (sort keys %{$self->{clusters}}) { + $self->{output}->add_disco_entry( + name => $self->{clusters}->{$cluster}->{name}, + status => $self->{clusters}->{$cluster}->{status}, + metro_cluster_mode => $self->{clusters}->{$cluster}->{metro_cluster_mode}, + metro_cluster_configuration_state => $self->{clusters}->{$cluster}->{metro_cluster_configuration_state}, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List clusters. + +=over 8 + +=item B<--filter-name> + +Filter snapmirror name (can be a regexp). + +=back + +=cut diff --git a/storage/netapp/restapi/mode/listfcports.pm b/storage/netapp/restapi/mode/listfcports.pm new file mode 100644 index 000000000..8919dc09d --- /dev/null +++ b/storage/netapp/restapi/mode/listfcports.pm @@ -0,0 +1,125 @@ +# +# Copyright 2018 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::restapi::mode::listfcports; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/fc-ports'); + + foreach my $fcport (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $fcport->{wwpn} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $fcport->{name} . "': no matching filter name.", debug => 1); + next; + } + + $self->{fcports}->{$fcport->{key}} = { + wwpn => $fcport->{wwpn}, + adapter => $fcport->{adapter}, + status => $fcport->{status}, + state => $fcport->{state}, + switch_port => $fcport->{switch_port}, + } + } +} + +sub run { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $fcport (sort keys %{$self->{fcports}}) { + $self->{output}->output_add(long_msg => sprintf("[wwpn = %s] [adapter = %s] [status = %s] [state = %s] [switch_port = %s]", + $self->{fcports}->{$fcport}->{wwpn}, + $self->{fcports}->{$fcport}->{adapter}, + $self->{fcports}->{$fcport}->{status}, + $self->{fcports}->{$fcport}->{state}, + $self->{fcports}->{$fcport}->{switch_port})); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List FC ports:'); + $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 => ['wwpn', 'adapter', 'status', 'state', 'switch_port']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $fcport (sort keys %{$self->{fcports}}) { + $self->{output}->add_disco_entry( + wwpn => $self->{fcports}->{$fcport}->{wwpn}, + adapter => $self->{fcports}->{$fcport}->{adapter}, + status => $self->{fcports}->{$fcport}->{status}, + state => $self->{fcports}->{$fcport}->{state}, + switch_port => $self->{fcports}->{$fcport}->{switch_port}, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List FC ports. + +=over 8 + +=item B<--filter-name> + +Filter FC ports name (can be a regexp). + +=back + +=cut diff --git a/storage/netapp/restapi/mode/listluns.pm b/storage/netapp/restapi/mode/listluns.pm new file mode 100644 index 000000000..8e7ab58ea --- /dev/null +++ b/storage/netapp/restapi/mode/listluns.pm @@ -0,0 +1,129 @@ +# +# Copyright 2018 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::restapi::mode::listluns; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/luns'); + + foreach my $lun (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $lun->{path} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $lun->{path} . "': no matching filter name.", debug => 1); + next; + } + + $self->{luns}->{$lun->{key}} = { + path => $lun->{path}, + is_online => $lun->{is_online}, + is_mapped => $lun->{is_mapped}, + lun_class => $lun->{lun_class}, + alignment => $lun->{alignment}, + multi_protocol_type => $lun->{multi_protocol_type}, + size => $lun->{size}, + } + } +} + +sub run { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $lun (sort keys %{$self->{luns}}) { + $self->{output}->output_add(long_msg => sprintf("[path = %s] [is_online = %s] [is_mapped = %s] [lun_class = %s] [alignment = %s] [multi_protocol_type = %s] [size = %s]", + $self->{luns}->{$lun}->{path}, $self->{luns}->{$lun}->{is_online}, + $self->{luns}->{$lun}->{is_mapped}, $self->{luns}->{$lun}->{lun_class}, + $self->{luns}->{$lun}->{alignment}, $self->{luns}->{$lun}->{multi_protocol_type}, + $self->{luns}->{$lun}->{size})); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List luns:'); + $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 => ['path', 'is_online', 'is_mapped', 'lun_class', 'alignment', + 'multi_protocol_type', 'size']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $lun (sort keys %{$self->{luns}}) { + $self->{output}->add_disco_entry( + path => $self->{luns}->{$lun}->{path}, + is_online => $self->{luns}->{$lun}->{is_online}, + is_mapped => $self->{luns}->{$lun}->{is_mapped}, + lun_class => $self->{luns}->{$lun}->{lun_class}, + alignment => $self->{luns}->{$lun}->{alignment}, + multi_protocol_type => $self->{luns}->{$lun}->{multi_protocol_type}, + size => $self->{luns}->{$lun}->{size}, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List LUNs. + +=over 8 + +=item B<--filter-name> + +Filter lun name (can be a regexp). + +=back + +=cut diff --git a/storage/netapp/restapi/mode/listnodes.pm b/storage/netapp/restapi/mode/listnodes.pm new file mode 100644 index 000000000..4952e9e22 --- /dev/null +++ b/storage/netapp/restapi/mode/listnodes.pm @@ -0,0 +1,123 @@ +# +# Copyright 2018 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::restapi::mode::listnodes; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/nodes'); + + foreach my $node (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $node->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $node->{name} . "': no matching filter name.", debug => 1); + next; + } + + $self->{nodes}->{$node->{key}} = { + name => $node->{name}, + is_node_healthy => $node->{is_node_healthy}, + current_mode => $node->{current_mode}, + failover_state => $node->{failover_state}, + is_failover_enabled => $node->{is_failover_enabled}, + } + } +} + +sub run { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $node (sort keys %{$self->{nodes}}) { + $self->{output}->output_add(long_msg => sprintf("[name = %s] [status = %s] [current_mode = %s] [failover_state = %s] [failover = %s]", + $self->{nodes}->{$node}->{name}, ($self->{nodes}->{$node}->{is_node_healthy}) ? "healthy" : "not healthy", + $self->{nodes}->{$node}->{current_mode}, $self->{nodes}->{$node}->{failover_state}, + ($self->{nodes}->{$node}->{is_failover_enabled}) ? "enabled" : "disabled")); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List nodes:'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'status', 'current_mode', 'failover_state', 'failover']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $node (sort keys %{$self->{nodes}}) { + $self->{output}->add_disco_entry( + name => $self->{nodes}->{$node}->{name}, + status => ($self->{nodes}->{$node}->{is_node_healthy}) ? "healthy" : "not healthy", + current_mode => $self->{nodes}->{$node}->{current_mode}, + failover_state => $self->{nodes}->{$node}->{failover_state}, + failover => ($self->{nodes}->{$node}->{is_failover_enabled}) ? "enabled" : "disabled", + ); + } +} + +1; + +__END__ + +=head1 MODE + +List nodes. + +=over 8 + +=item B<--filter-name> + +Filter node name (can be a regexp). + +=back + +=cut diff --git a/storage/netapp/restapi/mode/listsnapmirrors.pm b/storage/netapp/restapi/mode/listsnapmirrors.pm new file mode 100644 index 000000000..546525349 --- /dev/null +++ b/storage/netapp/restapi/mode/listsnapmirrors.pm @@ -0,0 +1,123 @@ +# +# Copyright 2018 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::restapi::mode::listsnapmirrors; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/snap-mirrors'); + + foreach my $snapmirror (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $snapmirror->{source_location} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $snapmirror->{name} . "': no matching filter name.", debug => 1); + next; + } + + $self->{snapmirrors}->{$snapmirror->{key}} = { + source_location => $snapmirror->{source_location}, + destination_location => $snapmirror->{destination_location}, + mirror_state => $snapmirror->{mirror_state}, + is_healthy => $snapmirror->{is_healthy}, + } + } +} + +sub run { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $snapmirror (sort keys %{$self->{snapmirrors}}) { + $self->{output}->output_add(long_msg => sprintf("[source_location = %s] [destination_location = %s] [mirror_state = %s] [is_healthy = %s]", + $self->{snapmirrors}->{$snapmirror}->{source_location}, + $self->{snapmirrors}->{$snapmirror}->{destination_location}, + $self->{snapmirrors}->{$snapmirror}->{mirror_state}, + $self->{snapmirrors}->{$snapmirror}->{is_healthy})); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List snap mirrors:'); + $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 => ['source_location', 'destination_location', 'mirror_state', + 'is_healthy']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $snapmirror (sort keys %{$self->{snapmirrors}}) { + $self->{output}->add_disco_entry( + source_location => $self->{snapmirrors}->{$snapmirror}->{source_location}, + destination_location => $self->{snapmirrors}->{$snapmirror}->{destination_location}, + mirror_state => $self->{snapmirrors}->{$snapmirror}->{mirror_state}, + is_healthy => $self->{snapmirrors}->{$snapmirror}->{is_healthy}, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List snap mirrors. + +=over 8 + +=item B<--filter-name> + +Filter snapmirror name (can be a regexp). + +=back + +=cut diff --git a/storage/netapp/restapi/mode/listvolumes.pm b/storage/netapp/restapi/mode/listvolumes.pm new file mode 100644 index 000000000..9383d47d3 --- /dev/null +++ b/storage/netapp/restapi/mode/listvolumes.pm @@ -0,0 +1,126 @@ +# +# Copyright 2018 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::restapi::mode::listvolumes; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +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}, + vol_type => $volume->{vol_type}, + style => $volume->{style}, + is_replica_volume => $volume->{is_replica_volume}, + size_total => $volume->{size_total}, + } + } +} + +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})); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List volumes:'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', '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}, + ); + } +} + +1; + +__END__ + +=head1 MODE + +List volumes. + +=over 8 + +=item B<--filter-name> + +Filter volume name (can be a regexp). + +=back + +=cut diff --git a/storage/netapp/restapi/mode/lunalignment.pm b/storage/netapp/restapi/mode/lunalignment.pm new file mode 100644 index 000000000..7622cd147 --- /dev/null +++ b/storage/netapp/restapi/mode/lunalignment.pm @@ -0,0 +1,170 @@ +# +# Copyright 2018 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::restapi::mode::lunalignment; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'luns', type => 0}, + ]; + + $self->{maps_counters}->{luns} = [ + { label => 'aligned', set => { + key_values => [ { name => 'aligned' } ], + output_template => 'Luns aligned: %d', + perfdatas => [ + { label => 'aligned', value => 'aligned_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'misaligned', set => { + key_values => [ { name => 'misaligned' } ], + output_template => 'Luns misaligned: %d', + perfdatas => [ + { label => 'misaligned', value => 'misaligned_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'possibly-misaligned', set => { + key_values => [ { name => 'possibly_misaligned' } ], + output_template => 'Luns possibly misaligned: %d', + perfdatas => [ + { label => 'possibly_misaligned', value => 'possibly_misaligned_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'indeterminate', set => { + key_values => [ { name => 'indeterminate' } ], + output_template => 'Luns indeterminate: %d', + perfdatas => [ + { label => 'indeterminate', value => 'indeterminate_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'partial-writes', set => { + key_values => [ { name => 'partial_writes' } ], + output_template => 'Luns partial writes: %d', + perfdatas => [ + { label => 'partial_writes', value => 'partial_writes_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'not-mapped', set => { + key_values => [ { name => 'not_mapped' } ], + output_template => 'Luns not mapped: %d', + perfdatas => [ + { label => 'not_mapped', value => 'not_mapped_absolute', template => '%d', + min => 0 }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-volume:s" => { name => 'filter_volume' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/luns'); + + $self->{luns}->{aligned} = 0; + $self->{luns}->{misaligned} = 0; + $self->{luns}->{possibly_misaligned} = 0; + $self->{luns}->{indeterminate} = 0; + $self->{luns}->{partial_writes} = 0; + $self->{luns}->{not_mapped} = 0; + + foreach my $lun (@{$result}) { + my $volume = $2 if ($lun->{path} =~ /^\/(\S+)\/(\S+)\/(\S+)\s*/); + + if (defined($self->{option_results}->{filter_volume}) && $self->{option_results}->{filter_volume} ne '' && + defined($volume) && $volume !~ /$self->{option_results}->{filter_volume}/) { + $self->{output}->output_add(long_msg => "skipping '" . $lun->{path} . "': no matching filter volume '" . $volume . "'", debug => 1); + next; + } + + $self->{luns}->{$lun->{alignment}}++; + + if ($lun->{alignment} ne "aligned" && $lun->{alignment} ne "indeterminate") { + my $long_msg = "Lun '" . $lun->{path} . "' is '" . $lun->{alignment} . "'"; + $long_msg .= " [volume: " . $volume . "]" if (defined($self->{option_results}->{filter_volume}) && $self->{option_results}->{filter_volume} ne ''); + $self->{output}->output_add(long_msg => $long_msg); + } + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp luns alignment. + +=over 8 + +=item B<--filter-*> + +Filter lun. +Can be: 'volume' (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +'aligned', 'misaligned', 'possibly-misaligned', 'indeterminate', 'partial-writes', 'not-mapped'. + +=item B<--critical-*> + +Threshold critical. +'aligned', 'misaligned', 'possibly-misaligned', 'indeterminate', 'partial-writes', 'not-mapped'. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/lunonline.pm b/storage/netapp/restapi/mode/lunonline.pm new file mode 100644 index 000000000..eaec67745 --- /dev/null +++ b/storage/netapp/restapi/mode/lunonline.pm @@ -0,0 +1,159 @@ +# +# Copyright 2018 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::restapi::mode::lunonline; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'luns', type => 0}, + ]; + + $self->{maps_counters}->{luns} = [ + { label => 'online', set => { + key_values => [ { name => 'online' } ], + output_template => 'Luns online: %d', + perfdatas => [ + { label => 'online', value => 'online_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'not-online', set => { + key_values => [ { name => 'not_online' } ], + output_template => 'Luns not online: %d', + perfdatas => [ + { label => 'not_online', value => 'not_online_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'mapped', set => { + key_values => [ { name => 'mapped' } ], + output_template => 'Luns mapped: %d', + perfdatas => [ + { label => 'mapped', value => 'mapped_absolute', template => '%d', + min => 0 }, + ], + } + }, + { label => 'not-mapped', set => { + key_values => [ { name => 'not_mapped' } ], + output_template => 'Luns not mapped: %d', + perfdatas => [ + { label => 'not_mapped', value => 'not_mapped_absolute', template => '%d', + min => 0 }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-volume:s" => { name => 'filter_volume' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/luns'); + + $self->{luns}->{online} = 0; + $self->{luns}->{not_online} = 0; + $self->{luns}->{mapped} = 0; + $self->{luns}->{not_mapped} = 0; + + foreach my $lun (@{$result}) { + my $volume = $2 if ($lun->{path} =~ /^\/(\S+)\/(\S+)\/(\S+)\s*/); + + if (defined($self->{option_results}->{filter_volume}) && $self->{option_results}->{filter_volume} ne '' && + defined($volume) && $volume !~ /$self->{option_results}->{filter_volume}/) { + $self->{output}->output_add(long_msg => "skipping '" . $lun->{path} . "': no matching filter volume '" . $volume . "'", debug => 1); + next; + } + + $self->{luns}->{online}++ if ($lun->{is_online}); + $self->{luns}->{mapped}++ if ($lun->{is_mapped}); + if (!$lun->{is_online}) { + $self->{luns}->{not_online}++; + + my $long_msg = "Lun '" . $lun->{path} . "' is 'not online'"; + $long_msg .= " [volume: " . $volume . "]" if (defined($self->{option_results}->{filter_volume}) && $self->{option_results}->{filter_volume} ne ''); + $self->{output}->output_add(long_msg => $long_msg); + } + if (!$lun->{is_mapped}) { + $self->{luns}->{not_mapped}++; + + my $long_msg = "Lun '" . $lun->{path} . "' is 'not mapped'"; + $long_msg .= " [volume: " . $volume . "]" if (defined($self->{option_results}->{filter_volume}) && $self->{option_results}->{filter_volume} ne ''); + $self->{output}->output_add(long_msg => $long_msg); + } + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp luns status. + +=over 8 + +=item B<--filter-*> + +Filter lun. +Can be: 'volume' (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'online', 'not-online', 'mapped', 'not-mapped'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'online', 'not-online', 'mapped', 'not-mapped'. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/lunusage.pm b/storage/netapp/restapi/mode/lunusage.pm new file mode 100644 index 000000000..9744b8099 --- /dev/null +++ b/storage/netapp/restapi/mode/lunusage.pm @@ -0,0 +1,199 @@ +# +# Copyright 2018 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::restapi::mode::lunusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_usage_perfdata { + my ($self, %options) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'used' . $extra_label, + unit => 'B', + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}, 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($instance_mode->{option_results}->{free})); + if ($instance_mode->{option_results}->{units} eq '%') { + $threshold_value = $self->{result_values}->{prct_used}; + $threshold_value = $self->{result_values}->{prct_free} if (defined($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_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} . '_path'}; + $self->{result_values}->{total} = $options{new_datas}->{$self->{instance} . '_size'}; + $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 prefix_output { + my ($self, %options) = @_; + + return "Lun '" . $options{instance_value}->{path} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'luns', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All luns usage are ok' }, + ]; + + $self->{maps_counters}->{luns} = [ + { label => 'usage', set => { + key_values => [ { name => 'size_used' }, { name => 'size' }, { name => 'path' } ], + 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'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "units:s" => { name => 'units', default => '%' }, + "free" => { name => 'free' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/luns'); + + foreach my $lun (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $lun->{path} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $lun->{path} . "': no matching filter name.", debug => 1); + next; + } + + $self->{luns}->{$lun->{key}} = { + path => $lun->{path}, + size => $lun->{size}, + size_used => $lun->{size_used}, + } + } + + if (scalar(keys %{$self->{luns}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp luns usage. + +=over 8 + +=item B<--filter-name> + +Filter lun name (can be a regexp). + +=item B<--warning-usage> + +Threshold warning. + +=item B<--critical-usage> + +Threshold critical. + +=item B<--units> + +Units of thresholds (Default: '%') ('%', 'B'). + +=item B<--free> + +Thresholds are on free space left. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/nodefailoverstatus.pm b/storage/netapp/restapi/mode/nodefailoverstatus.pm new file mode 100644 index 000000000..7b0f4d1c4 --- /dev/null +++ b/storage/netapp/restapi/mode/nodefailoverstatus.pm @@ -0,0 +1,204 @@ +# +# Copyright 2018 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::restapi::mode::nodefailoverstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("Failover state is '%s', Interconnect is '%s' [current mode: %s] [take_over_possible: %s]", + $self->{result_values}->{state}, + $self->{result_values}->{interconnect}, + $self->{result_values}->{current_mode}, + $self->{result_values}->{take_over_possible}); + 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} . '_failover_state'}; + $self->{result_values}->{interconnect} = ($options{new_datas}->{$self->{instance} . '_is_interconnect_up'}) ? "up" : "down"; + $self->{result_values}->{current_mode} = $options{new_datas}->{$self->{instance} . '_current_mode'}; + $self->{result_values}->{take_over_possible} = ($options{new_datas}->{$self->{instance} . '_is_take_over_possible'}) ? "true" : "false"; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Node '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'nodes', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All nodes status are ok' }, + ]; + + $self->{maps_counters}->{nodes} = [ + { label => 'status', set => { + key_values => [ { name => 'failover_state' }, { name => 'current_mode' }, { name => 'is_interconnect_up' }, + { name => 'is_take_over_possible' }, { 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 => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status', default => '%{state} !~ /connected/i || %{interconnect} !~ /up/i'}, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $clusters; + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '') { + $clusters = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + } + + my $result = $options{custom}->get(path => '/nodes'); + + foreach my $node (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $node->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $node->{name} . "': no matching filter name.", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($clusters->{$node->{cluster_key}}) && $clusters->{$node->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $node->{name} . "': no matching filter cluster '" . $clusters->{$node->{cluster_key}} . "'", debug => 1); + next; + } + + $self->{nodes}->{$node->{key}} = { + name => $node->{name}, + failover_state => $node->{failover_state}, + current_mode => $node->{current_mode}, + is_interconnect_up => $node->{is_interconnect_up}, + is_take_over_possible => $node->{is_take_over_possible}, + } + } + + if (scalar(keys %{$self->{nodes}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp nodes status. + +=over 8 + +=item B<--filter-*> + +Filter node. +Can be: 'name', 'clusters' (can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{state}, %{interconnect}, %{current_mode}, %{take_over_possible} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{state} !~ /connected/i || %{interconnect} !~ /up/i'). +Can used special variables like: %{state}, %{interconnect}, %{current_mode}, %{take_over_possible} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/nodehardwarestatus.pm b/storage/netapp/restapi/mode/nodehardwarestatus.pm new file mode 100644 index 000000000..24cb13fb1 --- /dev/null +++ b/storage/netapp/restapi/mode/nodehardwarestatus.pm @@ -0,0 +1,233 @@ +# +# Copyright 2018 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::restapi::mode::nodehardwarestatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("Status is '%s', Battery status is '%s', Temperature is '%s'", + $self->{result_values}->{status}, + $self->{result_values}->{battery_status}, + $self->{result_values}->{temperature}); + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; + $self->{result_values}->{status} = ($options{new_datas}->{$self->{instance} . '_is_node_healthy'}) ? "healthy" : "not healthy"; + $self->{result_values}->{temperature} = ($options{new_datas}->{$self->{instance} . '_is_over_temperature'}) ? "critical" : "ok"; + $self->{result_values}->{battery_status} = $options{new_datas}->{$self->{instance} . '_nvram_battery_status'}; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Node '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'nodes', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All nodes status are ok' }, + ]; + + $self->{maps_counters}->{nodes} = [ + { label => 'status', set => { + key_values => [ { name => 'is_node_healthy' }, { name => 'is_over_temperature' }, + { name => 'nvram_battery_status' }, { 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 => $self->can('custom_status_threshold'), + } + }, + { label => 'failed-fans', set => { + key_values => [ { name => 'failed_fan_count' }, { name => 'name' } ], + output_template => '%d failed fan(s)', + perfdatas => [ + { label => 'failed_fans', value => 'failed_fan_count_absolute', template => '%d', + min => 0, label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { label => 'failed-psu', set => { + key_values => [ { name => 'failed_power_supply_count' }, { name => 'name' } ], + output_template => '%d failed psu', + perfdatas => [ + { label => 'failed_psu', value => 'failed_power_supply_count_absolute', template => '%d', + min => 0, label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "filter-cluster:s" => { name => 'filter_cluster' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status', default => '%{status} =~ /not healthy/i || ' . + '%{temperature} !~ /ok/i || %{battery_status} !~ /battery_ok|battery_fully_charge|battery_over_charged/i' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $clusters; + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '') { + $clusters = $options{custom}->get_objects(path => '/clusters', key => 'key', name => 'name'); + } + + my $result = $options{custom}->get(path => '/nodes'); + + foreach my $node (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $node->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $node->{name} . "': no matching filter name.", debug => 1); + next; + } + + if (defined($self->{option_results}->{filter_cluster}) && $self->{option_results}->{filter_cluster} ne '' && + defined($clusters->{$node->{cluster_key}}) && $clusters->{$node->{cluster_key}} !~ /$self->{option_results}->{filter_cluster}/) { + $self->{output}->output_add(long_msg => "skipping '" . $node->{name} . "': no matching filter cluster '" . $clusters->{$node->{cluster_key}} . "'", debug => 1); + next; + } + + $self->{nodes}->{$node->{key}} = { + name => $node->{name}, + is_node_healthy => $node->{is_node_healthy}, + is_over_temperature => $node->{is_over_temperature}, + nvram_battery_status => $node->{nvram_battery_status}, + failed_fan_count => $node->{failed_fan_count}, + failed_power_supply_count => $node->{failed_power_supply_count}, + } + } + + if (scalar(keys %{$self->{nodes}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp nodes hardware status. + +=over 8 + +=item B<--filter-*> + +Filter node. +Can be: 'name', 'clusters' (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'failed-fans', 'psu'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'failed-fans', 'psu'. + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{status}, %{temperature}, %{battery_status} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{status} =~ /not healthy/i || %{temperature} !~ /ok/i || +%{battery_status} !~ /battery_ok|battery_fully_charge|battery_over_charged/i'). +Can used special variables like: %{status}, %{temperature}, %{battery_status} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/qtreestatus.pm b/storage/netapp/restapi/mode/qtreestatus.pm new file mode 100644 index 000000000..f52dc0049 --- /dev/null +++ b/storage/netapp/restapi/mode/qtreestatus.pm @@ -0,0 +1,196 @@ +# +# Copyright 2018 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::restapi::mode::qtreestatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("Status is '%s' [path: %s] [volume: %s]", + $self->{result_values}->{status}, + $self->{result_values}->{qtree_path}, + $self->{result_values}->{volume}); + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{display} = $options{new_datas}->{$self->{instance} . '_name'}; + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; + $self->{result_values}->{qtree_path} = $options{new_datas}->{$self->{instance} . '_qtree_path'}; + $self->{result_values}->{volume} = $options{new_datas}->{$self->{instance} . '_volume'}; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Qtree '" . $options{instance_value}->{name} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'qtrees', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All Qtrees status are ok' }, + ]; + + $self->{maps_counters}->{qtrees} = [ + { label => 'status', set => { + key_values => [ { name => 'status' }, { name => 'qtree_path' }, { name => 'volume' }, { 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 => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "filter-volume:s" => { name => 'filter_volume' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/qtrees'); + + foreach my $qtree (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $qtree->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $qtree->{name} . "': no matching filter name.", debug => 1); + next; + } + + my $volume = $2 if ($qtree->{qtree_path} =~ /^\/(\S+)\/(\S+)\/(\S+)\s*/); + + if (defined($self->{option_results}->{filter_volume}) && $self->{option_results}->{filter_volume} ne '' && + defined($volume) && $volume !~ /$self->{option_results}->{filter_volume}/) { + $self->{output}->output_add(long_msg => "skipping '" . $qtree->{name} . "': no matching filter volume '" . $volume . "'", debug => 1); + next; + } + + $self->{qtrees}->{$qtree->{key}} = { + name => $qtree->{name}, + status => $qtree->{status}, + qtree_path => $qtree->{qtree_path}, + volume => $volume, + } + } + + if (scalar(keys %{$self->{qtrees}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp Qtrees status. + +=over 8 + +=item B<--filter-*> + +Filter qtree. +Can be: 'name', 'volume' (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: ''). +Can used special variables like: %{status} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/snapmirrorstatus.pm b/storage/netapp/restapi/mode/snapmirrorstatus.pm new file mode 100644 index 000000000..35b89ab13 --- /dev/null +++ b/storage/netapp/restapi/mode/snapmirrorstatus.pm @@ -0,0 +1,181 @@ +# +# Copyright 2018 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::restapi::mode::snapmirrorstatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = sprintf("State is '%s', Update is '%s'", $self->{result_values}->{state}, $self->{result_values}->{update}); + return $msg; +} + +sub custom_status_calc { + my ($self, %options) = @_; + + $self->{result_values}->{source_location} = $options{new_datas}->{$self->{instance} . '_source_location'}; + $self->{result_values}->{state} = $options{new_datas}->{$self->{instance} . '_mirror_state'}; + $self->{result_values}->{update} = $options{new_datas}->{$self->{instance} . '_is_healthy'} ? "healthy" : "not healthy"; + + return 0; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Snap mirror '" . $options{instance_value}->{source_location} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'snapmirrors', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All snap mirrors status are ok' }, + ]; + + $self->{maps_counters}->{snapmirrors} = [ + { label => 'status', set => { + key_values => [ { name => 'source_location' }, { name => 'mirror_state' }, { name => 'is_healthy' } ], + closure_custom_calc => $self->can('custom_status_calc'), + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + "warning-status:s" => { name => 'warning_status' }, + "critical-status:s" => { name => 'critical_status', default => '%{state} !~ /snapmirrored/i || %{update} =~ /not healthy/i' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/snap-mirrors'); + + foreach my $snapmirror (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $snapmirror->{source_location} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $snapmirror->{name} . "': no matching filter name.", debug => 1); + next; + } + + $self->{snapmirrors}->{$snapmirror->{key}} = { + source_location => $snapmirror->{source_location}, + mirror_state => $snapmirror->{mirror_state}, + is_healthy => $snapmirror->{is_healthy}, + } + } + + if (scalar(keys %{$self->{snapmirrors}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp snap mirrors status. + +=over 8 + +=item B<--filter-name> + +Filter snapmirror name (can be a regexp). + +=item B<--warning-status> + +Set warning threshold for status (Default: ''). +Can used special variables like: %{state}, %{update} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{state} !~ /snapmirrored/i || %{update} =~ /not healthy/i'). +Can used special variables like: %{state}, %{update} + +=back + +=cut diff --git a/storage/netapp/restapi/mode/snapmirrorusage.pm b/storage/netapp/restapi/mode/snapmirrorusage.pm new file mode 100644 index 000000000..aa49bbb5d --- /dev/null +++ b/storage/netapp/restapi/mode/snapmirrorusage.pm @@ -0,0 +1,144 @@ +# +# Copyright 2018 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::restapi::mode::snapmirrorusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub prefix_output { + my ($self, %options) = @_; + + return "Snap mirror '" . $options{instance_value}->{source_location} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'snapmirrors', type => 1, cb_prefix_output => 'prefix_output', message_multiple => 'All snap mirrors usage are ok' }, + ]; + + $self->{maps_counters}->{snapmirrors} = [ + { label => 'last-transfer-duration', set => { + key_values => [ { name => 'last_transfer_duration' }, { name => 'source_location' } ], + output_template => 'Last transfer duration: %.2f s', + perfdatas => [ + { label => 'last_transfer_duration', value => 'last_transfer_duration_absolute', template => '%.2f', + min => 0, unit => 's', label_extra_instance => 1, instance_use => 'source_location_absolute' }, + ], + } + }, + { label => 'last-transfer-size', set => { + key_values => [ { name => 'last_transfer_size' }, { name => 'source_location' } ], + output_template => 'Last transfer size: %s %s', + output_change_bytes => 1, + perfdatas => [ + { label => 'last_transfer_size', value => 'last_transfer_size_absolute', template => '%d', + min => 0, unit => 'B', label_extra_instance => 1, instance_use => 'source_location_absolute' }, + ], + } + }, + { label => 'lag-time', set => { + key_values => [ { name => 'lag_time' }, { name => 'source_location' } ], + output_template => 'Lag time: %.2f s', + perfdatas => [ + { label => 'lag_time', value => 'lag_time_absolute', template => '%.2f', + min => 0, unit => 's', label_extra_instance => 1, instance_use => 'source_location_absolute' }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "filter-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->get(path => '/snap-mirrors'); + + foreach my $snapmirror (@{$result}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $snapmirror->{source_location} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $snapmirror->{name} . "': no matching filter name.", debug => 1); + next; + } + + $self->{snapmirrors}->{$snapmirror->{key}} = { + source_location => $snapmirror->{source_location}, + last_transfer_duration => $snapmirror->{last_transfer_duration}, + last_transfer_size => $snapmirror->{last_transfer_size}, + lag_time => $snapmirror->{lag_time}, + } + } + + if (scalar(keys %{$self->{snapmirrors}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No entry found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check NetApp snap mirrors usage. + +=over 8 + +=item B<--filter-name> + +Filter snapmirror name (can be a regexp). + +=item B<--warning-*> + +Threshold warning. +Can be: 'last-transfer-duration', 'last-transfer-size', 'lag-time'. + +=item B<--critical-*> + +Threshold critical. +Can be: 'last-transfer-duration', 'last-transfer-size', 'lag-time'. + +=back + +=cut diff --git a/storage/netapp/restapi/mode/volumeio.pm b/storage/netapp/restapi/mode/volumeio.pm new file mode 100644 index 000000000..ef6cbb73c --- /dev/null +++ b/storage/netapp/restapi/mode/volumeio.pm @@ -0,0 +1,225 @@ +# +# Copyright 2018 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::restapi::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_absolute', template => '%.2f', + min => 0, unit => 'ops/s', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { 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_absolute', template => '%.2f', + min => 0, unit => 'ops/s', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { 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_absolute', template => '%.2f', + min => 0, unit => 'ops/s', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { label => 'avg-latency', set => { + key_values => [ { name => 'avg_latency' }, { name => 'name' } ], + output_template => 'Average latency: %.2f ms', + perfdatas => [ + { label => 'avg_latency', value => 'avg_latency_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { label => 'read-latency', set => { + key_values => [ { name => 'read_latency' }, { name => 'name' } ], + output_template => 'Read latency: %.2f ms', + perfdatas => [ + { label => 'read_latency', value => 'read_latency_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { label => 'write-latency', set => { + key_values => [ { name => 'write_latency' }, { name => 'name' } ], + output_template => 'Write latency: %.2f ms', + perfdatas => [ + { label => 'write_latency', value => 'write_latency_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + { label => 'other-latency', set => { + key_values => [ { name => 'other_latency' }, { name => 'name' } ], + output_template => 'Other latency: %.2f ms', + perfdatas => [ + { label => 'other_latency', value => 'other_latency_absolute', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'name_absolute' }, + ], + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "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/restapi/mode/volumestatus.pm b/storage/netapp/restapi/mode/volumestatus.pm new file mode 100644 index 000000000..f00b423b5 --- /dev/null +++ b/storage/netapp/restapi/mode/volumestatus.pm @@ -0,0 +1,180 @@ +# +# Copyright 2018 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::restapi::mode::volumestatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_status_threshold { + my ($self, %options) = @_; + my $status = 'ok'; + my $message; + + eval { + local $SIG{__WARN__} = sub { $message = $_[0]; }; + local $SIG{__DIE__} = sub { $message = $_[0]; }; + + if (defined($instance_mode->{option_results}->{critical_status}) && $instance_mode->{option_results}->{critical_status} ne '' && + eval "$instance_mode->{option_results}->{critical_status}") { + $status = 'critical'; + } elsif (defined($instance_mode->{option_results}->{warning_status}) && $instance_mode->{option_results}->{warning_status} ne '' && + eval "$instance_mode->{option_results}->{warning_status}") { + $status = 'warning'; + } + }; + if (defined($message)) { + $self->{output}->output_add(long_msg => 'filter status issue: ' . $message); + } + + return $status; +} + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = 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 => $self->can('custom_status_threshold'), + } + }, + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + $options{options}->add_options(arguments => + { + "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 change_macros { + my ($self, %options) = @_; + + foreach ('warning_status', 'critical_status') { + if (defined($self->{option_results}->{$_})) { + $self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$self->{result_values}->{$1}/g; + } + } +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $self; + $self->change_macros(); +} + +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/restapi/mode/volumeusage.pm b/storage/netapp/restapi/mode/volumeusage.pm new file mode 100644 index 000000000..fb3d74d00 --- /dev/null +++ b/storage/netapp/restapi/mode/volumeusage.pm @@ -0,0 +1,487 @@ +# +# Copyright 2018 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::restapi::mode::volumeusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my $instance_mode; + +sub custom_usage_perfdata { + my ($self, %options) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'used' . $extra_label, + unit => 'B', + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}, 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($instance_mode->{option_results}->{free})); + if ($instance_mode->{option_results}->{units} eq '%') { + $threshold_value = $self->{result_values}->{prct_used}; + $threshold_value = $self->{result_values}->{prct_free} if (defined($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_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) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'inodes' . $extra_label, + unit => '%', + value => sprintf("%.2f", $self->{result_values}->{prct_used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}), + 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->{label}, exit_litteral => 'critical' }, + { label => 'warning-' . $self->{label}, 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) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'snapshot' . $extra_label, + unit => 'B', + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}, 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($instance_mode->{option_results}->{free})); + if ($instance_mode->{option_results}->{units} eq '%') { + $threshold_value = $self->{result_values}->{prct_used}; + $threshold_value = $self->{result_values}->{prct_free} if (defined($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_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) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'compresssaved' . $extra_label, + unit => 'B', + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}, 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($instance_mode->{option_results}->{free})); + if ($instance_mode->{option_results}->{units} eq '%') { + $threshold_value = $self->{result_values}->{prct_used}; + $threshold_value = $self->{result_values}->{prct_free} if (defined($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) = @_; + + my $extra_label = ''; + $extra_label = '_' . $self->{result_values}->{display} if (!defined($options{extra_instance}) || $options{extra_instance} != 0); + + $self->{output}->perfdata_add(label => 'dedupsaved' . $extra_label, + unit => 'B', + value => $self->{result_values}->{used}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{label}, total => $self->{result_values}->{total}, cast_int => 1), + min => 0, max => $self->{result_values}->{total}); +} + +sub custom_deduplicationn_threshold { + my ($self, %options) = @_; + + my ($exit, $threshold_value); + $threshold_value = $self->{result_values}->{used}; + $threshold_value = $self->{result_values}->{free} if (defined($instance_mode->{option_results}->{free})); + if ($instance_mode->{option_results}->{units} eq '%') { + $threshold_value = $self->{result_values}->{prct_used}; + $threshold_value = $self->{result_values}->{prct_free} if (defined($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_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; + + $self->{version} = '1.0'; + $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 check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $instance_mode = $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/restapi/plugin.pm b/storage/netapp/restapi/plugin.pm new file mode 100644 index 000000000..c0d5e8b34 --- /dev/null +++ b/storage/netapp/restapi/plugin.pm @@ -0,0 +1,74 @@ +# +# Copyright 2018 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::restapi::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_custom); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '1.0'; + %{$self->{modes}} = ( + 'aggregate-raid-status' => 'storage::netapp::restapi::mode::aggregateraidstatus', + 'aggregate-status' => 'storage::netapp::restapi::mode::aggregatestatus', + 'aggregate-usage' => 'storage::netapp::restapi::mode::aggregateusage', + 'cluster-io' => 'storage::netapp::restapi::mode::clusterio', + 'cluster-status' => 'storage::netapp::restapi::mode::clusterstatus', + 'cluster-usage' => 'storage::netapp::restapi::mode::clusterusage', + 'disk-failed' => 'storage::netapp::restapi::mode::diskfailed', + 'disk-spare' => 'storage::netapp::restapi::mode::diskspare', + 'fc-port-status' => 'storage::netapp::restapi::mode::fcportstatus', + 'list-aggregates' => 'storage::netapp::restapi::mode::listaggregates', + 'list-clusters' => 'storage::netapp::restapi::mode::listclusters', + 'list-fc-ports' => 'storage::netapp::restapi::mode::listfcports', + 'list-luns' => 'storage::netapp::restapi::mode::listluns', + 'list-nodes' => 'storage::netapp::restapi::mode::listnodes', + 'list-snapmirrors' => 'storage::netapp::restapi::mode::listsnapmirrors', + 'list-volumes' => 'storage::netapp::restapi::mode::listvolumes', + 'lun-alignment' => 'storage::netapp::restapi::mode::lunalignment', + 'lun-online' => 'storage::netapp::restapi::mode::lunonline', + 'lun-usage' => 'storage::netapp::restapi::mode::lunusage', + 'node-failover-status' => 'storage::netapp::restapi::mode::nodefailoverstatus', + 'node-hardware-status' => 'storage::netapp::restapi::mode::nodehardwarestatus', + 'qtree-status' => 'storage::netapp::restapi::mode::qtreestatus', + 'snapmirror-status' => 'storage::netapp::restapi::mode::snapmirrorstatus', + 'snapmirror-usage' => 'storage::netapp::restapi::mode::snapmirrorusage', + 'volume-io' => 'storage::netapp::restapi::mode::volumeio', + 'volume-status' => 'storage::netapp::restapi::mode::volumestatus', + 'volume-usage' => 'storage::netapp::restapi::mode::volumeusage', + ); + $self->{custom_modes}{api} = 'storage::netapp::restapi::custom::restapi'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check NetApp with OnCommand API. + +=cut