From 35d73e19cda4331627f2e5a8fbf021353ee1279d Mon Sep 17 00:00:00 2001 From: omercier Date: Mon, 14 Oct 2024 16:10:01 +0200 Subject: [PATCH] feat(vmware8-esx): new plugin to monitor ESX status using vSphere 8 API --- .../deb.json | 5 + .../pkg.json | 10 + .../rpm.json | 5 + src/apps/vmware/vsphere8/custom/api.pm | 406 +++++++++++++ .../vmware/vsphere8/esx/mode/discovery.pm | 101 ++++ .../vmware/vsphere8/esx/mode/hoststatus.pm | 172 ++++++ src/apps/vmware/vsphere8/esx/plugin.pm | 50 ++ src/centreon/plugins/misc.pm | 541 ++++++++++++++++++ src/centreon/plugins/statefile.pm | 185 +++++- tests/apps/vmware/vsphere8/api.t | 114 ++++ .../apps/vmware/vsphere8/esx/discovery.robot | 42 ++ .../vmware/vsphere8/esx/host-status.robot | 51 ++ .../vsphere8/esx/vmware8-restapi.mockoon.json | 183 ++++++ tests/centreon/plugins/statefile.t | 84 +++ tests/resources/spellcheck/stopwords.txt | 9 + 15 files changed, 1954 insertions(+), 4 deletions(-) create mode 100644 packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/deb.json create mode 100644 packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/pkg.json create mode 100644 packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/rpm.json create mode 100644 src/apps/vmware/vsphere8/custom/api.pm create mode 100644 src/apps/vmware/vsphere8/esx/mode/discovery.pm create mode 100644 src/apps/vmware/vsphere8/esx/mode/hoststatus.pm create mode 100644 src/apps/vmware/vsphere8/esx/plugin.pm create mode 100644 tests/apps/vmware/vsphere8/api.t create mode 100644 tests/apps/vmware/vsphere8/esx/discovery.robot create mode 100644 tests/apps/vmware/vsphere8/esx/host-status.robot create mode 100644 tests/apps/vmware/vsphere8/esx/vmware8-restapi.mockoon.json create mode 100644 tests/centreon/plugins/statefile.t diff --git a/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/deb.json b/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/deb.json new file mode 100644 index 0000000000..540c269f3b --- /dev/null +++ b/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/deb.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "libjson-perl" + ] +} diff --git a/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/pkg.json b/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/pkg.json new file mode 100644 index 0000000000..5b83baae93 --- /dev/null +++ b/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/pkg.json @@ -0,0 +1,10 @@ +{ + "pkg_name": "centreon-plugin-Virtualization-Vmware8-Esx-Restapi", + "pkg_summary": "Centreon Plugin to monitor VMware ESX physical servers using vSphere 8 REST API", + "plugin_name": "centreon_vmware8_esx_restapi.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "apps/vmware/vsphere8/esx", + "apps/vmware/vsphere8/custom" + ] +} diff --git a/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/rpm.json b/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/rpm.json new file mode 100644 index 0000000000..1869614605 --- /dev/null +++ b/packaging/centreon-plugin-Virtualization-Vmware8-Esx-Restapi/rpm.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "perl(JSON)" + ] +} diff --git a/src/apps/vmware/vsphere8/custom/api.pm b/src/apps/vmware/vsphere8/custom/api.pm new file mode 100644 index 0000000000..45754509ea --- /dev/null +++ b/src/apps/vmware/vsphere8/custom/api.pm @@ -0,0 +1,406 @@ +# +# 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 apps::vmware::vsphere8::custom::api; + +use strict; +use warnings; +use centreon::plugins::http; +use centreon::plugins::statefile; +use JSON::XS; +use MIME::Base64; +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 => { + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port' }, + 'proto:s' => { name => 'proto' }, + 'username:s' => { name => 'username' }, + 'password:s' => { name => 'password' }, + 'timeout:s' => { name => 'timeout' } + } + ); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST 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) = @_; + + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : ''; + $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443; + $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10; + $self->{username} = (defined($self->{option_results}->{username})) ? $self->{option_results}->{username} : ''; + $self->{password} = (defined($self->{option_results}->{password})) ? $self->{option_results}->{password} : ''; + #$self->{http_backend} = (defined($self->{option_results}->{http_backend})) ? $self->{option_results}->{http_backend} : 'curl'; + + if ($self->{hostname} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --hostname option."); + $self->{output}->option_exit(); + } + if (!defined($self->{username}) || $self->{username} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --username option."); + $self->{output}->option_exit(); + } + if (!defined($self->{password}) || $self->{password} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --password option."); + $self->{output}->option_exit(); + } + + $self->{cache}->check_options(option_results => $self->{option_results}); + + return 0; +} + +sub build_options_for_httplib { + my ($self, %options) = @_; + + $self->{option_results}->{hostname} = $self->{hostname}; + $self->{option_results}->{timeout} = $self->{timeout}; + $self->{option_results}->{port} = $self->{port}; + $self->{option_results}->{proto} = $self->{proto}; + # add options of this api + $self->{option_results}->{username} = $self->{username}; + $self->{option_results}->{password} = $self->{password}; + $self->{option_results}->{warning_status} = ''; + $self->{option_results}->{critical_status} = ''; + $self->{option_results}->{unknown_status} = $self->{http_unknown_status}; +} + +sub settings { + my ($self, %options) = @_; + + return 1 if (defined($self->{settings_done})); + $self->build_options_for_httplib(); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->set_options(%{$self->{option_results}}); + $self->{settings_done} = 1; + + return 1; +} + +sub get_token { + my ($self, %options) = @_; + + my $has_cache_file = $self->{cache}->read( + statefile => 'vsphere8_api_' . md5_hex( + $self->{hostname} + . ':' . $self->{port} + . '_' . $self->{username}) + ); + my $token = $self->{cache}->get(name => 'token'); + + if ( + $has_cache_file == 0 + || !defined($token) + || $options{force_authentication} + ) { + my $auth_string = MIME::Base64::encode_base64($self->{username} . ':' . $self->{password}); + + $self->settings(); + my $content = $self->{http}->request( + method => 'POST', + url_path => '/api/session', + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status}, + header => [ + 'Content-Type: application/json', + 'Authorization: Basic ' . $auth_string + ] + ); + + $content =~ s/^"(.*)"$/$1/; + $token = $content; + + my $data = { + updated => time(), + token => $token + }; + $self->{cache}->write(data => $data); + } + + return $token; +} + +sub try_request_api { + my ($self, %options) = @_; + + my $token = $self->get_token(%options); + my ($content) = $self->{http}->request( + url_path => '/api' . $options{endpoint}, + get_param => $options{get_param}, + header => [ 'vmware-api-session-id: ' . $token ], + unknown_status => '', + insecure => (defined($self->{option_results}->{insecure}) ? 1 : 0) + + ); + + 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; +} + +sub request_api { + my ($self, %options) = @_; + + $self->settings(); + my $api_response = $self->try_request_api(%options); + + # if the token is invalid, we try to authenticate again + if (ref($api_response) eq 'HASH' + && defined($api_response->{error_type}) + && $api_response->{error_type} eq 'UNAUTHENTICATED') { + $api_response = $self->try_request_api('force_authentication' => 1, %options); + } + # if we could not authenticate, we exit + if (ref($api_response) eq 'HASH' && defined($api_response->{error_type})) { + my $full_message = ''; + for my $error_item (@{$api_response->{messages}}) { + $full_message .= '[Id: ' . $error_item->{id} . ' - Msg: ' . $error_item->{default_message} . ' (' . join(', ', @{$error_item->{args}}) . ')]'; + } + $self->{output}->add_option_msg(short_msg => "API returns error of type " . $api_response->{error_type} . ": " . $full_message); + $self->{output}->option_exit(); + } + return $api_response; +} + +1; +__END__ + +=head1 NAME + +apps::vmware::vsphere8::custom::api - Custom module for VMware vSphere 8 API. + +=head1 SYNOPSIS + + use apps::vmware::vsphere8::custom::api; + + my $api = apps::vmware::vsphere8::custom::api->new( + output => $output, + options => $options + ); + + $api->set_options(option_results => $option_results); + $api->check_options(); + my $response = $api->request_api(endpoint => '/vcenter/host'); + +=head1 DESCRIPTION + +This module provides methods to interact with the VMware vSphere 8 REST API. It handles authentication, caching, and API requests. + +=head1 METHODS + +=head2 new + + my $api = apps::vmware::vsphere8::custom::api->new(%options); + +Creates a new `apps::vmware::vsphere8::custom::api` object. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - An output object for messages. + +=item * C - An options object for adding command-line options. + +=back + +=back + +=head2 set_options + + $api->set_options(option_results => $option_results); + +Sets the options for the API module. + +=over 4 + +=item * C - A hash of option results. + +=back + +=head2 set_defaults + + $api->set_defaults(); + +Sets the default options for the API module. + +=head2 check_options + + $api->check_options(); + +Checks and processes the provided options. + +=head2 build_options_for_httplib + + $api->build_options_for_httplib(); + +Builds the options for the HTTP library. + +=head2 settings + + $api->settings(); + +Configures the HTTP settings for the API requests. + +=head2 get_token + + my $token = $api->get_token(%options); + +Retrieves the authentication token from the cache or requests a new one if necessary. + +=over 4 + +=item * C<%options> - A hash of options. + +=back + +=head2 try_request_api + + my $response = $api->try_request_api(%options); + +Attempts to make an API request with the provided options. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The API endpoint to request. + +=item * C - Additional GET parameters for the request. + +=item * C - Force re-authentication if set to true. + +=back + +=back + +=head2 request_api + + my $response = $api->request_api(%options); + +Calls try_request_api and recalls it forcing authentication if the first call fails. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The API endpoint to request. + +=item * C - Additional GET parameters for the request. + +=back + +=back + +=head1 REST API OPTIONS + +Command-line options for VMware vSphere 8 API: + +=over 8 + +=item B<--hostname> + +Define the hostname of the vSphere server. + +=item B<--port> + +Define the port of the vSphere server (default: 443). + +=item B<--proto> + +Define the protocol to use (default: https). + +=item B<--username> + +Define the username for authentication. + +=item B<--password> + +Define the password for authentication. + +=item B<--timeout> + +Define the timeout for API requests (default: 10 seconds). + +=back + +=head1 AUTHOR + +Centreon + +=head1 LICENSE + +Licensed under the Apache License, Version 2.0. + +=cut \ No newline at end of file diff --git a/src/apps/vmware/vsphere8/esx/mode/discovery.pm b/src/apps/vmware/vsphere8/esx/mode/discovery.pm new file mode 100644 index 0000000000..52c3643b7d --- /dev/null +++ b/src/apps/vmware/vsphere8/esx/mode/discovery.pm @@ -0,0 +1,101 @@ +# +# 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 apps::vmware::vsphere8::esx::mode::discovery; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use JSON::XS; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'prettify' => { name => 'prettify' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub run { + my ($self, %options) = @_; + + my $disco_stats; + $disco_stats->{start_time} = time(); + + # Retrieve the data + my $response = $options{custom}->request_api('endpoint' => '/vcenter/host', 'method' => 'GET'); + + # Format the data for host discovery + my @results = map { + 'host_name' => $_->{name}, + 'vmw_host_id' => $_->{host}, + 'power_state' => $_->{power_state}, + 'connection_state' => $_->{connection_state}, + }, @{$response}; + + # Record the metadata + $disco_stats->{end_time} = time(); + $disco_stats->{duration} = $disco_stats->{end_time} - $disco_stats->{start_time}; + $disco_stats->{discovered_items} = scalar(@results); + $disco_stats->{results} = \@results; + + my $encoded_data; + eval { + if (defined($self->{option_results}->{prettify})) { + $encoded_data = JSON::XS->new->utf8->canonical(1)->pretty->encode($disco_stats); + } else { + $encoded_data = JSON::XS->new->utf8->canonical(1)->encode($disco_stats); + } + }; + if ($@) { + $encoded_data = '{"code":"encode_error","message":"Cannot encode discovered data into JSON format"}'; + } + + $self->{output}->output_add(short_msg => $encoded_data); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1); +} + +1; + +__END__ + +=head1 MODE + +Resources discovery. + +=over 8 + +=item B<--prettify> + +Prettify JSON output. + +=back + +=cut diff --git a/src/apps/vmware/vsphere8/esx/mode/hoststatus.pm b/src/apps/vmware/vsphere8/esx/mode/hoststatus.pm new file mode 100644 index 0000000000..6d86c2e6e5 --- /dev/null +++ b/src/apps/vmware/vsphere8/esx/mode/hoststatus.pm @@ -0,0 +1,172 @@ +# +# 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 apps::vmware::vsphere8::esx::mode::hoststatus; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub custom_power_status_output { + my ($self, %options) = @_; + + return 'power state is ' . $self->{result_values}->{power_state}; +} + +sub custom_connection_status_output { + my ($self, %options) = @_; + + return 'connection state is ' . $self->{result_values}->{connection_state}; +} + +sub prefix_host_output { + my ($self, %options) = @_; + + return "Host '" . $options{instance_value}->{display} . "': "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options( + arguments => { + 'esx-name:s' => { name => 'esx_name', default => '.*' }, + 'warning-power-status:s' => { name => 'warning-power-status' }, + 'critical-power-status:s' => { name => 'critical-power-status', default => '%{power_state} !~ /^powered_on$/i' }, + 'warning-connection-status:s' => { name => 'warning-connection-status' }, + 'critical-connection-status:s' => { name => 'critical-connection-status', default => '%{connection_state} !~ /^connected$/i' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + + $self->SUPER::check_options(%options); + $self->change_macros(macros => ['warning-power-status', 'critical-power-status', 'warning-connection-status', 'critical-connection-status']); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'host', + type => 1, + cb_prefix_output => 'prefix_host_output', + message_multiple => 'All ESX Hosts are ok' + } + ]; + + $self->{maps_counters}->{host} = [ + { + label => 'power-status', + type => 2, + set => { + key_values => [ { name => 'display' }, { name => 'power_state' } ], + closure_custom_output => $self->can('custom_power_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { + label => 'connection-status', + type => 2, + set => { + key_values => [{ name => 'display' }, { name => 'connection_state' }], + closure_custom_output => $self->can('custom_connection_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $response = $options{custom}->request_api( + 'endpoint' => '/vcenter/host', + 'method' => 'GET' + ); + + $self->{host} = {}; + foreach my $host (@{$response}) { + next if (!defined($host->{name}) || $host->{name} !~ $self->{option_results}->{esx_name}); + + $self->{host}->{$host->{host}} = { + display => $host->{name}, + power_state => $host->{power_state}, + connection_state => $host->{connection_state}, + id => $host->{host}, + }; + } + if (scalar(keys %{$self->{host}}) == 0) { + $self->{output}->add_option_msg(short_msg => "No ESX Host found."); + $self->{output}->option_exit(); + } + return 1; +} + +1; + +__END__ + +=head1 MODE + +Monitor the status of VMware ESX hosts through vSphere 8 REST API. + +=over 8 + +=item B<--esx-name> + +Define which ESX server to monitor based on their name. +This option will be treated as a regular expression. + +=item B<--warning-power-status> + +Define the warning threshold for the power status of the ESX host. +The value should be a Perl expression using the %{power_state} macro. + +=item B<--critical-power-status> + +Define the critical threshold for the power status of the ESX host. +The value should be a Perl expression using the %{power_state} macro. +Default: '%{power_state} !~ /^powered_on$/i' + +=item B<--warning-connection-status> + +Define the warning threshold for the connection status of the ESX host. +The value should be a Perl expression using the %{connection_state} macro. + +=item B<--critical-connection-status> + +Define the critical threshold for the connection status of the ESX host. +The value should be a Perl expression using the %{connection_state} macro. +Default: '%{connection_state} !~ /^connected$/i' + +=back + +=cut diff --git a/src/apps/vmware/vsphere8/esx/plugin.pm b/src/apps/vmware/vsphere8/esx/plugin.pm new file mode 100644 index 0000000000..e73ef136aa --- /dev/null +++ b/src/apps/vmware/vsphere8/esx/plugin.pm @@ -0,0 +1,50 @@ +# +# 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 apps::vmware::vsphere8::esx::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_custom); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{version} = '0.1'; + $self->{modes} = { + 'discovery' => 'apps::vmware::vsphere8::esx::mode::discovery', + 'host-status' => 'apps::vmware::vsphere8::esx::mode::hoststatus' + }; + + $self->{custom_modes}->{api} = 'apps::vmware::vsphere8::custom::api'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Monitor VMware physical hosts through vSphere 8 REST API. + +=cut diff --git a/src/centreon/plugins/misc.pm b/src/centreon/plugins/misc.pm index 29d4f7df4f..627a953147 100644 --- a/src/centreon/plugins/misc.pm +++ b/src/centreon/plugins/misc.pm @@ -745,6 +745,547 @@ sub check_security_whitelist { return 0; } +sub json_decode { + my ($content) = @_; + + $content =~ s/\r//mg; + my $object; + eval { + $object = JSON::XS->new->utf8->decode($content); + }; + if ($@) { + print STDERR "Cannot decode JSON string: $@" . "\n"; + return undef; + } + + return $object; +} + +sub json_encode { + my ($object) = @_; + + $object =~ s/\r//mg; + my $encoded; + eval { + $encoded = encode_json($object); + }; + if ($@) { + use Data::Dumper; + print STDERR 'Cannot encode object to JSON' . Dumper($object); + return undef; + } + + return $encoded; +} + + 1; __END__ + +=head1 NAME + +centreon::plugins::misc - A collection of miscellaneous utility functions for Centreon plugins. + +=head1 SYNOPSIS + + use centreon::plugins::misc; + + my $result = centreon::plugins::misc::execute( + command => 'ls', + command_options => '-l' + ); + +=head1 DESCRIPTION + +The `centreon::plugins::misc` module provides a variety of utility functions that can be used in Centreon plugins. These functions include command execution, string manipulation, file handling, and more. + +=head1 METHODS + +=head2 execute + + my $result = centreon::plugins::misc::execute(%options); + +Executes a command and returns the result. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The command to execute. + +=item * C - Options for the command. + +=item * C - Timeout for the command execution. + +=back + +=back + +=head2 windows_execute + + my ($stdout, $exit_code) = centreon::plugins::misc::windows_execute(%options); + +Executes a command on Windows and returns the output and exit code. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The command to execute. + +=item * C - Options for the command. + +=item * C - Timeout for the command execution. + +=back + +=back + +=head2 unix_execute + + my $stdout = centreon::plugins::misc::unix_execute(%options); + +Executes a command on Unix and returns the output. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The command to execute. + +=item * C - Options for the command. + +=item * C - Timeout for the command execution. + +=back + +=back + +=head2 mymodule_load + + my $result = centreon::plugins::misc::mymodule_load(%options); + +Loads a Perl module dynamically. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The module to load. + +=item * C - Error message to display if the module cannot be loaded. + +=back + +=back + +=head2 backtick + + my ($status, $output, $exit_code) = centreon::plugins::misc::backtick(%options); + +Executes a command using backticks and returns the status, output, and exit code. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The command to execute. + +=item * C - Arguments for the command. + +=item * C - Timeout for the command execution. + +=back + +=back + +=head2 is_empty + + my $is_empty = centreon::plugins::misc::is_empty($value); + +Checks if a value is empty. + +=over 4 + +=item * C<$value> - The value to check. + +=back + +=head2 trim + + my $trimmed_value = centreon::plugins::misc::trim($value); + +Trims whitespace from a string. + +=over 4 + +=item * C<$value> - The string to trim. + +=back + +=head2 powershell_encoded + + my $encoded = centreon::plugins::misc::powershell_encoded($value); + +Encodes a string for use in PowerShell. + +=over 4 + +=item * C<$value> - The string to encode. + +=back + +=head2 powershell_escape + + my $escaped = centreon::plugins::misc::powershell_escape($value); + +Escapes special characters in a string for use in PowerShell. + +=over 4 + +=item * C<$value> - The string to escape. + +=back + +=head2 minimal_version + + my $is_minimal = centreon::plugins::misc::minimal_version($version_src, $version_dst); + +Checks if a version is at least a specified version. + +=over 4 + +=item * C<$version_src> - The source version. + +=item * C<$version_dst> - The destination version. + +=back + +=head2 change_seconds + + my $formatted_time = centreon::plugins::misc::change_seconds(%options); + +Converts seconds into a human-readable format. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The number of seconds. + +=item * C - The starting unit. + +=back + +=back + +=head2 scale_bytesbit + + my $scaled_value = centreon::plugins::misc::scale_bytesbit(%options); + +Scales a value between bytes and bits. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The value to scale. + +=item * C - The source unit. + +=item * C - The destination unit. + +=back + +=back + +=head2 convert_bytes + + my $bytes = centreon::plugins::misc::convert_bytes(%options); + +Converts a value to bytes. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The value to convert. + +=item * C - The unit of the value. + +=back + +=back + +=head2 convert_fahrenheit + + my $celsius = centreon::plugins::misc::convert_fahrenheit(%options); + +Converts a temperature from Fahrenheit to Celsius. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The temperature in Fahrenheit. + +=back + +=back + +=head2 expand_exponential + + my $expanded = centreon::plugins::misc::expand_exponential(%options); + +Expands an exponential value to its full form. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The exponential value. + +=back + +=back + +=head2 alert_triggered + + my $is_triggered = centreon::plugins::misc::alert_triggered(%options); + +Checks if an alert is triggered based on thresholds. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The value to check. + +=item * C - The warning threshold. + +=item * C - The critical threshold. + +=back + +=back + +=head2 parse_threshold + + my ($status, $threshold) = centreon::plugins::misc::parse_threshold(%options); + +Parses a threshold string. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The threshold string. + +=back + +=back + +=head2 get_threshold_litteral + + my $threshold_str = centreon::plugins::misc::get_threshold_litteral(%options); + +Returns the literal representation of a threshold. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - Indicates if the threshold is inclusive. + +=item * C - The start of the threshold. + +=item * C - The end of the threshold. + +=back + +=back + +=head2 set_timezone + + my $timezone = centreon::plugins::misc::set_timezone(%options); + +Sets the timezone. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The name of the timezone. + +=back + +=back + +=head2 uniq + + my @unique = centreon::plugins::misc::uniq(@values); + +Returns a list of unique values. + +=over 4 + +=item * C<@values> - The list of values. + +=back + +=head2 eval_ssl_options + + my $ssl_context = centreon::plugins::misc::eval_ssl_options(%options); + +Evaluates SSL options. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The SSL options. + +=back + +=back + +=head2 slurp_file + + my $content = centreon::plugins::misc::slurp_file(%options); + +Reads the content of a file. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The file to read. + +=back + +=back + +=head2 sanitize_command_param + + my $sanitized = centreon::plugins::misc::sanitize_command_param(%options); + +Sanitizes a command parameter. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The value to sanitize. + +=back + +=back + +=head2 check_security_command + + my $status = centreon::plugins::misc::check_security_command(%options); + +Checks the security of a command. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The command to check. + +=item * C - Options for the command. + +=back + +=back + +=head2 check_security_whitelist + + my $status = centreon::plugins::misc::check_security_whitelist(%options); + +Checks if a command is in the security whitelist. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The command to check. + +=item * C - Options for the command. + +=back + +=back + +=head2 json_decode + + my $decoded = centreon::plugins::misc::json_decode($content); + +Decodes a JSON string. + +=over 4 + +=item * C<$content> - The JSON string to decode and transform into an object. + +=back + +=head2 json_encode + + my $encoded = centreon::plugins::misc::json_encode($object); + +Encodes an object to a JSON string. + +=over 4 + +=item * C<$object> - The object to encode. + +=back + +=head1 AUTHOR + +Centreon + +=head1 LICENSE + +Licensed under the Apache License, Version 2.0. + +=cut diff --git a/src/centreon/plugins/statefile.pm b/src/centreon/plugins/statefile.pm index 62b58c99bf..468b4e16c9 100644 --- a/src/centreon/plugins/statefile.pm +++ b/src/centreon/plugins/statefile.pm @@ -170,7 +170,10 @@ sub check_options { } $self->{statefile_dir} = $options{option_results}->{statefile_dir}; - if ($self->{statefile_dir} ne $default_dir && defined($options{option_results}->{statefile_concat_cwd})) { + if (defined($self->{statefile_dir}) + && $self->{statefile_dir} ne $default_dir + && defined($options{option_results}->{statefile_concat_cwd}) + ) { centreon::plugins::misc::mymodule_load( output => $self->{output}, module => 'Cwd', @@ -443,11 +446,185 @@ __END__ =head1 NAME -Statefile class +centreon::plugins::statefile - A module for managing state files with various storage backends. =head1 SYNOPSIS -- + use centreon::plugins::statefile; + + my $statefile = centreon::plugins::statefile->new( + output => $output, + options => $options + ); + + $statefile->check_options(option_results => $option_results); + $statefile->read(statefile => 'my_statefile'); + my $data = $statefile->get(name => 'some_key'); + $statefile->write(data => { some_key => 'some_value' }); + +=head1 DESCRIPTION + +The `centreon::plugins::statefile` module provides methods to manage state files (files storing the data to keep from an +execution for the next one), supporting various storage backends such as local files, Memcached, and Redis. It also supports encryption and different serialization formats. + +=head1 METHODS + +=head2 new + + my $statefile = centreon::plugins::statefile->new(%options); + +Creates a new `centreon::plugins::statefile` object. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - An output object for logging and error messages. + +=item * C - An options object for adding command-line options. + +=back + +=back + +=head2 check_options + + $statefile->check_options(%options); + +Checks and processes the provided options. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - A hash of option results. + +=back + +=back + +=head2 read + + $statefile->read(%options); + +Reads the state file. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The name of the state file to read. + +=item * C - An optional suffix for the state file name. + +=item * C - An optional directory for the state file. + +=item * C - An optional flag to prevent the program from exiting on error. + +=back + +=back + +=head2 write + + $statefile->write(%options); + +Writes data to the state file. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - A hash reference containing the data to write. + +=back + +=back + +=head2 get + + my $value = $statefile->get(%options); + +Retrieves a value from the state file data. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The key name of the value to retrieve. + +=back + +=back + +=head2 get_string_content + + my $string = $statefile->get_string_content(); + +Returns the state file data as a string. + +=head2 error + + my $error = $statefile->error(); + +Gets or sets the error state. + +=over 4 + +=item * C<$error> - An optional error value to set. + +=back + +=head1 EXAMPLES + +=head2 Creating a Statefile Object + + use centreon::plugins::statefile; + + my $statefile = centreon::plugins::statefile->new( + output => $output, + options => $options + ); + +=head2 Checking Options + + $statefile->check_options(option_results => $option_results); + +=head2 Reading a Statefile + + $statefile->read(statefile => 'my_statefile'); + +=head2 Writing to a Statefile + + $statefile->write(data => { some_key => 'some_value' }); + +=head2 Retrieving a Value + + my $value = $statefile->get(name => 'some_key'); + +=head2 Getting Statefile Data as a String + + my $string = $statefile->get_string_content(); + +=head1 AUTHOR + +Centreon + +=head1 LICENSE + +Licensed under the Apache License, Version 2.0. + +=cut =head1 RETENTION OPTIONS @@ -471,7 +648,7 @@ Set Redis database index. =item B<--failback-file> -Failback on a local file if Redis connection fails. +Fall back on a local file if Redis connection fails. =item B<--memexpiration> diff --git a/tests/apps/vmware/vsphere8/api.t b/tests/apps/vmware/vsphere8/api.t new file mode 100644 index 0000000000..ddc2168bd0 --- /dev/null +++ b/tests/apps/vmware/vsphere8/api.t @@ -0,0 +1,114 @@ +use strict; +use warnings; +use Test2::V0; +use FindBin; +use lib "$FindBin::RealBin/../../../../src"; +use apps::vmware::vsphere8::custom::api; + + +# Mock options class +{ + package MockOptions; + sub new { bless {}, shift } + sub add_options { } + sub add_help { } +} + +{ + package MockOutput; + sub new { bless {}, shift } + sub add_option_msg { } + sub option_exit { } + +} + +sub process_test { + my ($hostname, $port, $proto, $url_path, $timeout, $username, $password) = @_; + + # Create mock object + my $options = MockOptions->new(); + my $output = MockOutput->new(); + + # Add options to the $options hashref + $options->{hostname} = $hostname; + $options->{port} = $port; + $options->{proto} = $proto; + $options->{timeout} = $timeout; + $options->{username} = $username; + $options->{password} = $password; + + # Test object creation + my $api; + eval { + $api = apps::vmware::vsphere8::custom::api->new( + options => $options, + output => $output + ); + }; + ok(!$@, 'Object creation without errors'); + + # Test if the object is blessed correctly + is(ref($api), 'apps::vmware::vsphere8::custom::api', 'Object is of correct class'); + + # Test if the object has the expected attributes + can_ok($api, qw(new set_options check_options)); + + $api->set_options(option_results => $options); + # Verify that option_results is set correctly + is($api->{option_results}, $options, 'option_results set correctly'); + + # Test check_options method + eval { $api->check_options(option_results => $options) }; + ok(!$@, 'check_options method executed without errors'); + + is($api->{hostname}, $hostname, 'hostname set correctly'); + is($api->{port}, defined($port) ? $port : 443, 'port set correctly'); + is($api->{proto}, defined($proto) ? $proto : 'https', 'proto set correctly'); + is($api->{timeout}, defined($timeout) ? $timeout : 10, 'timeout set correctly'); + is($api->{username}, defined($username) ? $username : '', 'username set correctly'); + is($api->{password}, defined($password) ? $password : '', 'password set correctly'); + +} + +sub main { + #process_test('localhost', 443, 'https', '/v2', 10, 'user', 'pass'); + process_test('localhost', 443, undef, undef, undef, undef, undef); +} + +main(); + +done_testing(); + + +__END__ + + + +# Test check_options method with missing username +$option_results = { + hostname => 'localhost', + port => 443, + proto => 'https', + url_path => '/v2', + timeout => 10, + password => 'pass' +}; +$api->set_options(option_results => $option_results); +eval { $api->check_options() }; +like($@, qr/Need to specify --username option/, 'Missing username handled correctly'); + +# Test check_options method with missing password +$option_results = { + hostname => 'localhost', + port => 443, + proto => 'https', + url_path => '/v2', + timeout => 10, + username => 'user' +}; +$api->set_options(option_results => $option_results); +eval { $api->check_options() }; +like($@, qr/Need to specify --password option/, 'Missing password handled correctly'); + + +done_testing(); \ No newline at end of file diff --git a/tests/apps/vmware/vsphere8/esx/discovery.robot b/tests/apps/vmware/vsphere8/esx/discovery.robot new file mode 100644 index 0000000000..0fc9d8337f --- /dev/null +++ b/tests/apps/vmware/vsphere8/esx/discovery.robot @@ -0,0 +1,42 @@ +*** Settings *** + + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}vmware8-restapi.mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin=apps::vmware::vsphere8::esx::plugin +... --password=C3POR2P2 +... --username=obi-wan +... --mode=discovery +... --hostname=127.0.0.1 +... --proto=http +... --port=3000 + +*** Test Cases *** +Discovery ${tc} + [Tags] apps api vmware vsphere8 esx discovery + ${command} Catenate ${CMD} --http-backend=${http_backend} + + # We sort the host names and keep only the last one and make sure it is the expected one + ${output} Run ${command} | jq -r '.results | .[].host_name' | sort | tail -1 + + ${output} Strip String ${output} + Should Be Equal As Strings + ... ${output} + ... ${expected_result} + ... Wrong output result for command:\n${command}\n\nObtained:\n${output}\n\nExpected:\n${expected_result}\n + ... values=False + ... collapse_spaces=True + + + Examples: tc http_backend expected_result -- + ... 1 curl esx3.acme.com + ... 2 lwp esx3.acme.com + diff --git a/tests/apps/vmware/vsphere8/esx/host-status.robot b/tests/apps/vmware/vsphere8/esx/host-status.robot new file mode 100644 index 0000000000..eaefd77a7e --- /dev/null +++ b/tests/apps/vmware/vsphere8/esx/host-status.robot @@ -0,0 +1,51 @@ +*** Settings *** + + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}vmware8-restapi.mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin=apps::vmware::vsphere8::esx::plugin +... --password=C3POR2P2 +... --username=obi-wan +... --mode=host-status +... --hostname=127.0.0.1 +... --proto=http +... --port=3000 + +*** Test Cases *** +Host-Status ${tc} + [Tags] apps api vmware vsphere8 esx + ${command} Catenate ${CMD} --http-backend=${http_backend} --esx-name=${esx_name} ${extraoptions} + + # We sort the host names and keep only the last one and make sure it is the expected one + ${output} Run ${command} + + ${output} Strip String ${output} + Should Be Equal As Strings + ... ${output} + ... ${expected_result} + ... Wrong output result for command:\n${command}\n\nObtained:\n${output}\n\nExpected:\n${expected_result}\n + ... values=False + ... collapse_spaces=True + + + Examples: tc http_backend esx_name extraoptions expected_result -- + ... 1 curl esx1.acme.com ${EMPTY} OK: Host 'esx1.acme.com': power state is POWERED_ON, connection state is CONNECTED + ... 2 lwp esx1.acme.com ${EMPTY} OK: Host 'esx1.acme.com': power state is POWERED_ON, connection state is CONNECTED + ... 3 curl esx2.acme.com ${EMPTY} CRITICAL: Host 'esx2.acme.com': power state is POWERED_OFF + ... 4 lwp esx2.acme.com ${EMPTY} CRITICAL: Host 'esx2.acme.com': power state is POWERED_OFF + ... 5 curl esx3.acme.com ${EMPTY} CRITICAL: Host 'esx3.acme.com': connection state is DISCONNECTED + ... 6 lwp esx3.acme.com ${EMPTY} CRITICAL: Host 'esx3.acme.com': connection state is DISCONNECTED + ... 7 curl esx ${EMPTY} CRITICAL: Host 'esx2.acme.com': power state is POWERED_OFF - Host 'esx3.acme.com': connection state is DISCONNECTED + ... 8 lwp esx ${EMPTY} CRITICAL: Host 'esx2.acme.com': power state is POWERED_OFF - Host 'esx3.acme.com': connection state is DISCONNECTED + ... 9 curl nothing ${EMPTY} UNKNOWN: No ESX Host found. + ... 10 lwp nothing ${EMPTY} UNKNOWN: No ESX Host found. + ... 11 curl esx1.acme.com --port=8888 UNKNOWN: curl perform error : Couldn't connect to server + ... 12 lwp esx1.acme.com --port=8888 UNKNOWN: 500 Can't connect to 127.0.0.1:8888 (Connection refused) diff --git a/tests/apps/vmware/vsphere8/esx/vmware8-restapi.mockoon.json b/tests/apps/vmware/vsphere8/esx/vmware8-restapi.mockoon.json new file mode 100644 index 0000000000..91b9a2f356 --- /dev/null +++ b/tests/apps/vmware/vsphere8/esx/vmware8-restapi.mockoon.json @@ -0,0 +1,183 @@ +{ + "uuid": "dd7d9589-c42b-42e9-8790-f11c8a0f344d", + "lastMigration": 32, + "name": "Vmware8 restapi.mockoon", + "endpointPrefix": "", + "latency": 0, + "port": 3000, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "24bee589-6166-4849-bc82-937ea7a5480c", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "api/session", + "responses": [ + { + "uuid": "d037b485-9952-467c-985c-415b9033e4d9", + "body": "\"32c9819179813376a9bbda43e9c84165\"", + "latency": 0, + "statusCode": 201, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "68acba14-1ccf-4597-a90c-69264b07d558", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/host", + "responses": [ + { + "uuid": "cc160130-c765-4a8a-ba09-0ad544ef956f", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "DATABUCKET", + "filePath": "", + "databucketID": "7kak", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [], + "body": "{}" + } + ], + "responseMode": null + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "24bee589-6166-4849-bc82-937ea7a5480c" + }, + { + "type": "route", + "uuid": "68acba14-1ccf-4597-a90c-69264b07d558" + } + ], + "proxyMode": false, + "proxyHost": "", + "proxyRemovePrefix": false, + "tlsOptions": { + "enabled": false, + "type": "CERT", + "pfxPath": "", + "certPath": "", + "keyPath": "", + "caPath": "", + "passphrase": "" + }, + "cors": true, + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Access-Control-Allow-Methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + } + ], + "proxyReqHeaders": [ + { + "key": "", + "value": "" + } + ], + "proxyResHeaders": [ + { + "key": "", + "value": "" + } + ], + "data": [ + { + "uuid": "a438d9f3-8360-49c3-9d1f-d2f6d202513d", + "id": "7kak", + "name": "/api/vcenter/host", + "documentation": "Complete response", + "value": "[{\"host\":\"host-22\",\"name\":\"esx1.acme.com\",\"connection_state\":\"CONNECTED\",\"power_state\":\"POWERED_ON\"},{\"host\":\"host-28\",\"name\":\"esx2.acme.com\",\"connection_state\":\"CONNECTED\",\"power_state\":\"POWERED_OFF\"},{\"host\":\"host-35\",\"name\":\"esx3.acme.com\",\"connection_state\":\"DISCONNECTED\",\"power_state\":\"POWERED_ON\"}]\n" + } + ], + "callbacks": [] +} \ No newline at end of file diff --git a/tests/centreon/plugins/statefile.t b/tests/centreon/plugins/statefile.t new file mode 100644 index 0000000000..faf58089a0 --- /dev/null +++ b/tests/centreon/plugins/statefile.t @@ -0,0 +1,84 @@ +#!/usr/bin/perl +use strict; +use warnings; +use Test::More; + +use FindBin; +use lib "$FindBin::RealBin/../../../src"; +use centreon::plugins::statefile; + +# Mock options class +{ + package MockOptions; + sub new { bless {}, shift } + sub add_options { } + sub add_help { } +} + +sub process_test { + my ($dir, $filename, $suffix, $format, $crypt_algo, $crypt_key) = @_; + + # Create mock objects + my $options = MockOptions->new(); + my $expected_statefile = $dir . '/' . $filename . $suffix; + unlink $expected_statefile if (-e $expected_statefile); + + # Options to create a statefile object + $options->{statefile_dir} = $dir; + $options->{statefile_suffix} = $suffix; + $options->{statefile_format} = $format; + $options->{statefile_cipher} = $crypt_algo; + $options->{statefile_key} = $crypt_key; + + # Test object creation + my $statefile; + eval { $statefile = centreon::plugins::statefile->new(options => $options) }; + ok(!$@, 'Object creation without errors'); + + $statefile->check_options(option_results => $options); + + # Test if the object is blessed correctly + is(ref($statefile), 'centreon::plugins::statefile', 'Object is of correct class'); + + # Test if the object has the expected attributes + can_ok($statefile, qw(new check_options read write get get_string_content error)); + + # Try a first read to initiate a session + eval { $statefile->read(statefile => 'test_statefile') }; + ok(!$@, 'Read method executed without errors'); + + # Test write method + eval { $statefile->write(data => { some_key => 'some_value' }) }; + ok(!$@, 'Write method executed with plain text data without errors'); + + # Test if the statefile was created + ok(-e $expected_statefile, 'File ' . $expected_statefile . ' exists'); + + + # Test read method + eval { $statefile->read(statefile => 'test_statefile') }; + ok(!$@, 'Read method executed without errors'); + + # Test get method + my $value = $statefile->get(name => 'some_key'); + #use Data::Dumper; + #is($value, 'some_value', 'Get method with plain text data retrieves correct value' . Dumper($statefile->{datas})); + is($value, 'some_value', 'Get method with plain text data retrieves correct value'); + + # Test get_string_content method + my $string_content = $statefile->get_string_content(); + like($string_content, qr/some_key/, 'Get plain text string content method works correctly'); + + # cleanup the mess + unlink $expected_statefile; +} + +sub main { + process_test('/tmp', 'test_statefile', '_plaintext', 'json', undef, undef); + process_test('/tmp', 'test_statefile', '_encrypted', 'json', 'AES', 'mellon'); + done_testing(); +} + +main(); + + diff --git a/tests/resources/spellcheck/stopwords.txt b/tests/resources/spellcheck/stopwords.txt index dec2236406..c581ac9501 100644 --- a/tests/resources/spellcheck/stopwords.txt +++ b/tests/resources/spellcheck/stopwords.txt @@ -46,6 +46,7 @@ dfsrevent --display-transform-src --dyn-mode -EncodedCommand +ESX eth --exclude-fs fanspeed @@ -100,6 +101,7 @@ machineaccount MBean memAvailReal memBuffer +--memexpiration memTotalReal Meraki MIB @@ -159,6 +161,9 @@ proto psu QoS queue-messages-inflighted +--redis-attribute +--redis-db +--redis-server RestAPI RFC1628 RRDCached @@ -170,6 +175,8 @@ space-usage-prct --sql-errors-exit SSDCapacity SSH +statefile +--statefile-concat-cwd SureBackup systemd SysVol @@ -186,6 +193,7 @@ tower-cli TrendMicro UCD UDP +uniq uptime --urlpath usage-prct @@ -200,6 +208,7 @@ VM VMware VPN vSAN +vSphere --warning-backend-congestions --warning-backend-outstanding-io --warning-bytesallocatedpercentage