enh(backbox): add service discovery list-devices + new device-backup mode (#5381)

REFS: CTOR-870
This commit is contained in:
sdepassio 2025-01-10 13:24:57 +01:00 committed by GitHub
parent 4dfdfb8158
commit 046a383192
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 471 additions and 11 deletions

View File

@ -149,10 +149,11 @@ sub request {
$self->settings();
my $content = $self->{http}->request(
method => 'GET',
method => $options{method},
url_path => $endpoint,
get_param => $options{get_param},
header => [
'Accept: application/json',
'AUTH: ' . $self->{api_token}
],
warning_status => '',
@ -163,9 +164,11 @@ sub request {
# Maybe there is an issue with the token. So we retry.
if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) {
$content = $self->{http}->request(
method => $options{method},
url_path => $endpoint,
get_param => $options{get_param},
header => [
'Accept: application/json',
'AUTH: ' . $self->{api_token}
],
unknown_status => $self->{unknown_http_status},
@ -179,7 +182,6 @@ sub request {
$self->{output}->add_option_msg(short_msg => 'Error while retrieving data (add --debug option for detailed message)');
$self->{output}->option_exit();
}
return $decoded;
}
@ -190,7 +192,8 @@ sub get_backup_jobs_status {
if (!centreon::plugins::misc::is_empty($options{filter_type})) {
$endpoint .= '/' . $options{filter_type};
}
return $self->request(endpoint => $endpoint);
return $self->request(method => 'GET',
endpoint => $endpoint);
}
sub get_config_status {
@ -200,7 +203,8 @@ sub get_config_status {
if (!centreon::plugins::misc::is_empty($options{filter_type})) {
$endpoint .= '/' . $options{filter_type};
}
return $self->request(endpoint => $endpoint);
return $self->request(method => 'GET',
endpoint => $endpoint);
}
sub get_intelli_check_status {
@ -213,7 +217,49 @@ sub get_intelli_check_status {
if (!centreon::plugins::misc::is_empty($options{report_id})) {
$endpoint .= '/' . $options{report_id};
}
return $self->request(endpoint => $endpoint);
return $self->request(method => 'GET',
endpoint => $endpoint);
}
sub get_devices {
my ($self, %options) = @_;
my $json_decode = 1;
if (defined($options{json_decode})) {
$json_decode = $options{json_decode};
}
return $self->request(method => 'GET',
endpoint => 'devices');
}
sub get_device_id_from_name {
my ($self, %options) = @_;
my $devices = $self->get_devices();
for my $device (@$devices) {
if ($device->{deviceName} eq $options{device_name}) {
return $device->{deviceId};
}
}
}
sub get_devices_backups_status {
my ($self, %options) = @_;
return $self->request(method => 'POST',
endpoint => 'devices/dynamic');
}
sub get_device_backup_status {
my ($self, %options) = @_;
my $backups = $self->get_devices_backups_status();
for my $backup (@$backups) {
if ($backup->{deviceId} == $options{device_id}) {
return $backup;
}
}
}
1;

View File

@ -24,7 +24,6 @@ use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub prefix_backup_output {
my ($self, %options) = @_;

View File

@ -24,7 +24,6 @@ use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub set_counters {
my ($self, %options) = @_;

View File

@ -0,0 +1,143 @@
#
# Copyright 2024 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 network::backbox::restapi::mode::devicebackup;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub custom_status_output {
my ($self, %options) = @_;
my $msg = sprintf("Device [id: %s] [name: %s] [status: %s] [status reason: %s]",
$self->{result_values}->{device_id},
$self->{result_values}->{device_name},
$self->{result_values}->{status},
$self->{result_values}->{status_reason});
return $msg;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'backup', type => 0 },
];
$self->{maps_counters}->{backup} = [
{ label => 'status',
type => 2,
warning_default => '%{status} =~ /SUSPECT/i',
critical_default => '%{status} =~ /FAILURE/i',
set => {
key_values => [ { name => 'device_id' },
{ name => 'device_name' },
{ name => 'status' },
{ name => 'status_reason' }
],
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 => {
'device-id:s' => { name => 'device_id' },
'device-name:s' => { name => 'device_name' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if (centreon::plugins::misc::is_empty($self->{option_results}->{device_id}) && centreon::plugins::misc::is_empty($self->{option_results}->{device_name})) {
$self->{output}->add_option_msg(short_msg => "Need to specify --device-id or --device-name option.");
$self->{output}->option_exit();
}
}
sub manage_selection {
my ($self, %options) = @_;
my $device_id = $self->{option_results}->{device_id};
my $device_name = $self->{option_results}->{device_name} || '';
if (centreon::plugins::misc::is_empty($device_id)) {
$device_id = $options{custom}->get_device_id_from_name(device_name => $device_name);
}
my $backup = $options{custom}->get_device_backup_status(device_id => $device_id);
if (centreon::plugins::misc::is_empty($backup)) {
$self->{output}->add_option_msg(short_msg => "No backup found for device id '" . $device_id . "'.");
$self->{output}->option_exit();
}
$self->{backup} = { device_id => $device_id,
device_name => $device_name,
status => $backup->{historyStatus},
status_reason => $backup->{statusReason}
};
}
1;
__END__
=head1 MODE
Check a device backup on BackBox.
=over 8
=item B<--device-id>
ID of the device (if you prefer to use the ID instead of the name).
ID or name is mandatory.
=item B<--device-name>
Name of the device (if you prefer to use the name instead of the ID).
ID or name is mandatory. If you specify both, the ID will be used.
=item B<--warning-status>
Set warning threshold for status (Default: '%{status} =~ /SUSPECT/i').
You can use the following variables: %{status}, %{status_reason}, %{device_name}, %{device_id}.
=item B<--critical-status>
Set critical threshold for status (Default: '%{status} =~ /FAILURE/i').
You can use the following variables: %{status}, %{status_reason}, %{device_name}, %{device_id}.
=back
=cut

View File

@ -24,7 +24,6 @@ use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub prefix_intellicheck_output {
my ($self, %options) = @_;

View File

@ -0,0 +1,121 @@
#
# Copyright 2024 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 network::backbox::restapi::mode::listdevices;
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;
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
}
sub run {
my ($self, %options) = @_;
my $jsondevices = $options{custom}->get_devices();
$self->{devices} = [];
for my $jsondevice (@{$jsondevices}) {
my $device = {
id => $jsondevice->{deviceId},
name => $jsondevice->{deviceName},
description => defined($jsondevice->{description}) ? $jsondevice->{description} : '',
site => defined($jsondevice->{siteName}) ? $jsondevice->{siteName} : '',
group => defined($jsondevice->{groupName}) ? $jsondevice->{groupName} : '',
vendor => defined($jsondevice->{vendorName}) ? $jsondevice->{vendorName} : '',
product => defined($jsondevice->{productName}) ? $jsondevice->{productName} : '',
product_type => defined($jsondevice->{productTypeName}) ? $jsondevice->{productTypeName} : '',
version => defined($jsondevice->{versionName}) ? $jsondevice->{versionName} : ''
};
push @{$self->{devices}}, $device;
$self->{output}->output_add(
long_msg => sprintf(
"[id: %s][name: %s][description: %s][site: %s][group: %s][vendor: %s][product: %s][product_type: %s][version: %s]",
$device->{id},
$device->{name},
$device->{description},
$device->{site},
$device->{group},
$device->{vendor},
$device->{product},
$device->{product_type},
$device->{version}
)
);
}
if (!defined($options{disco_show})) {
$self->{output}->output_add(severity => 'OK', short_msg => 'Devices:');
$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', 'description', 'site', 'group', 'vendor', 'product', 'product_type', 'version' ]);
}
sub disco_show {
my ($self, %options) = @_;
$options{disco_show} = 1;
$self->run(%options);
for my $device (@{$self->{devices}}) {
$self->{output}->add_disco_entry(
id => $device->{id},
name => $device->{name},
description => $device->{description},
site => $device->{site},
group => $device->{group},
vendor => $device->{vendor},
product => $device->{product},
product_type => $device->{product_type},
version => $device->{version}
);
}
}
1;
__END__
=head1 MODE
List devices using the Backbox REST API.
=back
=cut

View File

@ -31,9 +31,11 @@ sub new {
$self->{version} = '1.0';
$self->{modes} = {
'backup' => 'network::backbox::restapi::mode::backup',
'configstatus' => 'network::backbox::restapi::mode::configstatus',
'intellicheck' => 'network::backbox::restapi::mode::intellicheck'
'backup' => 'network::backbox::restapi::mode::backup',
'device-backup' => 'network::backbox::restapi::mode::devicebackup',
'configstatus' => 'network::backbox::restapi::mode::configstatus',
'intellicheck' => 'network::backbox::restapi::mode::intellicheck',
'list-devices' => 'network::backbox::restapi::mode::listdevices'
};
$self->{custom_modes}->{api} = 'network::backbox::restapi::custom::api';

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,48 @@
*** Settings ***
Documentation Check a device backup status
Resource ${CURDIR}${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}backbox.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${cmd} ${CENTREON_PLUGINS}
... --plugin=network::backbox::restapi::plugin
... --custommode=api
... --hostname=${HOSTNAME}
... --port=${APIPORT}
... --proto=http
... --api-token=token
... --mode=device-backup
*** Test Cases ***
Device backup ${tc}
[Documentation] Check a device backup status
[Tags] network backbox restapi backup
${command} Catenate
... ${cmd}
... ${extraoptions}
Log ${cmd}
Ctn Run Command And Check Result As Strings ${command} ${expected_result}
Examples: tc extraoptions expected_result --
... 1 ${EMPTY} UNKNOWN: Need to specify --device-id or --device-name option.
... 2 --device-name="Juniper SRX" OK: Device [id: 4] [name: Juniper SRX] [status: SUCCESS] [status reason: ]
... 3 --device-id=4 OK: Device [id: 4] [name: ] [status: SUCCESS] [status reason: ]
... 4 --device-name="Juniper SRX" --device-id=4 OK: Device [id: 4] [name: Juniper SRX] [status: SUCCESS] [status reason: ]
... 5 --device-name="Cisco C8000" CRITICAL: Device [id: 1] [name: Cisco C8000] [status: FAILURE] [status reason: The failed expected result was found: (No route to host)]
... 6 --device-id=1 CRITICAL: Device [id: 1] [name: ] [status: FAILURE] [status reason: The failed expected result was found: (No route to host)]
... 7 --device-name="Fortinet Fortigate" WARNING: Device [id: 3] [name: Fortinet Fortigate] [status: SUSPECT] [status reason: Unknown status]
... 8 --device-id=3 WARNING: Device [id: 3] [name: ] [status: SUSPECT] [status reason: Unknown status]
... 9 --device-id=3 --critical-status='\\\%{status} =~ /FAILURE|SUSPECT/i' CRITICAL: Device [id: 3] [name: ] [status: SUSPECT] [status reason: Unknown status]
... 10 --device-id=1 --warning-status='\\\%{status} =~ /FAILURE|SUSPECT/i' --critical-status='' WARNING: Device [id: 1] [name: ] [status: FAILURE] [status reason: The failed expected result was found: (No route to host)]
... 11 --device-id=3 --critical-status='\\\%{status_reason} =~ /unknown/i' CRITICAL: Device [id: 3] [name: ] [status: SUSPECT] [status reason: Unknown status]
... 12 --device-name="Fortinet Fortigate" --critical-status='\\\%{device_name} =~ /Fortinet/i' CRITICAL: Device [id: 3] [name: Fortinet Fortigate] [status: SUSPECT] [status reason: Unknown status]
... 13 --device-name="Fortinet Fortigate" --critical-status='\\\%{device_id} == 3' CRITICAL: Device [id: 3] [name: Fortinet Fortigate] [status: SUSPECT] [status reason: Unknown status]

View File

@ -0,0 +1,37 @@
*** Settings ***
Documentation Test the Backbox list-devices mode
Resource ${CURDIR}${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}backbox.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${cmd} ${CENTREON_PLUGINS}
... --plugin=network::backbox::restapi::plugin
... --custommode=api
... --hostname=${HOSTNAME}
... --port=${APIPORT}
... --proto=http
... --api-token=token
... --mode=list-devices
*** Test Cases ***
List-Devices ${tc}
[Documentation] Check list-devices results
[Tags] network backbox restapi list-devices
${command} Catenate
... ${cmd}
... ${extraoptions}
Ctn Run Command And Check Result As Regexp ${command} ${expected_result}
Examples: tc extraoptions expected_result --
... 1 ${EMPTY} ^Devices: (\\\\n\\\\[.*\\\\]){5}\\\\Z
... 2 --disco-show \\\\<\\\\?xml version="1.0" encoding="utf-8"\\\\?\\\\>\\\\n\\\\<data\\\\>(\\\\n\\\\s*\\\\<label .*\\\\/\\\\>){5}\\\\n\\\\<\\\\/data\\\\>
... 3 --disco-format \\\\<\\\\?xml version="1.0" encoding="utf-8"\\\\?\\\\>\\\\n\\\\<data\\\\>(\\\\n\\\\s*\\\\<element\\\\>.*\\\\<\\\\/element\\\\>){9}\\\\n\\\\<\\\\/data\\\\>