From 4565304d18209ded4b4f0893838e2155f8ecb3e4 Mon Sep 17 00:00:00 2001 From: scresto Date: Fri, 4 Jul 2025 17:50:12 +0200 Subject: [PATCH] CTOR-880: New Pack(storage::hp::alletra::restapi) (#5653) Co-authored-by: Roman Morandell <46994680+rmorandell-pgum@users.noreply.github.com> --- .../deb.json | 4 + .../pkg.json | 9 + .../rpm.json | 4 + src/storage/hp/alletra/restapi/custom/api.pm | 267 ++++++++++ .../hp/alletra/restapi/mode/capacity.pm | 494 ++++++++++++++++++ .../hp/alletra/restapi/mode/diskstatus.pm | 342 ++++++++++++ .../hp/alletra/restapi/mode/diskusage.pm | 360 +++++++++++++ .../hp/alletra/restapi/mode/licenses.pm | 240 +++++++++ .../hp/alletra/restapi/mode/listdisks.pm | 188 +++++++ .../hp/alletra/restapi/mode/listvolumes.pm | 148 ++++++ .../hp/alletra/restapi/mode/volumestatus.pm | 304 +++++++++++ .../hp/alletra/restapi/mode/volumeusage.pm | 282 ++++++++++ src/storage/hp/alletra/restapi/plugin.pm | 55 ++ tests/resources/spellcheck/stopwords.txt | 1 + .../restapi/hpe-alletra-capacity.robot | 50 ++ .../restapi/hpe-alletra-diskstatus.robot | 45 ++ .../restapi/hpe-alletra-diskusage.robot | 46 ++ .../restapi/hpe-alletra-licenses.robot | 39 ++ .../restapi/hpe-alletra-listdisks.robot | 38 ++ .../restapi/hpe-alletra-listvolumes.robot | 37 ++ .../restapi/hpe-alletra-volumestatus.robot | 42 ++ .../restapi/hpe-alletra-volumeusage.robot | 40 ++ .../alletra/restapi/hpe-alletra.mockoon.json | 259 +++++++++ 23 files changed, 3294 insertions(+) create mode 100644 packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/deb.json create mode 100644 packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/pkg.json create mode 100644 packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/rpm.json create mode 100644 src/storage/hp/alletra/restapi/custom/api.pm create mode 100644 src/storage/hp/alletra/restapi/mode/capacity.pm create mode 100644 src/storage/hp/alletra/restapi/mode/diskstatus.pm create mode 100644 src/storage/hp/alletra/restapi/mode/diskusage.pm create mode 100644 src/storage/hp/alletra/restapi/mode/licenses.pm create mode 100644 src/storage/hp/alletra/restapi/mode/listdisks.pm create mode 100644 src/storage/hp/alletra/restapi/mode/listvolumes.pm create mode 100644 src/storage/hp/alletra/restapi/mode/volumestatus.pm create mode 100644 src/storage/hp/alletra/restapi/mode/volumeusage.pm create mode 100644 src/storage/hp/alletra/restapi/plugin.pm create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-capacity.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-diskstatus.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-diskusage.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-licenses.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-listdisks.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-listvolumes.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-volumestatus.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra-volumeusage.robot create mode 100644 tests/storage/hp/alletra/restapi/hpe-alletra.mockoon.json diff --git a/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/deb.json b/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/deb.json new file mode 100644 index 000000000..9757fe112 --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/deb.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ + ] +} diff --git a/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/pkg.json b/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/pkg.json new file mode 100644 index 000000000..e41982d0a --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/pkg.json @@ -0,0 +1,9 @@ +{ + "pkg_name": "centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi", + "pkg_summary": "Centreon Plugin", + "plugin_name": "centreon_hpe_alletra_restapi.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "storage/hp/alletra/restapi/" + ] +} diff --git a/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/rpm.json b/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/rpm.json new file mode 100644 index 000000000..9757fe112 --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-Hpe-Alletra-Restapi/rpm.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ + ] +} diff --git a/src/storage/hp/alletra/restapi/custom/api.pm b/src/storage/hp/alletra/restapi/custom/api.pm new file mode 100644 index 000000000..d50eb5f7a --- /dev/null +++ b/src/storage/hp/alletra/restapi/custom/api.pm @@ -0,0 +1,267 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::custom::api; + +use strict; +use warnings; +use centreon::plugins::http; +use centreon::plugins::statefile; +use JSON::XS; +use Digest::MD5 qw(md5_hex); + +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 => { + 'api-username:s' => + { name => 'api_username' }, + 'api-password:s' => + { name => 'api_password' }, + 'hostname:s' => + { name => 'hostname' }, + 'port:s' => + { name => 'port', default => 443 }, + 'proto:s' => + { name => 'proto', default => 'https' }, + 'timeout:s' => + { name => 'timeout', default => 30 }, + 'unknown-http-status:s' => + { name => 'unknown_http_status', default => '%{http_code} < 200 or %{http_code} >= 300' }, + 'warning-http-status:s' => + { name => 'warning_http_status' }, + 'critical-http-status:s' => + { name => 'critical_http_status' } + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'HPE Alletra API OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl'); + $self->{cache} = centreon::plugins::statefile->new(%options); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults {} + +sub check_options { + my ($self, %options) = @_; + + if (centreon::plugins::misc::is_empty($self->{option_results}->{hostname})) { + $self->{output}->add_option_msg(short_msg => 'Need to specify --hostname option.'); + $self->{output}->option_exit(); + } + if (centreon::plugins::misc::is_empty($self->{option_results}->{api_username})) { + $self->{output}->add_option_msg(short_msg => 'Need to specify --api-username option.'); + $self->{output}->option_exit(); + } + if (centreon::plugins::misc::is_empty($self->{option_results}->{api_password})) { + $self->{output}->add_option_msg(short_msg => 'Need to specify --api-password option.'); + $self->{output}->option_exit(); + } + $self->{http}->set_options(%{$self->{option_results}}); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); + + $self->{cache}->check_options(option_results => $self->{option_results}); + + return 0; +} + +sub get_connection_info { + my ($self, %options) = @_; + + return $self->{option_results}->{hostname} . ':' . $self->{option_results}->{port}; +} + +sub get_token { + my ($self, %options) = @_; + + $self->{cache}->read(statefile => + 'hpe_alletra_' . md5_hex($self->get_connection_info() . '_' . $self->{option_results}->{api_username})); + my $auth_key = $self->{cache}->get(name => 'auth_key'); + + if (! $auth_key) { + my $json_request = { + user => $self->{option_results}->{api_username}, + password => $self->{option_results}->{api_password} + }; + my $encoded; + eval { + $encoded = encode_json($json_request); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => + 'An error occurred while encoding the credentials to a JSON string.'); + $self->{output}->option_exit(); + } + + my $content = $self->{http}->request( + method => 'POST', + url_path => '/api/v1/credentials', + query_form_post => $encoded, + unknown_status => $self->{option_results}->{unknown_http_status}, + warning_status => $self->{option_results}->{warning_http_status}, + critical_status => $self->{option_results}->{critical_http_status}, + header => [ 'Content-Type: application/json' ] + ); + + my $decoded; + eval { + $decoded = JSON::XS->new->utf8->decode($content); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "An error occurred while decoding the response ('$content')."); + $self->{output}->option_exit(); + } + + $auth_key = $decoded->{key}; + my $data = { + updated => time(), + auth_key => $auth_key + }; + $self->{cache}->write(data => $data); + } + + return $auth_key; +} + +sub clean_token { + my ($self, %options) = @_; + + my $data = { updated => time() }; + $self->{cache}->write(data => $data); +} + +sub request_api { + my ($self, %options) = @_; + + my $get_param = []; + if (defined($options{get_param})) { + $get_param = $options{get_param}; + } + + my $token = $self->get_token(); + my ($content) = $self->{http}->request( + url_path => $options{endpoint}, + get_param => $get_param, + header => [ 'X-HP3PAR-WSAPI-SessionKey: ' . $token ], + unknown_status => '', + warning_status => '', + critical_status => '' + ); + + # Maybe token is invalid. so we retry + if (!defined($self->{token}) && $self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { + $self->clean_token(); + $token = $self->get_token(); + + $content = $self->{http}->request( + url_path => $options{endpoint}, + get_param => $get_param, + header => [ 'X-HP3PAR-WSAPI-SessionKey: ' . $token ], + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + } + + if (!defined($content) || $content eq '') { + $self->{output}->add_option_msg(short_msg => + "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']"); + $self->{output}->option_exit(); + } + + my $decoded; + eval { + $decoded = JSON::XS->new->allow_nonref(1)->utf8->decode($content); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => + "Cannot decode response (add --debug option to display returned content)"); + $self->{output}->option_exit(); + } + + return $decoded; +} + +1; + +__END__ + +=head1 NAME + +HPE Alletra REST API + +=head1 HPE Alletra API OPTIONS + +HPE Alletra REST API + +=over 8 + +=item B<--hostname> + +Address of the server that hosts the API. + +=item B<--port> + +Define the TCP port to use to reach the API (default: 443). + +=item B<--proto> + +Define the protocol to reach the API (default: 'https'). + +=item B<--api-username> + +Define the username for authentication. + +=item B<--api-password> + +Define the password associated with the username. + +=item B<--timeout> + +Define the timeout in seconds for HTTP requests (default: 30). + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/capacity.pm b/src/storage/hp/alletra/restapi/mode/capacity.pm new file mode 100644 index 000000000..3dae47a75 --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/capacity.pm @@ -0,0 +1,494 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::capacity; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => + $self->{result_values}->{total}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + return sprintf( + 'space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)', + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} + ); +} + +sub storage_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking storage '%s'", + $options{instance_value}->{type} + ); +} + +sub prefix_storage_output { + my ($self, %options) = @_; + + return sprintf( + "storage '%s' ", + $options{instance_value}->{type} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'storages', + type => 3, + cb_prefix_output => 'prefix_storage_output', + cb_long_output => 'storage_long_output', + indent_long_output => ' ', + message_multiple => 'All storage capacities are ok', + group => + [ + { name => 'space', type => 0 }, + { name => 'provisioning', type => 0 }, + { name => 'efficiency', type => 0, skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{space} = [ + { + label => 'space-usage', nlabel => 'storage.space.usage.bytes', set => { + key_values => + [ + { name => 'used' }, + { name => 'free' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'total' } + ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'total', + unit => 'B', + cast_int => 1, + label_extra_instance => 1 + } + ] + } + }, + { + label => 'space-usage-free', nlabel => 'storage.space.free.bytes', display_ok => 0, set => { + key_values => + [ + { name => 'free' }, + { name => 'used' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'total' } + ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'total', + unit => 'B', + cast_int => 1, + label_extra_instance => 1 + } + ] + } + }, + { + label => 'space-usage-prct', nlabel => 'storage.space.usage.percentage', display_ok => 0, set => { + key_values => + [ + { name => 'prct_used' }, + { name => 'used' }, + { name => 'free' }, + { name => 'prct_free' }, + { name => 'total' } + ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => + [ + { + template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1 + } + ] + } + }, + { + label => 'space-unavailable', nlabel => 'storage.space.unavailable.bytes', set => { + key_values => + [ + { name => 'unavailable' } + ], + output_template => 'unavailable: %s %s', + output_change_bytes => 1, + perfdatas => [ + { + template => '%s', unit => 'B', min => 0, label_extra_instance => 1 + } + ] + } + }, + { + label => 'space-failed', nlabel => 'storage.space.failed.bytes', set => { + key_values => [ { name => 'failed' } ], + output_template => 'failed: %s %s', + output_change_bytes => 1, + perfdatas => [ + { + template => '%s', unit => 'B', min => 0, label_extra_instance => 1 + } + ] + } + } + ]; + + $self->{maps_counters}->{provisioning} = [ + { + label => 'provisioning-virtual-size', nlabel => 'storage.provisioning.virtualsize.bytes', set => { + key_values => [ { name => 'virtual_size' } ], + output_template => 'provisioning virtual size: %s %s', + output_change_bytes => 1, + perfdatas => [ + { + template => '%s', unit => 'B', min => 0, label_extra_instance => 1 + } + ] + } + }, + { + label => 'provisioning-used', nlabel => 'storage.provisioning.used.bytes', set => { + key_values => [ { name => 'used' } ], + output_template => 'provisioning used: %s %s', + output_change_bytes => 1, + perfdatas => [ + { + template => '%s', unit => 'B', min => 0, label_extra_instance => 1 + } + ] + } + }, + { + label => 'provisioning-allocated', nlabel => 'storage.provisioning.allocated.bytes', set => { + key_values => [ { name => 'allocated' } ], + output_template => 'provisioning allocated: %s %s', + output_change_bytes => 1, + perfdatas => [ + { + template => '%s', unit => 'B', min => 0, label_extra_instance => 1 + } + ] + } + }, + { + label => 'provisioning-free', nlabel => 'storage.provisioning.free.bytes', set => { + key_values => [ { name => 'free' } ], + output_template => 'provisioning free: %s %s', + output_change_bytes => 1, + perfdatas => [ + { + template => '%s', unit => 'B', min => 0, label_extra_instance => 1 + } + ] + } + } + ]; + + $self->{maps_counters}->{efficiency} = [ + { + label => 'compaction', nlabel => 'storage.space.compaction.ratio.count', set => { + key_values => [ { name => 'compaction' } ], + output_template => 'compaction: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { + label => 'deduplication', nlabel => 'storage.space.deduplication.ratio.count', set => { + key_values => [ { name => 'deduplication' } ], + output_template => 'deduplication: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { + label => 'compression', nlabel => 'storage.space.compression.ratio.count', set => { + key_values => [ { name => 'compression' } ], + output_template => 'compression: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { + label => 'data-reduction', nlabel => 'storage.space.data_reduction.ratio.count', set => { + key_values => [ { name => 'data_reduction' } ], + output_template => 'data reduction: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + }, + { + label => 'overprovisioning', nlabel => 'storage.space.overprovisioning.ratio.count', set => { + key_values => [ { name => 'overprovisioning' } ], + output_template => 'overprovisioning: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1 } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-type:s' => { name => 'filter_type' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $response = $options{custom}->request_api( + endpoint => '/api/v1/capacity' + ); + + for my $type (keys %{$response}) { + next if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' + && $type !~ /$self->{option_results}->{filter_type}/); + + my $total = $response->{$type}->{totalMiB} * 1024 * 1024; + my $free = $response->{$type}->{freeMiB} * 1024 * 1024; + my $unavailable = $response->{$type}->{unavailableCapacityMiB} * 1024 * 1024; + my $failed = $response->{$type}->{failedCapacityMiB} * 1024 * 1024; + + $self->{storages}->{$type} = { + type => $type, + space => { + total => $total, + free => $free, + used => $total - $free, + unavailable => $unavailable, + prct_used => $total > 0 ? ($total - $free) * 100 / $total : 0, + prct_free => $total > 0 ? $free * 100 / $total : 0, + failed => $failed + }, + provisioning => { + virtual_size => $response->{$type}->{overProvisionedVirtualSizeMiB} * 1024 * 1024, + used => $response->{$type}->{overProvisionedUsedMiB} * 1024 * 1024, + allocated => $response->{$type}->{overProvisionedAllocatedMiB} * 1024 * 1024, + free => $response->{$type}->{overProvisionedFreeMiB} * 1024 * 1024, + } + }; + + my $shortcut = $response->{$type}->{allocated}->{volumes}->{capacityEfficiency}; + + $self->{storages}->{$type}->{efficiency}->{compaction} = $shortcut->{compaction} if defined($shortcut->{compaction}); + $self->{storages}->{$type}->{efficiency}->{deduplication} = $shortcut->{deduplication} if defined($shortcut->{deduplication}); + $self->{storages}->{$type}->{efficiency}->{compression} = $shortcut->{compression} if defined($shortcut->{compression}); + $self->{storages}->{$type}->{efficiency}->{data_reduction} = $shortcut->{dataReduction} if defined($shortcut->{dataReduction}); + $self->{storages}->{$type}->{efficiency}->{overprovisioning} = $shortcut->{overProvisioning} if defined($shortcut->{overProvisioning}); + + } + + if (scalar(keys %{$self->{storages}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get capacity information"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check storage capacity per storage type. + +=over 8 + +=item B<--filter-counters> + +Define which counters (filtered by regular expression) should be monitored. +Can be : compaction deduplication compression data-reduction overprovisioning provisioning-virtual-size provisioning-used provisioning-allocated provisioning-free space-usage space-usage-free space-usage-prct space-unavailable space-failed +Example: --filter-counters='^compaction$' + +=item B<--filter-type> + +Filter storage by type (regular expression). +The known types are: C, C, C and C. + +=back + +=head2 Thresholds for space oriented metrics + +=over 8 + +=item B<--warning-space-failed> + +Threshold in bytes. + +=item B<--critical-space-failed> + +Threshold in bytes. + +=item B<--warning-space-unavailable> + +Threshold in bytes. + +=item B<--critical-space-unavailable> + +Threshold in bytes. + +=item B<--warning-space-usage> + +Threshold in bytes. + +=item B<--critical-space-usage> + +Threshold in bytes. + +=item B<--warning-space-usage-free> + +Threshold in bytes. + +=item B<--critical-space-usage-free> + +Threshold in bytes. + +=item B<--warning-space-usage-prct> + +Threshold in percentage. + +=item B<--critical-space-usage-prct> + +Threshold in percentage. + +=back + +=head2 Thresholds for provisioning metrics + +=over 8 + +=item B<--warning-provisioning-allocated> + +Threshold in bytes. + +=item B<--critical-provisioning-allocated> + +Threshold in bytes. + +=item B<--warning-provisioning-free> + +Threshold in bytes. + +=item B<--critical-provisioning-free> + +Threshold in bytes. + +=item B<--warning-provisioning-used> + +Threshold in bytes. + +=item B<--critical-provisioning-used> + +Threshold in bytes. + +=item B<--warning-provisioning-virtual-size> + +Threshold in bytes. + +=item B<--critical-provisioning-virtual-size> + +Threshold in bytes. + +=back + +=head2 Thresholds for storage optimization metrics + +=over 8 + +=item B<--warning-compaction> + +Threshold. + +=item B<--critical-compaction> + +Threshold. + +=item B<--warning-compression> + +Threshold. + +=item B<--critical-compression> + +Threshold. + +=item B<--warning-data-reduction> + +Threshold. + +=item B<--critical-data-reduction> + +Threshold. + +=item B<--warning-deduplication> + +Threshold. + +=item B<--critical-deduplication> + +Threshold. + +=item B<--warning-overprovisioning> + +Threshold. + +=item B<--critical-overprovisioning> + +Threshold. + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/diskstatus.pm b/src/storage/hp/alletra/restapi/mode/diskstatus.pm new file mode 100644 index 000000000..2f7124b50 --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/diskstatus.pm @@ -0,0 +1,342 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::diskstatus; + +use base qw(centreon::plugins::templates::counter); +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +use strict; +use warnings; + +my %map_state = ( + 1 => 'normal', + 2 => 'degraded', + 3 => 'new', + 4 => 'failed', + 99 => 'unknown' +); + +sub custom_status_output { + my ($self, %options) = @_; + + return sprintf( + "Disk #%s (%s/%s, serial: %s) located %s is %s", + $self->{result_values}->{id}, + $self->{result_values}->{manufacturer}, + $self->{result_values}->{model}, + $self->{result_values}->{serial}, + $self->{result_values}->{position}, + $self->{result_values}->{status} + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Disks '; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', cb_prefix_output => 'prefix_global_output', type => 0 }, + { name => 'disks', type => 1, message_multiple => 'All disks are ok' } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'disks-total', nlabel => 'disks.total.count', set => { + key_values => [ { name => 'total' } ], + output_template => 'total: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + }, + { label => 'disks-normal', nlabel => 'disks.normal.count', set => { + key_values => [ { name => 'normal' }, { name => 'total' } ], + output_template => 'normal: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'disks-degraded', nlabel => 'disks.degraded.count', set => { + key_values => [ { name => 'degraded' }, { name => 'total' } ], + output_template => 'degraded: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'disks-new', nlabel => 'disks.new.count', set => { + key_values => [ { name => 'new' }, { name => 'total' } ], + output_template => 'new: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'disks-failed', nlabel => 'disks.failed.count', set => { + key_values => [ { name => 'failed' }, { name => 'total' } ], + output_template => 'failed: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'disks-unknown', nlabel => 'disks.unknown.count', set => { + key_values => [ { name => 'unknown' }, { name => 'total' } ], + output_template => 'unknown: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + } + ]; + $self->{maps_counters}->{disks} = [ + { + label => 'status', + type => 2, + warning_default => '%{status} =~ /^(new|degraded|unknown)$/', + critical_default => '%{status} =~ /failed/', + unknown_default => '%{status} =~ /NOT_DOCUMENTED$/', + set => { + key_values => [ { name => 'status' }, { name => 'id' }, { name => 'manufacturer' }, { name => 'model' }, { name => 'serial' }, { name => 'position' } ], + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-id:s' => { name => 'filter_id' }, + 'filter-manufacturer:s' => { name => 'filter_manufacturer' }, + 'filter-model:s' => { name => 'filter_model' }, + 'filter-position:s' => { name => 'filter_position' }, + 'filter-serial:s' => { name => 'filter_serial' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $api_response = $options{custom}->request_api( + endpoint => '/api/v1/disks' + ); + + my $disks = $api_response->{members}; + + $self->{global} = { + total => 0, + normal => 0, + degraded => 0, + new => 0, + failed => 0, + unknown => 0 + }; + + for my $disk (@{$disks}) { + my $disk_intro = "disk #" . $disk->{id} . " (" . $disk->{manufacturer} . "/" . $disk->{model} + . ", serial: " . $disk->{serialNumber} . ") located '" . $disk->{position}; + # skip if filtered by id + if (defined($self->{option_results}->{filter_id}) + and $self->{option_results}->{filter_id} ne '' + and $disk->{id} !~ /$self->{option_results}->{filter_id}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the id does not match the filter.", debug => 1); + next; + } + # skip if filtered by manufacturer + if (defined($self->{option_results}->{filter_manufacturer}) + and $self->{option_results}->{filter_manufacturer} ne '' + and $disk->{manufacturer} !~ /$self->{option_results}->{filter_manufacturer}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the manufacturer does not match the filter.", debug => 1); + next; + } + # skip if filtered by model + if (defined($self->{option_results}->{filter_model}) + and $self->{option_results}->{filter_model} ne '' + and $disk->{model} !~ /$self->{option_results}->{filter_model}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the model does not match the filter.", debug => 1); + next; + } + # skip if filtered by position + if (defined($self->{option_results}->{filter_position}) + and $self->{option_results}->{filter_position} ne '' + and $disk->{position} !~ /$self->{option_results}->{filter_position}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the position does not match the filter.", debug => 1); + next; + } + # skip if filtered by serial + if (defined($self->{option_results}->{filter_serial}) + and $self->{option_results}->{filter_serial} ne '' + and $disk->{serialNumber} !~ /$self->{option_results}->{filter_serial}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the serial does not match the filter.", debug => 1); + next; + } + + my $state = defined($map_state{$disk->{state}}) ? $map_state{$disk->{state}} : 'NOT_DOCUMENTED'; + + # increment adequate global counters + $self->{global}->{total} = $self->{global}->{total} + 1; + $self->{global}->{$state} = $self->{global}->{$state} + 1; + + # add the instance + $self->{disks}->{ $disk->{id} } = { + status => $state, + position => $disk->{position}, + id => $disk->{id}, + manufacturer => $disk->{manufacturer}, + model => $disk->{model}, + serial => $disk->{serialNumber} + }; + } +} + +1; + +__END__ + +=head1 MODE + +Monitor the states of the physical disks. + +=over 8 + +=item B<--filter-counters> + +Define which counters (filtered by regular expression) should be monitored. +Can be : disks-total disks-normal disks-degraded disks-new disks-failed disks-unknown status +Example: --filter-counters='^disks-total$' + +=item B<--filter-id> + +Define which disks should be monitored based on their IDs. +This option will be treated as a regular expression. + +=item B<--filter-manufacturer> + +Define which volumes should be monitored based on the disk manufacturer. +This option will be treated as a regular expression. + +=item B<--filter-model> + +Define which volumes should be monitored based on the disk model. +This option will be treated as a regular expression. + +=item B<--filter-serial> + +Define which volumes should be monitored based on the disk serial number. +This option will be treated as a regular expression. + +=item B<--filter-position> + +Define which volumes should be monitored based on the disk position. +The position is composed of 3 integers, separated by colons: +- Cage number where the physical disk is in. +- Magazine number where the physical disk is in. +- For DC4 cages, disk position within the magazine. For non-DC4 cages, 0. +Example: 7:5:0 +This option will be treated as a regular expression. + +=item B<--warning-status> + +Define the condition to match for the returned status to be WARNING. +Default: '%{status} =~ /^(new|degraded|unknown)$/' + +=item B<--critical-status> + +Define the condition to match for the returned status to be CRITICAL. +Default: '%{status} =~ /failed/' + +=item B<--unknown-status> + +Define the condition to match for the returned status to be UNKNOWN. +Default: '%{status} =~ /NOT_DOCUMENTED$/' + +=item B<--warning-disks-degraded> + +Threshold. + +=item B<--critical-disks-degraded> + +Threshold. + +=item B<--warning-disks-failed> + +Threshold. + +=item B<--critical-disks-failed> + +Threshold. + +=item B<--warning-disks-new> + +Threshold. + +=item B<--critical-disks-new> + +Threshold. + +=item B<--warning-disks-normal> + +Threshold. + +=item B<--critical-disks-normal> + +Threshold. + +=item B<--warning-disks-total> + +Threshold. + +=item B<--critical-disks-total> + +Threshold. + +=item B<--warning-disks-unknown> + +Threshold. + +=item B<--critical-disks-unknown> + +Threshold. + +=item B<--warning-status> + +Threshold. + +=item B<--critical-status> + +Threshold. + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/diskusage.pm b/src/storage/hp/alletra/restapi/mode/diskusage.pm new file mode 100644 index 000000000..a48dd59f2 --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/diskusage.pm @@ -0,0 +1,360 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::diskusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub custom_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + return sprintf( + "Used: %s of %s (%.2f%%) Free: %s (%.2f%%)", + $total_used_value . " " . $total_used_unit, + $total_size_value . " " . $total_size_unit, + $self->{result_values}->{prct_used}, + $total_free_value . " " . $total_free_unit, + $self->{result_values}->{prct_free} + ); +} + +sub custom_global_total_usage_output { + my ($self, %options) = @_; + + my ($used_human, $used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_human, $total_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + my $msg = "Total Used: $used_human $used_unit / $total_human $total_unit" ; + + return $msg; +} + +sub custom_global_total_free_output { + my ($self, %options) = @_; + + my ($free_human, $free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + my $msg = "Total Free: $free_human $free_unit" ; + + return $msg; +} + + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0 }, + { name => 'disk', type => 1, cb_prefix_output => 'prefix_disk_output', message_multiple => 'All disks are ok' }, + ]; + $self->{maps_counters}->{global} = [ + { + label => 'total-usage', + nlabel => 'disks.total.space.usage.bytes', + set => { + key_values => [ { name => 'used' }, { name => 'total' } ], + closure_custom_output => $self->can('custom_global_total_usage_output'), + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { + label => 'total-usage-prct', + nlabel => 'disks.total.space.usage.percent', + set => { + key_values => [ { name => 'used_prct' }], + output_template => 'Total percentage used: %.2f %%', + perfdatas => [ + { template => '%s', uom => '%', min => 0, max => 100 } + ] + } + }, + { + label => 'total-free', + nlabel => 'disks.total.space.free.bytes', + set => { + key_values => [ { name => 'free' }, { name => 'total' } ], + closure_custom_output => $self->can('custom_global_total_free_output'), + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + } + ]; + $self->{maps_counters}->{disk} = [ + { label => 'usage', nlabel => 'disk.space.usage.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'id' }, { name => 'position' }, { name => 'manufacturer' }, { name => 'model' }, { name => 'serial' } ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'id' } + ] + } + }, + { label => 'usage-free', display_ok => 0, nlabel => 'disk.space.free.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'id' }, { name => 'position' }, { name => 'manufacturer' }, { name => 'model' }, { name => 'serial' } ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'id' } + ] + } + }, + { label => 'usage-prct', display_ok => 0, nlabel => 'disk.space.usage.percentage', set => { + key_values => [ { name => 'prct_used' }, { name => 'id' }, { name => 'position' }, { name => 'manufacturer' }, { name => 'model' }, { name => 'serial' } ], + output_template => 'Used : %.2f %%', + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'id' } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-id:s' => { name => 'filter_id' }, + 'filter-manufacturer:s' => { name => 'filter_manufacturer' }, + 'filter-model:s' => { name => 'filter_model' }, + 'filter-serial:s' => { name => 'filter_serial' }, + 'filter-position:s' => { name => 'filter_position' }, + }); + + return $self; +} + +sub prefix_disk_output { + my ($self, %options) = @_; + + #return "Disk '" . $options{instance_value}->{display} . "' "; + return sprintf( + "Disk #%s (%s/%s, serial: %s) located %s has ", + $options{instance_value}->{id}, + $options{instance_value}->{manufacturer}, + $options{instance_value}->{model}, + $options{instance_value}->{serial}, + $options{instance_value}->{position} + ); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $response = $options{custom}->request_api( + endpoint => '/api/v1/disks' + ); + my $disks = $response->{members}; + + $self->{global} = { + total => 0, + free => 0, + used => 0, + used_prct => 0 + }; + $self->{disk} = {}; + + for my $disk (@{$disks}) { + + my $disk_intro = "disk #" . $disk->{id} . " (" . $disk->{manufacturer} . "/" . $disk->{model} + . ", serial: " . $disk->{serialNumber} . ") located '" . $disk->{position}; + + # skip if filtered by id + if (defined($self->{option_results}->{filter_id}) + and $self->{option_results}->{filter_id} ne '' + and $disk->{id} !~ /$self->{option_results}->{filter_id}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the id does not match the filter.", debug => 1); + next; + } + # skip if filtered by manufacturer + if (defined($self->{option_results}->{filter_manufacturer}) + and $self->{option_results}->{filter_manufacturer} ne '' + and $disk->{manufacturer} !~ /$self->{option_results}->{filter_manufacturer}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the manufacturer does not match the filter.", debug => 1); + next; + } + # skip if filtered by model + if (defined($self->{option_results}->{filter_model}) + and $self->{option_results}->{filter_model} ne '' + and $disk->{model} !~ /$self->{option_results}->{filter_model}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the model does not match the filter.", debug => 1); + next; + } + # skip if filtered by position + if (defined($self->{option_results}->{filter_position}) + and $self->{option_results}->{filter_position} ne '' + and $disk->{position} !~ /$self->{option_results}->{filter_position}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the position does not match the filter.", debug => 1); + next; + } + # skip if filtered by serial + if (defined($self->{option_results}->{filter_serial}) + and $self->{option_results}->{filter_serial} ne '' + and $disk->{serialNumber} !~ /$self->{option_results}->{filter_serial}/) { + $self->{output}->output_add(long_msg => "Skipping $disk_intro because the serial does not match the filter.", debug => 1); + next; + } + + my $total = $disk->{totalSizeMiB} * 1024 * 1024; + my $free = $disk->{freeSizeMiB} * 1024 * 1024; + my $used = $total - $free; + + $self->{global}->{total} = $self->{global}->{total} + $total; + $self->{global}->{free} = $self->{global}->{free} + $free; + $self->{global}->{used} = $self->{global}->{used} + $used; + + $self->{disk}->{$disk->{id}} = { + id => $disk->{id}, + total => $total, + used => $used, + free => $free, + prct_used => $used * 100 / $total, + prct_free => $free * 100 / $total, + manufacturer => $disk->{manufacturer}, + model => $disk->{model}, + serial => $disk->{serialNumber}, + position => $disk->{position} + }; + } + + $self->{global}->{used_prct} = $self->{global}->{used} * 100 / $self->{global}->{total} if ($self->{global}->{total} > 0); + + if (scalar(keys %{$self->{disk}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No disk found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check disk usage. + +=over 8 + +=item B<--filter-counters> + +Define which counters (filtered by regular expression) should be monitored. +Can be : usage usage-free usage-prct total-usage total-usage-prct total-free +Example: --filter-counters='^usage-free$' + +=item B<--filter-id> + +Define which disks should be monitored based on their IDs. +This option will be treated as a regular expression. + +=item B<--filter-manufacturer> + +Define which volumes should be monitored based on the disk manufacturer. +This option will be treated as a regular expression. + +=item B<--filter-model> + +Define which volumes should be monitored based on the disk model. +This option will be treated as a regular expression. + +=item B<--filter-serial> + +Define which volumes should be monitored based on the disk serial number. +This option will be treated as a regular expression. + +=item B<--filter-position> + +Define which volumes should be monitored based on the disk position. +The position is composed of 3 integers, separated by colons: + Cage number where the physical disk is in. +- Magazine number where the physical disk is in. +- For DC4 cages, disk position within the magazine. For non-DC4 cages, 0. +Example: 7:5:0 +This option will be treated as a regular expression. + +=back + +=head2 Thresholds for individual disks + +=over 8 + +=item B<--warning-usage> + +Threshold in bytes. + +=item B<--critical-usage> + +Threshold in bytes. + +=item B<--warning-usage-free> + +Threshold in bytes. + +=item B<--critical-usage-free> + +Threshold in bytes. + +=item B<--warning-usage-prct> + +Threshold in percentage. + +=item B<--critical-usage-prct> + +Threshold in percentage. + +=back + +=head2 Thresholds for global statistics + +=over 8 + +=item B<--warning-total-free> + +Threshold in bytes. + +=item B<--critical-total-free> + +Threshold in bytes. + +=item B<--warning-total-usage> + +Threshold in bytes. + +=item B<--critical-total-usage> + +Threshold in bytes. + +=item B<--warning-total-usage-prct> + +Threshold in percentage. + +=item B<--critical-total-usage-prct> + +Threshold in percentage. + + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/licenses.pm b/src/storage/hp/alletra/restapi/mode/licenses.pm new file mode 100644 index 000000000..09fe3a99a --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/licenses.pm @@ -0,0 +1,240 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::licenses; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub custom_license_output { + my ($self, %options) = @_; + + my $message; + if ($self->{result_values}->{expiration_human} eq 'never') { + $message = $self->{result_values}->{name} . ' has permanent license.'; + } elsif ($self->{result_values}->{expires_seconds} == 0) { + $message = sprintf( + "%s license has expired.", + $self->{result_values}->{name} + ); + } else { + $message = sprintf( + "%s license expires in %s.", + $self->{result_values}->{name}, + centreon::plugins::misc::change_seconds(value => $self->{result_values}->{expires_seconds}) + ); + } + + return $message; +} + +sub custom_license_perfdata { + my ($self, %options) = @_; + + return if $self->{result_values}->{expiration_human} eq 'never'; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 's', + instances => $self->{result_values}->{name}, + value => $self->{result_values}->{expires_seconds}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); +} + +sub custom_license_threshold { + my ($self, %options) = @_; + + return 'ok' if (!defined($self->{result_values}->{expires_seconds})); + return $self->{perfdata}->threshold_check( + value => $self->{result_values}->{expires_seconds}, + threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } + ] + ); +} + +sub prefix_license_output { + my ($self, %options) = @_; + + return "License '" . $options{instance_value}->{name} . "' expires: " . $options{instance_value}->{expiration_human} . ". "; +} +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0 }, + { name => 'license_expiration', type => 1, cb_prefix_output => 'prefix_license_output', message_multiple => 'All licenses are ok'} + ]; + + $self->{maps_counters}->{global} = [ + { + label => 'total', + nlabel => 'licenses.total.count', + critical_default => '1:', + set => { + key_values => [ { name => 'total' } ], + output_template => 'Number of licenses: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + }, + { + label => 'expired', + nlabel => 'licenses.expired.count', + critical_default => ':0', + set => { + key_values => [ { name => 'expired' }, { name => 'total' } ], + output_template => 'Number of expired licenses: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + } + ]; + $self->{maps_counters}->{license_expiration} = [ + { label => 'license-expiration', nlabel => 'license.expiration.seconds', set => { + key_values => [ { name => 'name' }, { name => 'expires_seconds' }, { name => 'expiration_status' }, { name => 'expiration_human' }], + closure_custom_output => $self->can('custom_license_output'), + closure_custom_perfdata => $self->can('custom_license_perfdata'), + closure_custom_threshold_check => $self->can('custom_license_threshold') + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-name:s' => { name => 'filter_name' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $response = $options{custom}->request_api( + endpoint => '/api/v1/system' + ); + my $licenses = $response->{licenseInfo}->{licenses}; + + my ($total_licenses, $expired_licenses) = (0, 0); + for my $license_item (@{$licenses}) { + # skip if filter does not match + next if (defined($self->{option_results}->{filter_name}) + and $self->{option_results}->{filter_name} ne '' + and $license_item->{name} !~ /$self->{option_results}->{filter_name}/); + + $total_licenses = $total_licenses + 1; + $self->{license_expiration}->{$license_item->{name}} = { + name => $license_item->{name}, + expiration_human => defined($license_item->{expiryTime8601}) ? $license_item->{expiryTime8601} : 'never', + expires_seconds => 0, + }; + + my $license_status = 'valid'; + if (defined($license_item->{expiryTimeSec})) { + if ($license_item->{expiryTimeSec} > time()) { + $self->{license_expiration}->{$license_item->{name}}->{expires_seconds} = $license_item->{expiryTimeSec} - time(); + } else { + $license_status = 'expired'; + $expired_licenses = $expired_licenses + 1; + } + } + $self->{license_expiration}->{$license_item->{name}}->{expiration_status} = $license_status; + } + $self->{global} = { + total => $total_licenses, + expired => $expired_licenses + }; + + if (scalar(keys %{$self->{license_expiration}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "Couldn't get information about licenses"); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check storage capacities. + +=over 8 + +=item B<--filter-counters> + +Define which counters (filtered by regular expression) should be monitored. +Can be : license-expiration total expired +Example: --filter-counters='^total$' + +=item B<--filter-name> + +Filter licenses by name (regular expression). + +=item B<--warning-expired> + +Threshold. +Applies to the number of expired licenses. + +=item B<--critical-expired> + +Threshold. +Default: C<:0> +Applies to the number of expired licenses. + +=item B<--warning-license-expiration> + +Threshold in seconds. +Applies to the remaining time in seconds until the licenses expire. + +=item B<--critical-license-expiration> + +Threshold in seconds. +Applies to the remaining time in seconds until the licenses expire. + +=item B<--warning-total> + +Threshold. +Applies to the total number of licenses. + + +=item B<--critical-total> + +Threshold. +Default: C<1:> +Applies to the total number of licenses. + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/listdisks.pm b/src/storage/hp/alletra/restapi/mode/listdisks.pm new file mode 100644 index 000000000..eb30df945 --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/listdisks.pm @@ -0,0 +1,188 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::listdisks; + +use strict; +use warnings; + +use base qw(centreon::plugins::mode); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-id:s' => { name => 'filter_id' }, + 'filter-protocol:s' => { name => 'filter_protocol' }, + 'filter-type:s' => { name => 'filter_type' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +my %connection_type = ( + 1 => 'FC', + 2 => 'SATA', + 4 => 'NVMe', + 99 => 'unknown' +); + +my %media_type = ( + 1 => 'Magnetic', + 2 => 'SLC', + 3 => 'MLC', + 4 => 'cMLC', + 5 => '3DX', + 99 => 'unknown' +); + +sub run { + my ($self, %options) = @_; + + my $response = $options{custom}->request_api(endpoint => '/api/v1/disks'); + my $disks = $response->{members}; + + $self->{disks} = []; + + for my $disk (@{$disks}) { + $disk->{type} = $media_type{$disk->{type}}; + $disk->{protocol} = $connection_type{$disk->{protocol}}; + + # skip if filtered by protocol + if (defined($self->{option_results}->{filter_protocol}) + and $self->{option_results}->{filter_protocol} ne '' and $disk->{protocol} !~ /$self->{option_results}->{filter_protocol}/) { + $self->{output}->output_add( + long_msg => "Skipping $disk->{protocol} because the protocol does not match the protocol filter.", + debug => 1 + ); + next; + } + + # skip if filtered by type + if (defined($self->{option_results}->{filter_type}) + and $self->{option_results}->{filter_type} ne '' and $disk->{type} !~ /$self->{option_results}->{filter_type}/) { + $self->{output}->output_add( + long_msg => "Skipping $disk->{type} because the type does not match the type filter.", + debug => 1 + ); + next; + } + + # skip if filtered by id + if (defined($self->{option_results}->{filter_id}) + and $self->{option_results}->{filter_id} ne '' and $disk->{id} !~ /$self->{option_results}->{filter_id}/) { + $self->{output}->output_add( + long_msg => "Skipping $disk->{id} because the id does not match the id filter.", + debug => 1 + ); + next; + } + + push @{$self->{disks}}, { + id => $disk->{id}, + position => $disk->{position}, + size => $disk->{totalSizeMiB}, + manufacturer => $disk->{manufacturer}, + model => $disk->{model}, + serial => $disk->{serialNumber}, + protocol => $disk->{protocol}, + type => $disk->{type} + }; + + $self->{output}->output_add( + long_msg => sprintf( + "[id: %s][position: %s][size: %s][manufacturer: %s][model: %s][serial: %s][protocol: %s][type: %s]", + $disk->{id}, + $disk->{position}, + $disk->{totalSizeMiB}, + $disk->{manufacturer}, + $disk->{model}, + $disk->{serialNumber}, + $disk->{protocol}, + $disk->{type} + ) + ); + } + + if (!defined($options{disco_show})) { + $self->{output}->output_add(severity => 'OK', short_msg => 'Disks:'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + } +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => + [ 'id', 'position', 'size', 'manufacturer', 'model', 'serial', 'protocol', 'type' ]); +} + +sub disco_show { + my ($self, %options) = @_; + + $options{disco_show} = 1; + $self->run(%options); + + for my $disk (@{$self->{disks}}) { + $self->{output}->add_disco_entry( + id => $disk->{id}, + position => $disk->{position}, + size => $disk->{size}, + manufacturer => $disk->{manufacturer}, + model => $disk->{model}, + serial => $disk->{serial}, + protocol => $disk->{protocol}, + type => $disk->{type} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List physical disks using the HPE Alletra REST API. + +=over 8 + +=item B<--filter-id> + +Display disks matching the id filter. + +=item B<--filter-protocol> + +Display disks matching the protocol filter. + +=item B<--filter-type> + +Display disks matching the media type filter. + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/listvolumes.pm b/src/storage/hp/alletra/restapi/mode/listvolumes.pm new file mode 100644 index 000000000..6baff3ef6 --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/listvolumes.pm @@ -0,0 +1,148 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::listvolumes; + +use strict; +use warnings; + +use base qw(centreon::plugins::mode); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-id:s' => { name => 'filter_id' }, + 'filter-name:s' => { name => 'filter_name' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +my %map_state = ( + 1 => 'normal', + 2 => 'degraded', + 3 => 'failed', + 99 => 'unknown' +); + +sub run { + my ($self, %options) = @_; + + my $response = $options{custom}->request_api(endpoint => '/api/v1/volumes'); + my $volumes = $response->{members}; + + $self->{volumes} = []; + + for my $volume (@{$volumes}) { + # skip if filtered by name + if (defined($self->{option_results}->{filter_name}) + and $self->{option_results}->{filter_name} ne '' and $volume->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add( + long_msg => "Skipping $volume->{name} because the name does not match the name filter.", + debug => 1 + ); + next; + } + + # skip if filtered by name + if (defined($self->{option_results}->{filter_id}) + and $self->{option_results}->{filter_id} ne '' and $volume->{id} !~ /$self->{option_results}->{filter_id}/) { + $self->{output}->output_add( + long_msg => "Skipping $volume->{name} because the id does not match the id filter.", + debug => 1 + ); + next; + } + + push @{$self->{volumes}}, { + id => $volume->{id}, + name => $volume->{name}, + size => $volume->{sizeMiB}, + state => defined($map_state{$volume->{state}}) ? $map_state{$volume->{state}} : 'NOT_DOCUMENTED' + }; + + $self->{output}->output_add( + long_msg => sprintf( + "[id: %s][name: %s][size: %s][state: %s]", + $volume->{id}, + $volume->{name}, + $volume->{sizeMiB}, + $volume->{state} + ) + ); + } + + if (!defined($options{disco_show})) { + $self->{output}->output_add(severity => 'OK', short_msg => 'Volumes:'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + } +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => [ 'id', 'name', 'size', 'state' ]); +} + +sub disco_show { + my ($self, %options) = @_; + + $options{disco_show} = 1; + $self->run(%options); + + for my $disk (@{$self->{volumes}}) { + $self->{output}->add_disco_entry( + id => $disk->{id}, + name => $disk->{name}, + size => $disk->{size}, + state => $disk->{state} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List physical volumes using the HPE Alletra REST API. + +=over 8 + +=item B<--filter-name> + +Display volumes matching the name filter. + +=item B<--filter-id> + +Display volumes matching the id filter. + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/volumestatus.pm b/src/storage/hp/alletra/restapi/mode/volumestatus.pm new file mode 100644 index 000000000..04e9ca801 --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/volumestatus.pm @@ -0,0 +1,304 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::volumestatus; + +use base qw(centreon::plugins::templates::counter); +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +use strict; +use warnings; + +my %map_state = ( + 1 => 'normal', + 2 => 'degraded', + 4 => 'failed', + 99 => 'unknown' +); + +my %compression_state = ( + 1 => 'YES', + 2 => 'NO', + 3 => 'OFF', + 4 => 'NA', + 5 => 'V1', + 6 => 'v2' +); + +my %provisioning_type = ( + 1 => 'FULL', + 2 => 'TPVV', + 3 => 'SNP', + 4 => 'PEER', + 5 => 'UNKNOWN', + 6 => 'TDVV', + 7 => 'DDS' +); + +sub custom_status_output { + my ($self, %options) = @_; + + return sprintf( + "Volume #%s (%s) uuid: %s (readonly: %s, compression: %s, provisioning: %s)", + $self->{result_values}->{id}, + $self->{result_values}->{name}, + $self->{result_values}->{uuid}, + $self->{result_values}->{readonly}, + $self->{result_values}->{compression_state}, + $self->{result_values}->{provisioning_type} + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Volumes '; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', cb_prefix_output => 'prefix_global_output', type => 0 }, + { name => 'volumes', type => 1, message_multiple => 'All volumes are ok' } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'volumes-total', nlabel => 'volumes.total.count', set => { + key_values => [ { name => 'total' } ], + output_template => 'total: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + }, + { label => 'volumes-normal', nlabel => 'volumes.normal.count', set => { + key_values => [ { name => 'normal' }, { name => 'total' } ], + output_template => 'normal: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'volumes-degraded', nlabel => 'volumes.degraded.count', set => { + key_values => [ { name => 'degraded' }, { name => 'total' } ], + output_template => 'degraded: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'volumes-failed', nlabel => 'volumes.failed.count', set => { + key_values => [ { name => 'failed' }, { name => 'total' } ], + output_template => 'failed: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'volumes-unknown', nlabel => 'volumes.unknown.count', set => { + key_values => [ { name => 'unknown' }, { name => 'total' } ], + output_template => 'unknown: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + } + ]; + $self->{maps_counters}->{volumes} = [ + { + label => 'status', + type => 2, + warning_default => '%{status} =~ /^(degraded|unknown)$/', + critical_default => '%{status} =~ /failed/', + unknown_default => '%{status} =~ /NOT_DOCUMENTED$/', + set => { + key_values => [ + { name => 'status' }, + { name => 'id' }, + { name => 'name' }, + { name => 'uuid' }, + { name => 'compression_state' }, + { name => 'provisioning_type' }, + { name => 'readonly' } + ], + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub {return 0;}, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-id:s' => { name => 'filter_id' }, + 'filter-name:s' => { name => 'filter_name' }, + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $api_response = $options{custom}->request_api( + endpoint => '/api/v1/volumes' + ); + + my $volumes = $api_response->{members}; + + $self->{global} = { + total => 0, + normal => 0, + degraded => 0, + failed => 0, + unknown => 0 + }; + + for my $volume (@{$volumes}) { + # skip if filtered by name + if (defined($self->{option_results}->{filter_name}) + and $self->{option_results}->{filter_name} ne '' and $volume->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add( + long_msg => "Skipping $volume->{name} because the name does not match the name filter.", + debug => 1 + ); + next; + } + + # skip if filtered by name + if (defined($self->{option_results}->{filter_id}) + and $self->{option_results}->{filter_id} ne '' and $volume->{id} !~ /$self->{option_results}->{filter_id}/) { + $self->{output}->output_add( + long_msg => "Skipping $volume->{name} because the id does not match the id filter.", + debug => 1 + ); + next; + } + my $state = defined($map_state{$volume->{state}}) ? $map_state{$volume->{state}} : 'NOT_DOCUMENTED'; + + # increment adequate global counters + $self->{global}->{total} = $self->{global}->{total} + 1; + $self->{global}->{$state} = $self->{global}->{$state} + 1; + + # add the instance + $self->{volumes}->{ $volume->{id} } = { + status => $state, + name => $volume->{name}, + id => $volume->{id}, + uuid => $volume->{uuid}, + compression_state => defined($compression_state{$volume->{compressionState}}) ? + $compression_state{$volume->{compressionState}} : 'NOT_DOCUMENTED', + provisioning_type => defined($provisioning_type{$volume->{provisioningType}}) ? + $provisioning_type{$volume->{provisioningType}} : 'NOT_DOCUMENTED', + readonly => $volume->{readOnly} + }; + } +} + +1; + +__END__ + +=head1 MODE + +Monitor the states of the volumes. + +=over 8 + +=item B<--filter-counters> + +Define which counters (filtered by regular expression) should be monitored. +Can be : volumes-total volumes-normal volumes-degraded volumes-failed volumes-unknown status +Example: --filter-counters='^volumes-total$' + +=item B<--filter-id> + +Define which volumes should be monitored based on their IDs. +This option will be treated as a regular expression. + +=item B<--filter-name> + +Define which volumes should be monitored based on the volume name. +This option will be treated as a regular expression. + +=item B<--warning-status> + +Define the condition to match for the returned status to be WARNING. +Default: '%{status} =~ /^(degraded|unknown)$/' + +=item B<--critical-status> + +Define the condition to match for the returned status to be CRITICAL. +Default: '%{status} =~ /failed/' + +=item B<--unknown-status> + +Define the condition to match for the returned status to be UNKNOWN. +Default: '%{status} =~ /NOT_DOCUMENTED$/' + +=item B<--warning-volumes-degraded> + +Threshold. + +=item B<--critical-volumes-degraded> + +Threshold. + +=item B<--warning-volumes-failed> + +Threshold. + +=item B<--critical-volumes-failed> + +Threshold. + +=item B<--warning-volumes-normal> + +Threshold. + +=item B<--critical-volumes-normal> + +Threshold. + +=item B<--warning-volumes-total> + +Threshold. + +=item B<--critical-volumes-total> + +Threshold. + +=item B<--warning-volumes-unknown> + +Threshold. + +=item B<--critical-volumes-unknown> + +Threshold. + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/mode/volumeusage.pm b/src/storage/hp/alletra/restapi/mode/volumeusage.pm new file mode 100644 index 000000000..51d4d08e9 --- /dev/null +++ b/src/storage/hp/alletra/restapi/mode/volumeusage.pm @@ -0,0 +1,282 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::restapi::mode::volumeusage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub custom_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes( + value => $self->{result_values}->{total} + ); + 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} + ); + + return sprintf( + "Total: %s Reserved: %s Used: %s (%.2f%%) Free: %s (%.2f%%)", + $total_size_value . " " . $total_size_unit, + $reserved_value . " " . $reserved_unit, + $used_value . " " . $used_unit, + $self->{result_values}->{prct_used}, + $free_value . " " . $free_unit, + $self->{result_values}->{prct_free}, + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'volume', + type => 1, + cb_prefix_output => 'prefix_volume_output', + message_multiple => 'All volumes are ok' + }, + ]; + + $self->{maps_counters}->{volume} = [ + { + label => 'usage', + nlabel => 'volume.space.usage.bytes', + set => { + key_values => + [ + { name => 'used' }, + { name => 'free' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'total' }, + { name => 'reserved' }, + { name => 'name' }, + { name => 'id' } + ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { + template => '%d', + min => 0, + max => 'total', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'name' + } + ] + } + }, + { + label => 'usage-free', + display_ok => 0, + nlabel => 'volume.space.free.bytes', + set => { + key_values => + [ + { name => 'free' }, + { name => 'used' }, + { name => 'prct_used' }, + { name => 'prct_free' }, + { name => 'total' }, + { name => 'reserved' }, + { name => 'name' }, + { name => 'id' } + ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => + [ + { + template => '%d', + min => 0, + max => 'total', + unit => 'B', + cast_int => 1, + label_extra_instance => 1, + instance_use => 'name' + } + ] + } + }, + { + label => 'usage-prct', + display_ok => 0, + nlabel => 'volume.space.usage.percentage', + set => { + key_values => [ { name => 'prct_used' }, { name => 'name' }, { name => 'id' } ], + output_template => 'Used : %.2f %%', + perfdatas => [ + { + template => '%.2f', + min => 0, + max => 100, + unit => '%', + label_extra_instance => 1, + instance_use => 'name' + } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options( + arguments => { + 'filter-id:s' => { name => 'filter_id' }, + 'filter-name:s' => { name => 'filter_name' } + }); + + return $self; +} + +sub prefix_volume_output { + my ($self, %options) = @_; + + return "Volume '" . $options{instance_value}->{name} . "' (#" . $options{instance_value}->{id} . ") "; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $response = $options{custom}->request_api(endpoint => '/api/v1/volumes'); + my $volumes = $response->{members}; + $self->{volume} = {}; + + for my $volume (@{$volumes}) { + my $name = $volume->{name}; + my $id = $volume->{id}; + + if (defined($self->{option_results}->{filter_name}) and $self->{option_results}->{filter_name} ne '' and + $name !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add( + long_msg => + "Skipping volume named '" . $name . "': not matching filter /" . $self->{option_results}->{filter_name} . "/.", + debug => + 1 + ); + next; + } + if (defined($self->{option_results}->{filter_id}) and $self->{option_results}->{filter_id} ne '' and + $id !~ /$self->{option_results}->{filter_id}/) { + $self->{output}->output_add( + long_msg => + "Skipping volume #" . $id . ": not matching filter /" . $self->{option_results}->{filter_id} . "/.", + debug => 1 + ); + next; + } + + my $total = $volume->{sizeMiB} * 1024 * 1024; + my $reserved = $volume->{totalReservedMiB} * 1024 * 1024; + my $used = $volume->{totalUsedMiB} * 1024 * 1024; + my $free = ($total - $used) >= 0 ? ($total - $used) : 0; + + $self->{volume}->{$name} = { + id => $id, + name => $name, + total => $total, + reserved => $reserved, + used => $used, + free => $free, + prct_used => $used * 100 / $total, + prct_free => $free * 100 / $total + }; + } + + if (scalar(keys %{$self->{volume}}) <= 0) { + $self->{output}->add_option_msg(short_msg => "No volume found."); + $self->{output}->option_exit(); + } +} + +1; + +__END__ + +=head1 MODE + +Check volume usage. + +=over 8 + +=item B<--filter-counters> + +Define which counters (filtered by regular expression) should be monitored. +Can be : usage usage-free usage-prct +Example: --filter-counters='^usage$' + +=item B<--filter-id> + +Define which volumes should be monitored based on their IDs. +This option will be treated as a regular expression. + +=item B<--filter-name> + +Define which volumes should be monitored based on the volume names. +This option will be treated as a regular expression. + +=back + +=head2 Thresholds for volume usage metrics + +=over 8 + +=item B<--warning-usage> + +Threshold in bytes. + +=item B<--critical-usage> + +Threshold in bytes. + +=item B<--warning-usage-free> + +Threshold in bytes. + +=item B<--critical-usage-free> + +Threshold in bytes. + +=item B<--warning-usage-prct> + +Threshold in percentage. + +=item B<--critical-usage-prct> + +Threshold in percentage. + +=back + +=cut diff --git a/src/storage/hp/alletra/restapi/plugin.pm b/src/storage/hp/alletra/restapi/plugin.pm new file mode 100644 index 000000000..8f34d6b5c --- /dev/null +++ b/src/storage/hp/alletra/restapi/plugin.pm @@ -0,0 +1,55 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package storage::hp::alletra::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->{modes} = { + 'capacity' => 'storage::hp::alletra::restapi::mode::capacity', + 'disk-status' => 'storage::hp::alletra::restapi::mode::diskstatus', + 'disk-usage' => 'storage::hp::alletra::restapi::mode::diskusage', + 'licenses' => 'storage::hp::alletra::restapi::mode::licenses', + 'list-disks' => 'storage::hp::alletra::restapi::mode::listdisks', + 'list-volumes' => 'storage::hp::alletra::restapi::mode::listvolumes', + 'volume-usage' => 'storage::hp::alletra::restapi::mode::volumeusage', + 'volume-status' => 'storage::hp::alletra::restapi::mode::volumestatus' + }; + + $self->{custom_modes}->{api} = 'storage::hp::alletra::restapi::custom::api'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Monitor HPE Alletra storage controller. + +=cut diff --git a/tests/resources/spellcheck/stopwords.txt b/tests/resources/spellcheck/stopwords.txt index 0e02b8fb8..6a65907fd 100644 --- a/tests/resources/spellcheck/stopwords.txt +++ b/tests/resources/spellcheck/stopwords.txt @@ -13,6 +13,7 @@ ACS ADSL Alcatel allCapacity +Alletra allsteps Ansible --api-filter-orgs diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-capacity.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-capacity.robot new file mode 100644 index 000000000..ed053f976 --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-capacity.robot @@ -0,0 +1,50 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode Capacity + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode capacity +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +Capacity ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} OK: All storage capacities are ok | 'FCCapacity#storage.space.usage.bytes'=0B;;;0;0 'FCCapacity#storage.space.free.bytes'=0B;;;0;0 'FCCapacity#storage.space.usage.percentage'=0.00%;;;0;100 'FCCapacity#storage.space.unavailable.bytes'=0B;;;0; 'FCCapacity#storage.space.failed.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.virtualsize.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.used.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.allocated.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.free.bytes'=0B;;;0; 'FCCapacity#storage.space.compaction.ratio.count'=0;;;0; 'FCCapacity#storage.space.deduplication.ratio.count'=0;;;0; 'FCCapacity#storage.space.data_reduction.ratio.count'=0;;;0; 'FCCapacity#storage.space.overprovisioning.ratio.count'=0;;;0; 'NLCapacity#storage.space.usage.bytes'=0B;;;0;0 'NLCapacity#storage.space.free.bytes'=0B;;;0;0 'NLCapacity#storage.space.usage.percentage'=0.00%;;;0;100 'NLCapacity#storage.space.unavailable.bytes'=0B;;;0; 'NLCapacity#storage.space.failed.bytes'=0B;;;0; 'NLCapacity#storage.provisioning.virtualsize.bytes'=0B;;;0; 'NLCapacity#storage.provisioning.used.bytes'=0B;;;0; 'NLCapacity#storage.provisioning.allocated.bytes'=0B;;;0; 'NLCapacity#storage.provisioning.free.bytes'=0B;;;0; 'NLCapacity#storage.space.compaction.ratio.count'=0;;;0; 'NLCapacity#storage.space.deduplication.ratio.count'=0;;;0; 'NLCapacity#storage.space.data_reduction.ratio.count'=0;;;0; 'NLCapacity#storage.space.overprovisioning.ratio.count'=0;;;0; 'SSDCapacity#storage.space.usage.bytes'=123327838420992B;;;0;184305636605952 'SSDCapacity#storage.space.free.bytes'=60977798184960B;;;0;184305636605952 'SSDCapacity#storage.space.usage.percentage'=66.91%;;;0;100 'SSDCapacity#storage.space.unavailable.bytes'=0B;;;0; 'SSDCapacity#storage.space.failed.bytes'=0B;;;0; 'SSDCapacity#storage.provisioning.virtualsize.bytes'=145453502955520B;;;0; 'SSDCapacity#storage.provisioning.used.bytes'=98247842463744B;;;0; 'SSDCapacity#storage.provisioning.allocated.bytes'=8490068213760B;;;0; 'SSDCapacity#storage.provisioning.free.bytes'=60977798184960B;;;0; 'SSDCapacity#storage.space.compaction.ratio.count'=2.84;;;0; 'SSDCapacity#storage.space.deduplication.ratio.count'=1.17;;;0; 'SSDCapacity#storage.space.compression.ratio.count'=1;;;0; 'SSDCapacity#storage.space.data_reduction.ratio.count'=1.58;;;0; 'SSDCapacity#storage.space.overprovisioning.ratio.count'=0.87;;;0; 'allCapacity#storage.space.usage.bytes'=123327838420992B;;;0;184305636605952 'allCapacity#storage.space.free.bytes'=60977798184960B;;;0;184305636605952 'allCapacity#storage.space.usage.percentage'=66.91%;;;0;100 'allCapacity#storage.space.unavailable.bytes'=0B;;;0; 'allCapacity#storage.space.failed.bytes'=0B;;;0; 'allCapacity#storage.provisioning.virtualsize.bytes'=145453502955520B;;;0; 'allCapacity#storage.provisioning.used.bytes'=98247842463744B;;;0; 'allCapacity#storage.provisioning.allocated.bytes'=8490068213760B;;;0; 'allCapacity#storage.provisioning.free.bytes'=60977798184960B;;;0; 'allCapacity#storage.space.compaction.ratio.count'=2.84;;;0; 'allCapacity#storage.space.deduplication.ratio.count'=1.17;;;0; 'allCapacity#storage.space.compression.ratio.count'=1;;;0; 'allCapacity#storage.space.data_reduction.ratio.count'=1.58;;;0; 'allCapacity#storage.space.overprovisioning.ratio.count'=0.87;;;0; + ... 2 --filter-type=FCCapacity OK: storage 'FCCapacity' space usage total: 0.00 B used: 0.00 B (0.00%) free: 0.00 B (0.00%), unavailable: 0.00 B, failed: 0.00 B - provisioning virtual size: 0.00 B, provisioning used: 0.00 B, provisioning allocated: 0.00 B, provisioning free: 0.00 B - compaction: 0, deduplication: 0, data reduction: 0, overprovisioning: 0 | 'FCCapacity#storage.space.usage.bytes'=0B;;;0;0 'FCCapacity#storage.space.free.bytes'=0B;;;0;0 'FCCapacity#storage.space.usage.percentage'=0.00%;;;0;100 'FCCapacity#storage.space.unavailable.bytes'=0B;;;0; 'FCCapacity#storage.space.failed.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.virtualsize.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.used.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.allocated.bytes'=0B;;;0; 'FCCapacity#storage.provisioning.free.bytes'=0B;;;0; 'FCCapacity#storage.space.compaction.ratio.count'=0;;;0; 'FCCapacity#storage.space.deduplication.ratio.count'=0;;;0; 'FCCapacity#storage.space.data_reduction.ratio.count'=0;;;0; 'FCCapacity#storage.space.overprovisioning.ratio.count'=0;;;0; + ... 3 --filter-type=SSDCapacity --critical-compression=:0 --filter-counters=compression CRITICAL: storage 'SSDCapacity' compression: 1 | 'SSDCapacity#storage.space.compression.ratio.count'=1;;0:0;0; + ... 4 --filter-type=SSDCapacity --filter-counters=space-usage --warning-space-usage=:160 WARNING: storage 'SSDCapacity' space usage total: 167.62 TB used: 112.17 TB (66.91%) free: 55.46 TB (33.09%) | 'SSDCapacity#storage.space.usage.bytes'=123327838420992B;0:160;;0;184305636605952 'SSDCapacity#storage.space.free.bytes'=60977798184960B;;;0;184305636605952 'SSDCapacity#storage.space.usage.percentage'=66.91%;;;0;100 + ... 5 --filter-type=SSDCapacity --filter-counters=space-usage --critical-space-usage-free=:55 CRITICAL: storage 'SSDCapacity' space usage total: 167.62 TB used: 112.17 TB (66.91%) free: 55.46 TB (33.09%) | 'SSDCapacity#storage.space.usage.bytes'=123327838420992B;;;0;184305636605952 'SSDCapacity#storage.space.free.bytes'=60977798184960B;;0:55;0;184305636605952 'SSDCapacity#storage.space.usage.percentage'=66.91%;;;0;100 + ... 6 --filter-type=SSDCapacity --filter-counters=space-usage --critical-space-usage-prct=:60 CRITICAL: storage 'SSDCapacity' space usage total: 167.62 TB used: 112.17 TB (66.91%) free: 55.46 TB (33.09%) | 'SSDCapacity#storage.space.usage.bytes'=123327838420992B;;;0;184305636605952 'SSDCapacity#storage.space.free.bytes'=60977798184960B;;;0;184305636605952 'SSDCapacity#storage.space.usage.percentage'=66.91%;;0:60;0;100 + ... 7 --filter-type=SSDCapacity --filter-counters=space-unavailable --critical-space-unavailable=1:10 CRITICAL: storage 'SSDCapacity' unavailable: 0.00 B | 'SSDCapacity#storage.space.unavailable.bytes'=0B;;1:10;0; + ... 8 --filter-type=SSDCapacity --filter-counters=space-failed --critical-space-failed=1:10 CRITICAL: storage 'SSDCapacity' failed: 0.00 B | 'SSDCapacity#storage.space.failed.bytes'=0B;;1:10;0; + ... 9 --filter-type=SSDCapacity --filter-counters=provisioning-virtual-size OK: storage 'SSDCapacity' provisioning virtual size: 132.29 TB | 'SSDCapacity#storage.provisioning.virtualsize.bytes'=145453502955520B;;;0; + ... 10 --filter-type=SSDCapacity --filter-counters=provisioning-used OK: storage 'SSDCapacity' provisioning used: 89.36 TB | 'SSDCapacity#storage.provisioning.used.bytes'=98247842463744B;;;0; + ... 11 --filter-type=SSDCapacity --filter-counters=provisioning-allocated --critical-provisioning-allocated=:1 CRITICAL: storage 'SSDCapacity' provisioning allocated: 7.72 TB | 'SSDCapacity#storage.provisioning.allocated.bytes'=8490068213760B;;0:1;0; + ... 12 --filter-type=SSDCapacity --filter-counters=provisioning-free --warning-provisioning-free=:1 WARNING: storage 'SSDCapacity' provisioning free: 55.46 TB | 'SSDCapacity#storage.provisioning.free.bytes'=60977798184960B;0:1;;0; + ... 13 --filter-type=SSDCapacity --filter-counters=compaction --warning-compaction=:1 WARNING: storage 'SSDCapacity' compaction: 2.84 | 'SSDCapacity#storage.space.compaction.ratio.count'=2.84;0:1;;0; + ... 14 --filter-type=SSDCapacity --filter-counters=deduplication --warning-deduplication=:1 WARNING: storage 'SSDCapacity' deduplication: 1.17 | 'SSDCapacity#storage.space.deduplication.ratio.count'=1.17;0:1;;0; + ... 15 --filter-type=SSDCapacity --filter-counters=data-reduction --critical-data-reduction=3: CRITICAL: storage 'SSDCapacity' data reduction: 1.58 | 'SSDCapacity#storage.space.data_reduction.ratio.count'=1.58;;3:;0; + ... 16 --filter-type=SSDCapacity --filter-counters=overprovisioning OK: storage 'SSDCapacity' overprovisioning: 0.87 | 'SSDCapacity#storage.space.overprovisioning.ratio.count'=0.87;;;0; diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-diskstatus.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-diskstatus.robot new file mode 100644 index 000000000..f271aba94 --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-diskstatus.robot @@ -0,0 +1,45 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode Disk Status + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode disk-status +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +DiskStatus ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} CRITICAL: Disk #1 (XXXX/XXXX, serial: XXXX) located 1:2 is failed WARNING: Disk #2 (XXX/XXX, serial: XXX) located 1:3 is degraded | 'disks.total.count'=3;;;0; 'disks.normal.count'=1;;;0;3 'disks.degraded.count'=1;;;0;3 'disks.new.count'=0;;;0;3 'disks.failed.count'=1;;;0;3 'disks.unknown.count'=0;;;0;3 + ... 2 --filter-id='^0$' OK: Disks total: 1, normal: 1, degraded: 0, new: 0, failed: 0, unknown: 0 - Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 is normal | 'disks.total.count'=1;;;0; 'disks.normal.count'=1;;;0;1 'disks.degraded.count'=0;;;0;1 'disks.new.count'=0;;;0;1 'disks.failed.count'=0;;;0;1 'disks.unknown.count'=0;;;0;1 + ... 3 --filter-manufacturer='X1$' OK: Disks total: 1, normal: 1, degraded: 0, new: 0, failed: 0, unknown: 0 - Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 is normal | 'disks.total.count'=1;;;0; 'disks.normal.count'=1;;;0;1 'disks.degraded.count'=0;;;0;1 'disks.new.count'=0;;;0;1 'disks.failed.count'=0;;;0;1 'disks.unknown.count'=0;;;0;1 + ... 4 --filter-model='X1$' OK: Disks total: 1, normal: 1, degraded: 0, new: 0, failed: 0, unknown: 0 - Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 is normal | 'disks.total.count'=1;;;0; 'disks.normal.count'=1;;;0;1 'disks.degraded.count'=0;;;0;1 'disks.new.count'=0;;;0;1 'disks.failed.count'=0;;;0;1 'disks.unknown.count'=0;;;0;1 + ... 5 --filter-serial='X2$' CRITICAL: Disk #1 (XXXX2/XXXX2, serial: XXXX2) located 1:2 is failed | 'disks.total.count'=1;;;0; 'disks.normal.count'=0;;;0;1 'disks.degraded.count'=0;;;0;1 'disks.new.count'=0;;;0;1 'disks.failed.count'=1;;;0;1 'disks.unknown.count'=0;;;0;1 + ... 6 --filter-position=1:3 WARNING: Disk #2 (XXX3/XXX3, serial: XXX3) located 1:3 is degraded | 'disks.total.count'=1;;;0; 'disks.normal.count'=0;;;0;1 'disks.degraded.count'=1;;;0;1 'disks.new.count'=0;;;0;1 'disks.failed.count'=0;;;0;1 'disks.unknown.count'=0;;;0;1 + ... 7 --warning-disks-new=1: --critical-status='' --warning-status='' WARNING: Disks new: 0 | 'disks.total.count'=3;;;0; 'disks.normal.count'=1;;;0;3 'disks.degraded.count'=1;;;0;3 'disks.new.count'=0;1:;;0;3 'disks.failed.count'=1;;;0;3 'disks.unknown.count'=0;;;0;3 + ... 8 --warning-disks-degraded=:0 --critical-status='' --warning-status='' WARNING: Disks degraded: 1 | 'disks.total.count'=3;;;0; 'disks.normal.count'=1;;;0;3 'disks.degraded.count'=1;0:0;;0;3 'disks.new.count'=0;;;0;3 'disks.failed.count'=1;;;0;3 'disks.unknown.count'=0;;;0;3 + ... 9 --critical-status='' --warning-status='' --warning-disks-new=@0:0 WARNING: Disks new: 0 | 'disks.total.count'=3;;;0; 'disks.normal.count'=1;;;0;3 'disks.degraded.count'=1;;;0;3 'disks.new.count'=0;@0:0;;0;3 'disks.failed.count'=1;;;0;3 'disks.unknown.count'=0;;;0;3 + ... 10 --critical-status='' --warning-status='' --critical-disks-failed=:0 CRITICAL: Disks failed: 1 | 'disks.total.count'=3;;;0; 'disks.normal.count'=1;;;0;3 'disks.degraded.count'=1;;;0;3 'disks.new.count'=0;;;0;3 'disks.failed.count'=1;;0:0;0;3 'disks.unknown.count'=0;;;0;3 + ... 11 --critical-status='' --warning-status='' --critical-disks-unknown=@0:0 CRITICAL: Disks unknown: 0 | 'disks.total.count'=3;;;0; 'disks.normal.count'=1;;;0;3 'disks.degraded.count'=1;;;0;3 'disks.new.count'=0;;;0;3 'disks.failed.count'=1;;;0;3 'disks.unknown.count'=0;@0:0;;0;3 diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-diskusage.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-diskusage.robot new file mode 100644 index 000000000..dadeb2731 --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-diskusage.robot @@ -0,0 +1,46 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode Disk Usage + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode disk-usage +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +DiskUsage ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} OK: Total Used: 28.04 TB / 41.91 TB, Total percentage used: 66.91 %, Total Free: 13.87 TB - All disks are ok | 'disks.total.space.usage.bytes'=30830348992512;;;0;46076409151488 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 'disks.total.space.free.bytes'=15246060158976;;;0;46076409151488 '0#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.usage.percentage'=66.91%;;;0;100 '1#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '1#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '1#disk.space.usage.percentage'=66.91%;;;0;100 '2#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '2#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '2#disk.space.usage.percentage'=66.91%;;;0;100 + ... 2 --filter-id='^0$' OK: Total Used: 9.35 TB / 13.97 TB, Total percentage used: 66.91 %, Total Free: 4.62 TB - Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) | 'disks.total.space.usage.bytes'=10276782997504;;;0;15358803050496 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 'disks.total.space.free.bytes'=5082020052992;;;0;15358803050496 '0#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.usage.percentage'=66.91%;;;0;100 + ... 3 --filter-manufacturer='X1$' OK: Total Used: 9.35 TB / 13.97 TB, Total percentage used: 66.91 %, Total Free: 4.62 TB - Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) | 'disks.total.space.usage.bytes'=10276782997504;;;0;15358803050496 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 'disks.total.space.free.bytes'=5082020052992;;;0;15358803050496 '0#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.usage.percentage'=66.91%;;;0;100 + ... 4 --filter-model='X1$' OK: Total Used: 9.35 TB / 13.97 TB, Total percentage used: 66.91 %, Total Free: 4.62 TB - Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) | 'disks.total.space.usage.bytes'=10276782997504;;;0;15358803050496 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 'disks.total.space.free.bytes'=5082020052992;;;0;15358803050496 '0#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '0#disk.space.usage.percentage'=66.91%;;;0;100 + ... 5 --filter-serial='X2$' OK: Total Used: 9.35 TB / 13.97 TB, Total percentage used: 66.91 %, Total Free: 4.62 TB - Disk #1 (XXXX2/XXXX2, serial: XXXX2) located 1:2 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) | 'disks.total.space.usage.bytes'=10276782997504;;;0;15358803050496 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 'disks.total.space.free.bytes'=5082020052992;;;0;15358803050496 '1#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '1#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '1#disk.space.usage.percentage'=66.91%;;;0;100 + ... 6 --filter-position=1:2 OK: Total Used: 9.35 TB / 13.97 TB, Total percentage used: 66.91 %, Total Free: 4.62 TB - Disk #1 (XXXX2/XXXX2, serial: XXXX2) located 1:2 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) | 'disks.total.space.usage.bytes'=10276782997504;;;0;15358803050496 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 'disks.total.space.free.bytes'=5082020052992;;;0;15358803050496 '1#disk.space.usage.bytes'=10276782997504B;;;0;15358803050496 '1#disk.space.free.bytes'=10276782997504B;;;0;15358803050496 '1#disk.space.usage.percentage'=66.91%;;;0;100 + ... 7 --filter-counters='^usage$' --critical-usage=:10 CRITICAL: Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) - Disk #1 (XXXX2/XXXX2, serial: XXXX2) located 1:2 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) - Disk #2 (XXX3/XXX3, serial: XXX3) located 1:3 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) | '0#disk.space.usage.bytes'=10276782997504B;;0:10;0;15358803050496 '1#disk.space.usage.bytes'=10276782997504B;;0:10;0;15358803050496 '2#disk.space.usage.bytes'=10276782997504B;;0:10;0;15358803050496 + ... 8 --filter-counters='usage-free' --critical-usage-free=:10 CRITICAL: Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) - Disk #1 (XXXX2/XXXX2, serial: XXXX2) located 1:2 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) - Disk #2 (XXX3/XXX3, serial: XXX3) located 1:3 has Used: 9.35 TB of 13.97 TB (66.91%) Free: 4.62 TB (33.09%) | '0#disk.space.free.bytes'=10276782997504B;;0:10;0;15358803050496 '1#disk.space.free.bytes'=10276782997504B;;0:10;0;15358803050496 '2#disk.space.free.bytes'=10276782997504B;;0:10;0;15358803050496 + ... 9 --filter-counters='usage-prct' --warning-usage-prct=:10 WARNING: Disk #0 (XXXX1/XXXXXX1, serial: XXXXX1) located 1:1 has Used : 66.91 % - Disk #1 (XXXX2/XXXX2, serial: XXXX2) located 1:2 has Used : 66.91 % - Disk #2 (XXX3/XXX3, serial: XXX3) located 1:3 has Used : 66.91 % | 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 '0#disk.space.usage.percentage'=66.91%;0:10;;0;100 '1#disk.space.usage.percentage'=66.91%;0:10;;0;100 '2#disk.space.usage.percentage'=66.91%;0:10;;0;100 + ... 10 --filter-counters='total-free' --warning-total-free=:10 WARNING: Total Free: 13.87 TB | 'disks.total.space.free.bytes'=15246060158976;0:10;;0;46076409151488 + ... 11 --filter-counters='total-usage' --warning-total-usage=:10 WARNING: Total Used: 28.04 TB / 41.91 TB | 'disks.total.space.usage.bytes'=30830348992512;0:10;;0;46076409151488 'disks.total.space.usage.percent'=66.9113534675615;;;0;100 + ... 12 --filter-counters='total-usage-prct' --critical-total-usage-prct=:60 CRITICAL: Total percentage used: 66.91 % | 'disks.total.space.usage.percent'=66.9113534675615;;0:60;0;100 diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-licenses.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-licenses.robot new file mode 100644 index 000000000..9940741c1 --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-licenses.robot @@ -0,0 +1,39 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode Licenses + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode licenses +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +Licenses ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} CRITICAL: Number of expired licenses: 1 | 'licenses.total.count'=9;;0:;0; 'licenses.expired.count'=1;;0:0;0;1 'LICENSE 2#license.expiration.seconds'=0s;;;0; 'LICENSE 3#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 5#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 6#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 8#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 9#license.expiration.seconds'=(\\\\d+)s;;;0; + ... 2 --filter-counters=license-expiration OK: All licenses are ok | 'LICENSE 2#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 3#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 5#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 6#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 8#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 9#license.expiration.seconds'=(\\\\d+)s;;;0; + ... 3 --warning-total=10: --critical-expired='' WARNING: Number of licenses: 9 | 'licenses.total.count'=9;10:;;0; 'licenses.expired.count'=0;;;0;9 'LICENSE 2#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 3#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 5#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 6#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 8#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 9#license.expiration.seconds'=(\\\\d+)s;;;0; + ... 4 --critical-expired='' --warning-expired=':0' WARNING: Number of expired licenses: 1 | 'licenses.total.count'=9;;0:;0; 'licenses.expired.count'=1;0:0;;0;1 'LICENSE 2#license.expiration.seconds'=0s;;;0; 'LICENSE 3#license.expiration.seconds'(\\\\d+)s;;;0; 'LICENSE 5#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 6#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 8#license.expiration.seconds'=(\\\\d+)s;;;0; 'LICENSE 9#license.expiration.seconds'=(\\\\d+)s;;;0; + ... 5 --critical-expired='' --warning-license-expiration='1:' WARNING: License 'LICENSE 1' expires: never. LICENSE 1 has permanent license. - License 'LICENSE 2' expires: 2020-04-20T02:00:00+02:00. LICENSE 2 license has expired. - License 'LICENSE 4' expires: never. LICENSE 4 has permanent license. - License 'LICENSE 7' expires: never. LICENSE 7 has permanent license. | 'licenses.total.count'=9;;1:;0; 'licenses.expired.count'=1;;;0;9 'LICENSE 2#license.expiration.seconds'=0s;1:;;0; 'LICENSE 3#license.expiration.seconds'=(\\\\d+)s;1:;;0; 'LICENSE 5#license.expiration.seconds'=(\\\\d+)s;1:;;0; 'LICENSE 6#license.expiration.seconds'=(\\\\d+)s;1:;;0; 'LICENSE 8#license.expiration.seconds'=(\\\\d+)s;1:;;0; 'LICENSE 9#license.expiration.seconds'=(\\\\d+)s;1:;;0; diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-listdisks.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-listdisks.robot new file mode 100644 index 000000000..b4ba4a013 --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-listdisks.robot @@ -0,0 +1,38 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode List Disks + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode list-disks +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +ListDisks ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} ^Disks: (\\\\n\\\\[id:.*\\\\]){3}\\\\Z + ... 2 --filter-type=SLC ^Disks: (\\\\n\\\\[id:.*\\\\]){1}\\\\Z + ... 3 --filter-id=1 ^Disks: (\\\\n\\\\[id:.*\\\\]){1}\\\\Z + ... 4 --filter-protocol=SATA ^Disks: (\\\\n\\\\[id:.*\\\\]){1}\\\\Z diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-listvolumes.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-listvolumes.robot new file mode 100644 index 000000000..ac074f4ca --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-listvolumes.robot @@ -0,0 +1,37 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode List Volumes + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode list-volumes +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +ListVolumes ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} ^Volumes: (\\\\n\\\\[id:.*\\\\]){3}\\\\Z + ... 2 --filter-name='^test$' ^Volumes: (\\\\n\\\\[id:.*\\\\]){1}\\\\Z + ... 3 --filter-id=1 ^Volumes: (\\\\n\\\\[id:.*\\\\]){1}\\\\Z diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-volumestatus.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-volumestatus.robot new file mode 100644 index 000000000..d04be998f --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-volumestatus.robot @@ -0,0 +1,42 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode Volume Status + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode volume-status +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +VolumeStatus ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Strings ${command} ${expected_string} + + + Examples: tc extraoptions expected_string -- + ... 1 ${EMPTY} CRITICAL: Volume #2 (mtest) uuid: AEZDSSFDDSD (readonly: 0, compression: NA, provisioning: FULL) WARNING: Volume #1 (stest) uuid: SFFDSDDSDSD (readonly: 0, compression: NA, provisioning: FULL) | 'volumes.total.count'=3;;;0; 'volumes.normal.count'=1;;;0;3 'volumes.degraded.count'=1;;;0;3 'volumes.failed.count'=1;;;0;3 'volumes.unknown.count'=0;;;0;3 + ... 2 --critical-status='' --warning-status='' --warning-volumes-total=4: --filter-counters=volumes-total WARNING: Volumes total: 3 | 'volumes.total.count'=3;4:;;0; + ... 3 --mode=volume-status --critical-status='' --warning-status='' --warning-volumes-normal=:0 --filter-counters=volumes-normal WARNING: Volumes normal: 1 | 'volumes.normal.count'=1;0:0;;0;3 + ... 4 --critical-status='' --warning-status='' --warning-volumes-degraded=:0 --filter-counters=volumes-degraded WARNING: Volumes degraded: 1 | 'volumes.degraded.count'=1;0:0;;0;3 + ... 5 --critical-status='' --warning-status='' --warning-volumes-failed=:0 --filter-counters=volumes-failed WARNING: Volumes failed: 1 | 'volumes.failed.count'=1;0:0;;0;3 + ... 6 --critical-status='' --warning-status='' --critical-volumes-unknown=@0:0 --filter-counters=volumes-unknown CRITICAL: Volumes unknown: 0 | 'volumes.unknown.count'=0;;@0:0;0;3 + ... 7 --filter-name=mtest CRITICAL: Volume #2 (mtest) uuid: AEZDSSFDDSD (readonly: 0, compression: NA, provisioning: FULL) | 'volumes.total.count'=1;;;0; 'volumes.normal.count'=0;;;0;1 'volumes.degraded.count'=0;;;0;1 'volumes.failed.count'=1;;;0;1 'volumes.unknown.count'=0;;;0;1 + ... 8 --filter-id=0 OK: Volumes total: 1, normal: 1, degraded: 0, failed: 0, unknown: 0 - Volume #0 (test) uuid: ZAZZAZZA (readonly: 0, compression: NA, provisioning: FULL) | 'volumes.total.count'=1;;;0; 'volumes.normal.count'=1;;;0;1 'volumes.degraded.count'=0;;;0;1 'volumes.failed.count'=0;;;0;1 'volumes.unknown.count'=0;;;0;1 diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra-volumeusage.robot b/tests/storage/hp/alletra/restapi/hpe-alletra-volumeusage.robot new file mode 100644 index 000000000..0221c0f0e --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra-volumeusage.robot @@ -0,0 +1,40 @@ +*** Settings *** +Documentation HPE Alletra Storage REST API Mode Volume Usage + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}hpe-alletra.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=storage::hp::alletra::restapi::plugin +... --mode volume-usage +... --hostname=${HOSTNAME} +... --api-username=xx +... --api-password=xx +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +VolumeUsage ${tc} + [Tags] storage api hpe hp + ${command} Catenate + ... ${CMD} + ... ${extra_options} + + Ctn Run Command And Check Result As Strings ${command} ${expected_string} + + + Examples: tc extraoptions expected_string -- + ... 1 ${EMPTY} OK: All volumes are ok | 'mtest#volume.space.usage.bytes'=549755813888B;;;0;549755813888 'mtest#volume.space.free.bytes'=0B;;;0;549755813888 'mtest#volume.space.usage.percentage'=100.00%;;;0;100 'stest#volume.space.usage.bytes'=23991418880B;;;0;128849018880 'stest#volume.space.free.bytes'=104857600000B;;;0;128849018880 'stest#volume.space.usage.percentage'=18.62%;;;0;100 'test#volume.space.usage.bytes'=10737418240B;;;0;10737418240 'test#volume.space.free.bytes'=0B;;;0;10737418240 'test#volume.space.usage.percentage'=100.00%;;;0;100 + ... 2 --filter-id=1 OK: Volume 'stest' (#1) Total: 120.00 GB Reserved: 120.00 GB Used: 22.34 GB (18.62%) Free: 97.66 GB (81.38%) | 'stest#volume.space.usage.bytes'=23991418880B;;;0;128849018880 'stest#volume.space.free.bytes'=104857600000B;;;0;128849018880 'stest#volume.space.usage.percentage'=18.62%;;;0;100 + ... 3 --filter-name=mtest OK: Volume 'mtest' (#2) Total: 512.00 GB Reserved: 512.00 GB Used: 512.00 GB (100.00%) Free: 0.00 B (0.00%) | 'mtest#volume.space.usage.bytes'=549755813888B;;;0;549755813888 'mtest#volume.space.free.bytes'=0B;;;0;549755813888 'mtest#volume.space.usage.percentage'=100.00%;;;0;100 + ... 4 --filter-counters=usage-prct --critical-usage-prct=90 CRITICAL: Volume 'mtest' (#2) Used : 100.00 % - Volume 'test' (#0) Used : 100.00 % | 'mtest#volume.space.usage.percentage'=100.00%;;0:90;0;100 'stest#volume.space.usage.percentage'=18.62%;;0:90;0;100 'test#volume.space.usage.percentage'=100.00%;;0:90;0;100 + ... 5 --filter-counters=usage-free --critical-usage-free=:10 CRITICAL: Volume 'stest' (#1) Total: 120.00 GB Reserved: 120.00 GB Used: 22.34 GB (18.62%) Free: 97.66 GB (81.38%) | 'mtest#volume.space.free.bytes'=0B;;0:10;0;549755813888 'stest#volume.space.free.bytes'=104857600000B;;0:10;0;128849018880 'test#volume.space.free.bytes'=0B;;0:10;0;10737418240 + ... 6 --filter-counters='^usage$' --warning-usage=:10 WARNING: Volume 'mtest' (#2) Total: 512.00 GB Reserved: 512.00 GB Used: 512.00 GB (100.00%) Free: 0.00 B (0.00%) - Volume 'stest' (#1) Total: 120.00 GB Reserved: 120.00 GB Used: 22.34 GB (18.62%) Free: 97.66 GB (81.38%) - Volume 'test' (#0) Total: 10.00 GB Reserved: 10.00 GB Used: 10.00 GB (100.00%) Free: 0.00 B (0.00%) | 'mtest#volume.space.usage.bytes'=549755813888B;0:10;;0;549755813888 'stest#volume.space.usage.bytes'=23991418880B;0:10;;0;128849018880 'test#volume.space.usage.bytes'=10737418240B;0:10;;0;10737418240 diff --git a/tests/storage/hp/alletra/restapi/hpe-alletra.mockoon.json b/tests/storage/hp/alletra/restapi/hpe-alletra.mockoon.json new file mode 100644 index 000000000..a7cdf20d9 --- /dev/null +++ b/tests/storage/hp/alletra/restapi/hpe-alletra.mockoon.json @@ -0,0 +1,259 @@ +{ + "uuid": "a7e4aebb-29d2-4b2f-b5b9-342b2a4fcdd8", + "lastMigration": 33, + "name": "Alletra", + "endpointPrefix": "", + "latency": 0, + "port": 3001, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "57a9f2df-01e2-4af0-9f54-695ceb680c0e", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/capacity", + "responses": [ + { + "uuid": "17c03e91-3180-46f3-85e1-7ff4d5820319", + "body": "{\r\n \"allCapacity\": {\r\n \"totalMiB\": 175767552,\r\n \"allocated\": {\r\n \"totalAllocatedMiB\": 117614592,\r\n \"volumes\": {\r\n \"totalVolumesMiB\": 99950592,\r\n \"nonCPGsMiB\": 0,\r\n \"nonCPGUserMiB\": 0,\r\n \"nonCPGSnapshotMiB\": 0,\r\n \"nonCPGAdminMiB\": 0,\r\n \"CPGsMiB\": 99950592,\r\n \"CPGUserMiB\": 91842207,\r\n \"CPGUserUsedMiB\": 91842207,\r\n \"CPGUserUsedBulkVVMiB\": 0,\r\n \"CPGUserUnusedMiB\": 0,\r\n \"CPGSnapshotMiB\": 8108385,\r\n \"CPGSnapshotUsedMiB\": 0,\r\n \"CPGSnapshotUsedBulkVVMiB\": 0,\r\n \"CPGSnapshotUnusedMiB\": 8108385,\r\n \"CPGAdminMiB\": 1044480,\r\n \"CPGAdminUsedMiB\": 740352,\r\n \"CPGAdminUsedBulkVVMiB\": 0,\r\n \"CPGAdminUnusedMiB\": 304128,\r\n \"CPGSharedMiB\": 11078550,\r\n \"CPGPrivateMiB\": 80763657,\r\n \"CPGBasePrivateMiB\": 80763657,\r\n \"CPGBasePrivateReservedMiB\": 80763657,\r\n \"CPGBasePrivatevSphereVVolsMiB\": 0,\r\n \"CPGSnapshotPrivateMiB\": 0,\r\n \"CPGSnapshotPrivateReservedMiB\": 0,\r\n \"CPGSnapshotPrivatevSphereVVolsMiB\": 0,\r\n \"CPGFreeMiB\": 8108385,\r\n \"unmappedMiB\": 0,\r\n \"capacityEfficiency\": {\r\n \"compaction\": 2.84,\r\n \"deduplication\": 1.17,\r\n \"compression\": 1.00,\r\n \"dataReduction\": 1.58,\r\n \"overProvisioning\": 0.87\r\n }\r\n },\r\n \"system\": {\r\n \"totalSystemMiB\": 17664000,\r\n \"internalMiB\": 1972224,\r\n \"spareMiB\": 14647296,\r\n \"spareUsedMiB\": 0,\r\n \"spareUnusedMiB\": 14647296,\r\n \"adminMiB\": 1044480\r\n }\r\n },\r\n \"freeMiB\": 58152960,\r\n \"freeInitializedMiB\": 58152960,\r\n \"freeUninitializedMiB\": 0,\r\n \"unavailableCapacityMiB\": 0,\r\n \"failedCapacityMiB\": 0,\r\n \"overProvisionedVirtualSizeMiB\": 138715270,\r\n \"overProvisionedUsedMiB\": 93696444,\r\n \"overProvisionedAllocatedMiB\": 8096760,\r\n \"overProvisionedFreeMiB\": 58152960\r\n },\r\n \"FCCapacity\": {\r\n \"totalMiB\": 0,\r\n \"allocated\": {\r\n \"totalAllocatedMiB\": 0,\r\n \"volumes\": {\r\n \"totalVolumesMiB\": 0,\r\n \"nonCPGsMiB\": 0,\r\n \"nonCPGUserMiB\": 0,\r\n \"nonCPGSnapshotMiB\": 0,\r\n \"nonCPGAdminMiB\": 0,\r\n \"CPGsMiB\": 0,\r\n \"CPGUserMiB\": 0,\r\n \"CPGUserUsedMiB\": 0,\r\n \"CPGUserUsedBulkVVMiB\": 0,\r\n \"CPGUserUnusedMiB\": 0,\r\n \"CPGSnapshotMiB\": 0,\r\n \"CPGSnapshotUsedMiB\": 0,\r\n \"CPGSnapshotUsedBulkVVMiB\": 0,\r\n \"CPGSnapshotUnusedMiB\": 0,\r\n \"CPGAdminMiB\": 0,\r\n \"CPGAdminUsedMiB\": 0,\r\n \"CPGAdminUsedBulkVVMiB\": 0,\r\n \"CPGAdminUnusedMiB\": 0,\r\n \"CPGSharedMiB\": 0,\r\n \"CPGPrivateMiB\": 0,\r\n \"CPGBasePrivateMiB\": 0,\r\n \"CPGBasePrivateReservedMiB\": 0,\r\n \"CPGBasePrivatevSphereVVolsMiB\": 0,\r\n \"CPGSnapshotPrivateMiB\": 0,\r\n \"CPGSnapshotPrivateReservedMiB\": 0,\r\n \"CPGSnapshotPrivatevSphereVVolsMiB\": 0,\r\n \"CPGFreeMiB\": 0,\r\n \"unmappedMiB\": 0,\r\n \"capacityEfficiency\": {\r\n \"compaction\": 0.00,\r\n \"deduplication\": 0.00,\r\n \"dataReduction\": 0.00,\r\n \"overProvisioning\": 0.00\r\n }\r\n },\r\n \"system\": {\r\n \"totalSystemMiB\": 0,\r\n \"internalMiB\": 0,\r\n \"spareMiB\": 0,\r\n \"spareUsedMiB\": 0,\r\n \"spareUnusedMiB\": 0,\r\n \"adminMiB\": 0\r\n }\r\n },\r\n \"freeMiB\": 0,\r\n \"freeInitializedMiB\": 0,\r\n \"freeUninitializedMiB\": 0,\r\n \"unavailableCapacityMiB\": 0,\r\n \"failedCapacityMiB\": 0,\r\n \"overProvisionedVirtualSizeMiB\": 0,\r\n \"overProvisionedUsedMiB\": 0,\r\n \"overProvisionedAllocatedMiB\": 0,\r\n \"overProvisionedFreeMiB\": 0\r\n },\r\n \"NLCapacity\": {\r\n \"totalMiB\": 0,\r\n \"allocated\": {\r\n \"totalAllocatedMiB\": 0,\r\n \"volumes\": {\r\n \"totalVolumesMiB\": 0,\r\n \"nonCPGsMiB\": 0,\r\n \"nonCPGUserMiB\": 0,\r\n \"nonCPGSnapshotMiB\": 0,\r\n \"nonCPGAdminMiB\": 0,\r\n \"CPGsMiB\": 0,\r\n \"CPGUserMiB\": 0,\r\n \"CPGUserUsedMiB\": 0,\r\n \"CPGUserUsedBulkVVMiB\": 0,\r\n \"CPGUserUnusedMiB\": 0,\r\n \"CPGSnapshotMiB\": 0,\r\n \"CPGSnapshotUsedMiB\": 0,\r\n \"CPGSnapshotUsedBulkVVMiB\": 0,\r\n \"CPGSnapshotUnusedMiB\": 0,\r\n \"CPGAdminMiB\": 0,\r\n \"CPGAdminUsedMiB\": 0,\r\n \"CPGAdminUsedBulkVVMiB\": 0,\r\n \"CPGAdminUnusedMiB\": 0,\r\n \"CPGSharedMiB\": 0,\r\n \"CPGPrivateMiB\": 0,\r\n \"CPGBasePrivateMiB\": 0,\r\n \"CPGBasePrivateReservedMiB\": 0,\r\n \"CPGBasePrivatevSphereVVolsMiB\": 0,\r\n \"CPGSnapshotPrivateMiB\": 0,\r\n \"CPGSnapshotPrivateReservedMiB\": 0,\r\n \"CPGSnapshotPrivatevSphereVVolsMiB\": 0,\r\n \"CPGFreeMiB\": 0,\r\n \"unmappedMiB\": 0,\r\n \"capacityEfficiency\": {\r\n \"compaction\": 0.00,\r\n \"deduplication\": 0.00,\r\n \"dataReduction\": 0.00,\r\n \"overProvisioning\": 0.00\r\n }\r\n },\r\n \"system\": {\r\n \"totalSystemMiB\": 0,\r\n \"internalMiB\": 0,\r\n \"spareMiB\": 0,\r\n \"spareUsedMiB\": 0,\r\n \"spareUnusedMiB\": 0,\r\n \"adminMiB\": 0\r\n }\r\n },\r\n \"freeMiB\": 0,\r\n \"freeInitializedMiB\": 0,\r\n \"freeUninitializedMiB\": 0,\r\n \"unavailableCapacityMiB\": 0,\r\n \"failedCapacityMiB\": 0,\r\n \"overProvisionedVirtualSizeMiB\": 0,\r\n \"overProvisionedUsedMiB\": 0,\r\n \"overProvisionedAllocatedMiB\": 0,\r\n \"overProvisionedFreeMiB\": 0\r\n },\r\n \"SSDCapacity\": {\r\n \"totalMiB\": 175767552,\r\n \"allocated\": {\r\n \"totalAllocatedMiB\": 117614592,\r\n \"volumes\": {\r\n \"totalVolumesMiB\": 99950592,\r\n \"nonCPGsMiB\": 0,\r\n \"nonCPGUserMiB\": 0,\r\n \"nonCPGSnapshotMiB\": 0,\r\n \"nonCPGAdminMiB\": 0,\r\n \"CPGsMiB\": 99950592,\r\n \"CPGUserMiB\": 91842207,\r\n \"CPGUserUsedMiB\": 91842207,\r\n \"CPGUserUsedBulkVVMiB\": 0,\r\n \"CPGUserUnusedMiB\": 0,\r\n \"CPGSnapshotMiB\": 8108385,\r\n \"CPGSnapshotUsedMiB\": 0,\r\n \"CPGSnapshotUsedBulkVVMiB\": 0,\r\n \"CPGSnapshotUnusedMiB\": 8108385,\r\n \"CPGAdminMiB\": 1044480,\r\n \"CPGAdminUsedMiB\": 740352,\r\n \"CPGAdminUsedBulkVVMiB\": 0,\r\n \"CPGAdminUnusedMiB\": 304128,\r\n \"CPGSharedMiB\": 11078550,\r\n \"CPGPrivateMiB\": 80763657,\r\n \"CPGBasePrivateMiB\": 80763657,\r\n \"CPGBasePrivateReservedMiB\": 80763657,\r\n \"CPGBasePrivatevSphereVVolsMiB\": 0,\r\n \"CPGSnapshotPrivateMiB\": 0,\r\n \"CPGSnapshotPrivateReservedMiB\": 0,\r\n \"CPGSnapshotPrivatevSphereVVolsMiB\": 0,\r\n \"CPGFreeMiB\": 8108385,\r\n \"unmappedMiB\": 0,\r\n \"capacityEfficiency\": {\r\n \"compaction\": 2.84,\r\n \"deduplication\": 1.17,\r\n \"compression\": 1.00,\r\n \"dataReduction\": 1.58,\r\n \"overProvisioning\": 0.87\r\n }\r\n },\r\n \"system\": {\r\n \"totalSystemMiB\": 17664000,\r\n \"internalMiB\": 1972224,\r\n \"spareMiB\": 14647296,\r\n \"spareUsedMiB\": 0,\r\n \"spareUnusedMiB\": 14647296,\r\n \"adminMiB\": 1044480\r\n }\r\n },\r\n \"freeMiB\": 58152960,\r\n \"freeInitializedMiB\": 58152960,\r\n \"freeUninitializedMiB\": 0,\r\n \"unavailableCapacityMiB\": 0,\r\n \"failedCapacityMiB\": 0,\r\n \"overProvisionedVirtualSizeMiB\": 138715270,\r\n \"overProvisionedUsedMiB\": 93696444,\r\n \"overProvisionedAllocatedMiB\": 8096760,\r\n \"overProvisionedFreeMiB\": 58152960\r\n }\r\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "efc0b874-061c-40ba-9b83-8b61693b56c0", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "api/v1/credentials", + "responses": [ + { + "uuid": "d7b75a45-3639-4fbd-842f-cd3387c968de", + "body": "{\n \"key\": \"0-xxx-xxx\"\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "62dab170-be33-44b9-b2d3-3833e7039405", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/disks", + "responses": [ + { + "uuid": "2b4e6a66-69af-4c7a-a083-fd67a71551f2", + "body": "{\r\n \"total\": 12,\r\n \"members\": [\r\n {\r\n \"id\": 0,\r\n \"position\": \"1:1\",\r\n \"lastPosition\": \"1:1\",\r\n \"type\": 2,\r\n \"state\": 1,\r\n \"totalSizeMiB\": 14647296,\r\n \"freeSizeMiB\": 4846592,\r\n \"mfgCapacityGB\": 15360,\r\n \"WWN\": \"xxxx1\",\r\n \"manufacturer\": \"XXXX1\",\r\n \"model\": \"XXXXXX1\",\r\n \"serialNumber\": \"XXXXX1\",\r\n \"fwVersion\": \"3P03\",\r\n \"protocol\": 4,\r\n \"mediaType\": 3,\r\n \"admissionTime\": \"2025-04-08T11:14:23+02:00\",\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/disks/XXXXX\",\r\n \"rel\": \"self\"\r\n }\r\n ]\r\n },\r\n {\r\n \"id\": 1,\r\n \"position\": \"1:2\",\r\n \"lastPosition\": \"1:2\",\r\n \"type\": 3,\r\n \"state\": 4,\r\n \"totalSizeMiB\": 14647296,\r\n \"freeSizeMiB\": 4846592,\r\n \"mfgCapacityGB\": 15360,\r\n \"WWN\": \"XXXX2\",\r\n \"manufacturer\": \"XXXX2\",\r\n \"model\": \"XXXX2\",\r\n \"serialNumber\": \"XXXX2\",\r\n \"fwVersion\": \"XXXX2\",\r\n \"protocol\": 4,\r\n \"mediaType\": 3,\r\n \"admissionTime\": \"2024-04-08T11:14:23+02:00\",\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/disks/XXXXX\",\r\n \"rel\": \"self\"\r\n }\r\n ]\r\n },\r\n {\r\n \"id\": 2,\r\n \"position\": \"1:3\",\r\n \"lastPosition\": \"1:3\",\r\n \"type\": 3,\r\n \"state\": 2,\r\n \"totalSizeMiB\": 14647296,\r\n \"freeSizeMiB\": 4846592,\r\n \"mfgCapacityGB\": 15360,\r\n \"WWN\": \"XXXXX3\",\r\n \"manufacturer\": \"XXX3\",\r\n \"model\": \"XXX3\",\r\n \"serialNumber\": \"XXX3\",\r\n \"fwVersion\": \"XXX3\",\r\n \"protocol\": 2,\r\n \"mediaType\": 3,\r\n \"admissionTime\": \"2024-04-08T11:14:23+02:00\",\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/disks/XXXXX\",\r\n \"rel\": \"self\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/disks\",\r\n \"rel\": \"self\"\r\n }\r\n ]\r\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "2badd267-3152-4db3-8845-3d64d2abfcc9", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/system", + "responses": [ + { + "uuid": "dea39363-53cd-4ac0-98f0-24aa52b0e192", + "body": "{\r\n \"id\": 17,\r\n \"name\": \"Alletra_XXX\",\r\n \"systemVersion\": \"1.3.1.9\",\r\n \"IPv4Addr\": \"****\",\r\n \"model\": \"HPE Alletra Storage MP\",\r\n \"serialNumber\": \"****\",\r\n \"totalNodes\": 2,\r\n \"masterNode\": 0,\r\n \"onlineNodes\": [\r\n 0,\r\n 1\r\n ],\r\n \"clusterNodes\": [\r\n 0,\r\n 1\r\n ],\r\n \"chunkletSizeMiB\": 1024,\r\n \"totalCapacityMiB\": 175767553.0,\r\n \"allocatedCapacityMiB\": 117602304.0,\r\n \"freeCapacityMiB\": 58165248.0,\r\n \"failedCapacityMiB\": 0.0,\r\n \"readOnlyParameters\": {\r\n \"maxVolumeSizeMiB\": 67108864,\r\n \"maxDedupVolumeSizeMiB\": 67108864,\r\n \"maxCompressedVolumeSizeMiB\": 67108864\r\n },\r\n \"contact\": \"****\",\r\n \"timeZone\": \"Europe/Rome\",\r\n \"licenseInfo\": {\r\n \"issueTimeSec\": 1709726712,\r\n \"issueTime8601\": \"2024-03-06T13:05:12+01:00\",\r\n \"diskCount\": -1,\r\n \"WWNBASE\": 177580,\r\n \"licenses\": [\r\n {\r\n \"name\": \"LICENSE 1\"\r\n },\r\n {\r\n \"name\": \"LICENSE 2\",\r\n \"expiryTimeSec\": 1000,\r\n \"expiryTime8601\": \"2020-04-20T02:00:00+02:00\"\r\n },\r\n {\r\n \"name\": \"LICENSE 3\",\r\n \"expiryTimeSec\": 1871337600,\r\n \"expiryTime8601\": \"2029-04-20T02:00:00+02:00\"\r\n },\r\n {\r\n \"name\": \"LICENSE 4\"\r\n },\r\n {\r\n \"name\": \"LICENSE 5\",\r\n \"expiryTimeSec\": 1871337600,\r\n \"expiryTime8601\": \"2029-04-20T02:00:00+02:00\"\r\n },\r\n {\r\n \"name\": \"LICENSE 6\",\r\n \"expiryTimeSec\": 1871337600,\r\n \"expiryTime8601\": \"2029-04-20T02:00:00+02:00\"\r\n },\r\n {\r\n \"name\": \"LICENSE 7\"\r\n },\r\n {\r\n \"name\": \"LICENSE 8\",\r\n \"expiryTimeSec\": 1871337600,\r\n \"expiryTime8601\": \"2029-04-20T02:00:00+02:00\"\r\n },\r\n {\r\n \"name\": \"LICENSE 9\",\r\n \"expiryTimeSec\": 1871337600,\r\n \"expiryTime8601\": \"2029-04-20T02:00:00+02:00\"\r\n }\r\n ],\r\n \"licenseState\": {\r\n \"virtualCopy\": true,\r\n \"remoteCopy\": true,\r\n \"thinProvisioing\": true,\r\n \"thinProvisioning\": true,\r\n \"domains\": true,\r\n \"dynamicOptimization\": true,\r\n \"virtualLock\": true,\r\n \"thinPersistence\": true,\r\n \"thinConversion\": true,\r\n \"adaptiveOptimization\": false,\r\n \"peerVirtualization\": true,\r\n \"qos\": true,\r\n \"systemReporter\": true,\r\n \"darEncryption\": true,\r\n \"fileServices\": false,\r\n \"storageFederation\": false,\r\n \"onlineImport\": true,\r\n \"rmcApplicationSuite\": true,\r\n \"smartSAN\": true,\r\n \"sedEncryption\": false\r\n }\r\n },\r\n \"parameters\": {\r\n \"rawSpaceAlertSSD\": 0,\r\n \"remoteSyslog\": true,\r\n \"remoteSyslogHost\": \"***514\",\r\n \"remoteSyslogSecurityHost\": \"***:514\",\r\n \"sparingAlgorithm\": \"Minimal\",\r\n \"eventLogsize\": 4194304,\r\n \"eventLogNum\": 10,\r\n \"VVRetentionTimeMax\": 1209600,\r\n \"upgradeNote\": \"\",\r\n \"autoExportAfterReboot\": true,\r\n \"thermalShutdown\": true,\r\n \"sessionTimeout\": 3600,\r\n \"overProvRatioLimit\": 0.0,\r\n \"overProvRatioWarning\": 0.0,\r\n \"disableChunkletInitUNMAP\": false,\r\n \"remoteCopyHostThrottling\": false,\r\n \"autoAdmitTune\": true,\r\n \"singleLunHost\": false,\r\n \"allowDomainUsersAffectNoDomain\": 2,\r\n \"enableAIQoS\": false\r\n }\r\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "d0232ee2-9596-4a82-8879-2529d7b6ffa1", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/volumes", + "responses": [ + { + "uuid": "bff66b93-5f69-440b-9d11-51bdde804653", + "body": "{\r\n \"total\": 3,\r\n \"members\": [\r\n {\r\n \"id\": 0,\r\n \"name\": \"test\",\r\n \"deduplicationState\": 3,\r\n \"compressionState\": 4,\r\n \"provisioningType\": 1,\r\n \"copyType\": 1,\r\n \"baseId\": 0,\r\n \"readOnly\": false,\r\n \"state\": 1,\r\n \"failedStates\": [],\r\n \"degradedStates\": [],\r\n \"additionalStates\": [],\r\n \"totalReservedMiB\": 10240,\r\n \"totalUsedMiB\": 10240,\r\n \"sizeMiB\": 10240,\r\n \"wwn\": \"AAA1\",\r\n \"nguid\": \"AAAAA1\",\r\n \"creationTimeSec\": 1712567685,\r\n \"creationTime8601\": \"2024-04-07T11:14:45+02:00\",\r\n \"usrSpcAllocWarningPct\": 0,\r\n \"usrSpcAllocLimitPct\": 0,\r\n \"policies\": {\r\n \"staleSS\": true,\r\n \"oneHost\": false,\r\n \"zeroDetect\": false,\r\n \"system\": true,\r\n \"caching\": true\r\n },\r\n \"userCPG\": \"AAAAA_r6\",\r\n \"uuid\": \"ZAZZAZZA\",\r\n \"udid\": 0,\r\n \"rcopyStatus\": 1,\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumes/admin\",\r\n \"rel\": \"self\"\r\n },\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumespacedistribution/admin\",\r\n \"rel\": \"volumeSpaceDistribution\"\r\n }\r\n ]\r\n },\r\n {\r\n \"id\": 1,\r\n \"name\": \"stest\",\r\n \"deduplicationState\": 3,\r\n \"compressionState\": 4,\r\n \"provisioningType\": 1,\r\n \"copyType\": 1,\r\n \"baseId\": 1,\r\n \"readOnly\": false,\r\n \"state\": 2,\r\n \"failedStates\": [],\r\n \"degradedStates\": [],\r\n \"additionalStates\": [],\r\n \"totalReservedMiB\": 122880,\r\n \"totalUsedMiB\": 22880,\r\n \"sizeMiB\": 122880,\r\n \"wwn\": \"BBBB2\",\r\n \"nguid\": \"BBBBBB2\",\r\n \"creationTimeSec\": 1712567696,\r\n \"creationTime8601\": \"2024-04-08T11:24:56+02:00\",\r\n \"usrSpcAllocWarningPct\": 0,\r\n \"usrSpcAllocLimitPct\": 0,\r\n \"policies\": {\r\n \"staleSS\": true,\r\n \"oneHost\": false,\r\n \"zeroDetect\": false,\r\n \"system\": true,\r\n \"caching\": true\r\n },\r\n \"userCPG\": \"BBB_r6\",\r\n \"uuid\": \"SFFDSDDSDSD\",\r\n \"udid\": 1,\r\n \"rcopyStatus\": 1,\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumes/stest\",\r\n \"rel\": \"self\"\r\n },\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumespacedistribution/stest\",\r\n \"rel\": \"volumeSpaceDistribution\"\r\n }\r\n ]\r\n },\r\n {\r\n \"id\": 2,\r\n \"name\": \"mtest\",\r\n \"deduplicationState\": 3,\r\n \"compressionState\": 4,\r\n \"provisioningType\": 1,\r\n \"copyType\": 1,\r\n \"baseId\": 2,\r\n \"readOnly\": false,\r\n \"state\": 4,\r\n \"failedStates\": [],\r\n \"degradedStates\": [],\r\n \"additionalStates\": [],\r\n \"totalReservedMiB\": 524288,\r\n \"totalUsedMiB\": 524288,\r\n \"sizeMiB\": 524288,\r\n \"wwn\": \"CCCCCC3\",\r\n \"nguid\": \"CCCCC3\",\r\n \"creationTimeSec\": 1712567697,\r\n \"creationTime8601\": \"2024-04-08T12:14:57+02:00\",\r\n \"usrSpcAllocWarningPct\": 0,\r\n \"usrSpcAllocLimitPct\": 0,\r\n \"policies\": {\r\n \"staleSS\": true,\r\n \"oneHost\": false,\r\n \"zeroDetect\": false,\r\n \"system\": true,\r\n \"caching\": true\r\n },\r\n \"userCPG\": \"CCC_r6\",\r\n \"uuid\": \"AEZDSSFDDSD\",\r\n \"udid\": 2,\r\n \"rcopyStatus\": 1,\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumes/mtest\",\r\n \"rel\": \"self\"\r\n },\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumespacedistribution/mtest\",\r\n \"rel\": \"volumeSpaceDistribution\"\r\n }\r\n ]\r\n }\r\n ],\r\n \"links\": [\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumes\",\r\n \"rel\": \"self\"\r\n },\r\n {\r\n \"href\": \"https://127.0.0.1/api/v1/volumespacedistribution\",\r\n \"rel\": \"volumeSpaceDistribution\"\r\n }\r\n ]\r\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "57a9f2df-01e2-4af0-9f54-695ceb680c0e" + }, + { + "type": "route", + "uuid": "efc0b874-061c-40ba-9b83-8b61693b56c0" + }, + { + "type": "route", + "uuid": "62dab170-be33-44b9-b2d3-3833e7039405" + }, + { + "type": "route", + "uuid": "2badd267-3152-4db3-8845-3d64d2abfcc9" + }, + { + "type": "route", + "uuid": "d0232ee2-9596-4a82-8879-2529d7b6ffa1" + } + ], + "proxyMode": false, + "proxyHost": "", + "proxyRemovePrefix": false, + "tlsOptions": { + "enabled": false, + "type": "CERT", + "pfxPath": "", + "certPath": "", + "keyPath": "", + "caPath": "", + "passphrase": "" + }, + "cors": true, + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Access-Control-Allow-Methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + } + ], + "proxyReqHeaders": [ + { + "key": "", + "value": "" + } + ], + "proxyResHeaders": [ + { + "key": "", + "value": "" + } + ], + "data": [], + "callbacks": [] +} \ No newline at end of file