CTOR-880: New Pack(storage::hp::alletra::restapi) (#5653)

Co-authored-by: Roman Morandell <46994680+rmorandell-pgum@users.noreply.github.com>
This commit is contained in:
scresto 2025-07-04 17:50:12 +02:00 committed by GitHub
parent e04e5519b5
commit 4565304d18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 3294 additions and 0 deletions

View File

@ -0,0 +1,4 @@
{
"dependencies": [
]
}

View File

@ -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/"
]
}

View File

@ -0,0 +1,4 @@
{
"dependencies": [
]
}

View File

@ -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<custom>.
=cut

View File

@ -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<allCapacity>, C<FCCapacity>, C<SSDCapacity> and C<NLCapacity>.
=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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -13,6 +13,7 @@ ACS
ADSL
Alcatel
allCapacity
Alletra
allsteps
Ansible
--api-filter-orgs

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because one or more lines are too long