From 226a574d41fd2ef2e7bdebefef8eadb6c4712731 Mon Sep 17 00:00:00 2001 From: omercier <32134301+omercier@users.noreply.github.com> Date: Fri, 4 Jul 2025 15:54:02 +0200 Subject: [PATCH] feat(apps::vmware::vsphere8::vcenter::plugin): new plugin (#5608) Co-authored-by: sdepassio <114986849+sdepassio@users.noreply.github.com> Refs: CTOR-431 --- .github/scripts/test-all-plugins.py | 36 +- .../deb.json | 5 + .../pkg.json | 10 + .../rpm.json | 5 + src/apps/vmware/vsphere8/custom/api.pm | 2 +- src/apps/vmware/vsphere8/esx/mode.pm | 2 +- src/apps/vmware/vsphere8/vcenter/mode.pm | 170 ++++ .../vsphere8/vcenter/mode/clusterstatus.pm | 167 ++++ .../vsphere8/vcenter/mode/datastoreusage.pm | 229 +++++ .../vsphere8/vcenter/mode/listclusters.pm | 95 ++ .../vsphere8/vcenter/mode/listdatastores.pm | 96 ++ .../vmware/vsphere8/vcenter/mode/vmcount.pm | 218 +++++ src/apps/vmware/vsphere8/vcenter/plugin.pm | 53 + src/centreon/plugins/misc.pm | 60 +- .../vsphere8/vcenter/clusterstatus.robot | 41 + .../vsphere8/vcenter/datastoreusage.robot | 42 + .../vsphere8/vcenter/listclusters.robot | 72 ++ .../vsphere8/vcenter/listdatastores.robot | 53 + .../apps/vmware/vsphere8/vcenter/mockoon.json | 907 ++++++++++++++++++ .../vmware/vsphere8/vcenter/vmcount.robot | 54 ++ tests/centreon/plugins/misc/is_excluded.t | 100 ++ tests/centreon/plugins/misc/json_decode.t | 65 ++ .../plugins/{perfdata.t => perfdata/trim.t} | 2 +- tests/resources/import.resource | 1 + tests/resources/resources.resource | 9 +- tests/resources/spellcheck/stopwords.txt | 6 + 26 files changed, 2478 insertions(+), 22 deletions(-) create mode 100644 packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/deb.json create mode 100644 packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/pkg.json create mode 100644 packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/rpm.json create mode 100644 src/apps/vmware/vsphere8/vcenter/mode.pm create mode 100644 src/apps/vmware/vsphere8/vcenter/mode/clusterstatus.pm create mode 100644 src/apps/vmware/vsphere8/vcenter/mode/datastoreusage.pm create mode 100644 src/apps/vmware/vsphere8/vcenter/mode/listclusters.pm create mode 100644 src/apps/vmware/vsphere8/vcenter/mode/listdatastores.pm create mode 100644 src/apps/vmware/vsphere8/vcenter/mode/vmcount.pm create mode 100644 src/apps/vmware/vsphere8/vcenter/plugin.pm create mode 100644 tests/apps/vmware/vsphere8/vcenter/clusterstatus.robot create mode 100644 tests/apps/vmware/vsphere8/vcenter/datastoreusage.robot create mode 100644 tests/apps/vmware/vsphere8/vcenter/listclusters.robot create mode 100644 tests/apps/vmware/vsphere8/vcenter/listdatastores.robot create mode 100644 tests/apps/vmware/vsphere8/vcenter/mockoon.json create mode 100644 tests/apps/vmware/vsphere8/vcenter/vmcount.robot create mode 100644 tests/centreon/plugins/misc/is_excluded.t create mode 100644 tests/centreon/plugins/misc/json_decode.t rename tests/centreon/plugins/{perfdata.t => perfdata/trim.t} (97%) diff --git a/.github/scripts/test-all-plugins.py b/.github/scripts/test-all-plugins.py index 29f975936..fc7e3a19a 100644 --- a/.github/scripts/test-all-plugins.py +++ b/.github/scripts/test-all-plugins.py @@ -10,8 +10,8 @@ def get_tests_folders(plugin_name): folder_list = [] pkg_file = open("./packaging/" + plugin_name + "/pkg.json") packaging = json.load(pkg_file) - for file in packaging["files"]: # loop on "files" array in pkg.json file. - if os.path.isdir("tests/" + file): # check if the path is a directory in the "tests" folder + for file in packaging["files"]: # loop on "files" array in pkg.json file. + if os.path.isdir("tests/" + file): # check if the path is a directory in the "tests" folder folder_list.append("tests/" + file) return folder_list @@ -27,8 +27,10 @@ def test_plugin(plugin_name): print(f"{plugin_name} folders_list : {folders_list}") if len(folders_list) == 0: return 0 # no tests present at the moment, but we still have tested the plugin can be installed. - robot_results = subprocess.run("robot --exclude notauto -v ''CENTREON_PLUGINS:" + get_plugin_full_path(plugin_name) + " " + " ".join(folders_list), - shell=True, check=False) + robot_results = subprocess.run( + "robot --exclude notauto -v ''CENTREON_PLUGINS:" + get_plugin_full_path(plugin_name) + " " + " ".join( + folders_list), + shell=True, check=False) return robot_results.returncode @@ -52,12 +54,13 @@ def launch_snmp_sim(): snmpsim_cmd = "snmpsim-command-responder --logging-method=null --agent-udpv4-endpoint=127.0.0.1:2024 --process-user=snmp --process-group=snmp --data-dir='./tests' &" try_command(cmd=snmpsim_cmd, error="can't launch snmp sim daemon.") + def refresh_packet_manager(archi): with open('/var/log/robot-plugins-installation-tests.log', "a") as outfile: if archi == "deb": outfile.write("apt-get update\n") output_status = (subprocess.run( - "apt-get update", + "apt-get update", shell=True, check=False, stderr=subprocess.STDOUT, stdout=outfile)).returncode elif archi == "rpm": return 0 @@ -66,17 +69,20 @@ def refresh_packet_manager(archi): exit(1) return output_status + def install_plugin(plugin, archi): with open('/var/log/robot-plugins-installation-tests.log', "a") as outfile: if archi == "deb": - outfile.write("apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb\n") + outfile.write( + "apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb\n") output_status = (subprocess.run( - "apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb", + "apt-get install -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' -y ./" + plugin.lower() + "*.deb", shell=True, check=False, stderr=subprocess.STDOUT, stdout=outfile)).returncode elif archi == "rpm": outfile.write("dnf install --setopt=keepcache=True -y ./" + plugin + "*.rpm\n") - output_status = (subprocess.run("dnf install --setopt=keepcache=True -y ./" + plugin + "*.rpm", shell=True, check=False, - stderr=subprocess.STDOUT, stdout=outfile)).returncode + output_status = ( + subprocess.run("dnf install --setopt=keepcache=True -y ./" + plugin + "*.rpm", shell=True, check=False, + stderr=subprocess.STDOUT, stdout=outfile)).returncode else: print(f"Unknown architecture, expected deb or rpm, got {archi}. Exiting.") exit(1) @@ -86,17 +92,19 @@ def install_plugin(plugin, archi): def remove_plugin(plugin, archi): with open('/var/log/robot-plugins-installation-tests.log', "a") as outfile: if archi == "deb": - outfile.write("apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y " + plugin.lower() + "\n") + outfile.write( + "export SUDO_FORCE_REMOVE=yes; apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y " + + plugin.lower() + "\n") output_status = (subprocess.run( - "apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y " + plugin.lower(), + "export SUDO_FORCE_REMOVE=yes; apt-get -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' autoremove -y " + plugin.lower(), shell=True, check=False, stderr=subprocess.STDOUT, stdout=outfile)).returncode # -o 'Binary::apt::APT::Keep-Downloaded-Packages=1;' is an option to force apt to keep the package in # /var/cache/apt/archives, so it do not re download them for every installation. # 'autoremove', contrary to 'remove' all dependancy while removing the original package. elif archi == "rpm": - outfile.write("dnf remove --setopt=keepcache=True -y " + plugin + "\n") - output_status = (subprocess.run("dnf remove --setopt=keepcache=True -y " + plugin, shell=True, check=False, + outfile.write("dnf remove --setopt=protected_packages= --setopt=keepcache=True -y " + plugin + "\n") + output_status = (subprocess.run("dnf remove --setopt=protected_packages= --setopt=keepcache=True -y " + plugin, shell=True, check=False, stderr=subprocess.STDOUT, stdout=outfile)).returncode else: print(f"Unknown architecture, expected deb or rpm, got {archi}. Exiting.") @@ -153,7 +161,7 @@ if __name__ == '__main__': error_purge += tmp print(f"{nb_plugins} plugins tested.\n there was {error_install} installation error, {error_tests} test " - f"errors, and {error_purge} removal error list of error : {list_plugin_error}",) + f"errors, and {error_purge} removal error list of error : {list_plugin_error}", ) if error_install != 0 or error_tests != 0 or error_purge != 0: exit(1) diff --git a/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/deb.json b/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/deb.json new file mode 100644 index 000000000..540c269f3 --- /dev/null +++ b/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/deb.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "libjson-perl" + ] +} diff --git a/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/pkg.json b/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/pkg.json new file mode 100644 index 000000000..8924d4af9 --- /dev/null +++ b/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/pkg.json @@ -0,0 +1,10 @@ +{ + "pkg_name": "centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi", + "pkg_summary": "Centreon Plugin to monitor VMware vCenter using vSphere 8 REST API", + "plugin_name": "centreon_vmware8_vcenter_restapi.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "apps/vmware/vsphere8/vcenter/", + "apps/vmware/vsphere8/custom/" + ] +} diff --git a/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/rpm.json b/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-Restapi/rpm.json new file mode 100644 index 000000000..186961460 --- /dev/null +++ b/packaging/centreon-plugin-Virtualization-Vmware8-Vcenter-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 index 997e59546..7304c2118 100644 --- a/src/apps/vmware/vsphere8/custom/api.pm +++ b/src/apps/vmware/vsphere8/custom/api.pm @@ -203,7 +203,7 @@ sub try_request_api { } - my $decoded = ($method eq 'GET') ? centreon::plugins::misc::json_decode($content) : {}; + my $decoded = ($method eq 'GET') ? centreon::plugins::misc::json_decode($content, booleans_as_strings => 1) : {}; return $decoded; } diff --git a/src/apps/vmware/vsphere8/esx/mode.pm b/src/apps/vmware/vsphere8/esx/mode.pm index 577f8abec..d1776b01b 100644 --- a/src/apps/vmware/vsphere8/esx/mode.pm +++ b/src/apps/vmware/vsphere8/esx/mode.pm @@ -20,7 +20,7 @@ package apps::vmware::vsphere8::esx::mode; use strict; -use warnings FATAL => 'all'; +use warnings; use base qw(centreon::plugins::templates::counter); diff --git a/src/apps/vmware/vsphere8/vcenter/mode.pm b/src/apps/vmware/vsphere8/vcenter/mode.pm new file mode 100644 index 000000000..053d513c4 --- /dev/null +++ b/src/apps/vmware/vsphere8/vcenter/mode.pm @@ -0,0 +1,170 @@ +# +# 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 apps::vmware::vsphere8::vcenter::mode; +use strict; +use warnings; + +use base qw(centreon::plugins::templates::counter); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + + $options{options}->add_options( + arguments => {} + ); + $options{options}->add_help(package => __PACKAGE__, sections => 'VMWARE 8 VCENTER OPTIONS', once => 1); + + return $self; +} + +sub get_vms { + my ($self, %options) = @_; + # Retrieve the data + return $options{custom}->request_api('endpoint' => '/vcenter/vm', 'method' => 'GET'); +} + +sub get_datastore { + my ($self, %options) = @_; + # if a datastore_id option is given, prepare to append it to the endpoint path + my $datastore_param = defined($options{datastore_id}) ? '/' . $options{datastore_id} : ''; + + # Retrieve the data + return $options{custom}->request_api('endpoint' => '/vcenter/datastore' . $datastore_param, 'method' => 'GET'); +} + +sub get_cluster { + my ($self, %options) = @_; + # if a cluster_id option is given, prepare to append it to the endpoint path + my $cluster_param = defined($options{cluster_id}) ? '/' . $options{cluster_id} : ''; + + # Retrieve the data + return $options{custom}->request_api('endpoint' => '/vcenter/cluster' . $cluster_param, 'method' => 'GET'); +} + +sub request_api { + my ($self, %options) = @_; + + return $options{custom}->request_api(%options); +} + +sub check_options { + my ($self, %options) = @_; + + $self->SUPER::check_options(%options); + +} + +1; + +__END__ + +=head1 VMWARE 8 VCENTER OPTIONS + +=over 4 + +No specific options for vCenter modes. + +=back + +=cut + +=head1 NAME + +apps::vmware::vsphere8::vcenter::mode - Template for modes monitoring VMware vCenter + +=head1 SYNOPSIS + + use base apps::vmware::vsphere8::vcenter::mode; + + sub set_counters {...} + sub manage_selection { + my ($self, %options) = @_; + + $self->set_options(option_results => $option_results); + $self->check_options(); + my $vm_data = $self->get_vms(%options); + my $datastore_data = $self->get_datastore(%options); + } + +=head1 DESCRIPTION + +This module provides methods to interact with the VMware vSphere 8 REST API. It handles generic API requests and VMs GET requests. + +=head1 METHODS + +=head2 get_datastore + + my $all_datastores = $self->get_datastore(%options); + my $one_datastore = $self->get_datastore(%options, datastore_id => 'datastore-35'); + +Retrieves the vCenter's datastores or only one datastore's specifics in case the `datastore_id` option is provided. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The custom_mode object, defined in C and declared in C (mandatory). + +=item * C - The C of a datastore (optional). + +=back + +=back + +=head2 get_vms + + my $all_vms = $self->get_vms(%options); + +Retrieves the vCenter's virtual machines list. + +=over 4 + +=item * C<%options> - A hash of options. The following keys are supported: + +=over 8 + +=item * C - The custom_mode object, defined in C and declared in C (mandatory). + +=back + +=back + +Returns the list of all the virtual machines with the following attributes for each VM: + +=over 4 + +=item * C: ID of the virtual machine. + +=item * C: name of the virtual machine. + +=item * C: number of vCPU. + +=item * C: state of the VM. Can be POWERED_ON, POWERED_OFF, SUSPENDED. + +=item * C: amount of memory allocated to the virtual machine. + +=back + +=cut + diff --git a/src/apps/vmware/vsphere8/vcenter/mode/clusterstatus.pm b/src/apps/vmware/vsphere8/vcenter/mode/clusterstatus.pm new file mode 100644 index 000000000..73cac20c2 --- /dev/null +++ b/src/apps/vmware/vsphere8/vcenter/mode/clusterstatus.pm @@ -0,0 +1,167 @@ +# +# 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 apps::vmware::vsphere8::vcenter::mode::clusterstatus; + +use base qw(apps::vmware::vsphere8::vcenter::mode); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub custom_ha_status_output { + my ($self, %options) = @_; + + my $ha = ($self->{result_values}->{ha_enabled} eq 'true') ? 'enabled' : 'disabled'; + return "'" . $self->{result_values}->{name} . "' has HA " . $ha; +} + +sub custom_drs_status_output { + my ($self, %options) = @_; + + my $drs = ($self->{result_values}->{drs_enabled} eq 'true') ? 'enabled' : 'disabled'; + return "'" . $self->{result_values}->{name} . "' has DRS " . $drs; +} + + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'cluster', type => 1, cb_prefix_output => 'prefix_cluster_output', message_multiple => 'All clusters are ok', skipped_code => { -10 => 1 } } + ]; + + $self->{maps_counters}->{cluster} = [ + { + label => 'ha-status', + type => 2, + warning_default => '%{ha_enabled} ne "true"', + set => { + key_values => [ { name => 'name' }, { name => 'cluster' }, { name => 'ha_enabled' } ], + closure_custom_output => $self->can('custom_ha_status_output'), + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { + label => 'drs-status', + type => 2, + warning_default => '%{drs_enabled} ne "true"', + set => { + key_values => [ { name => 'name' }, { name => 'drs_enabled' }, { name => 'cluster' } ], + closure_custom_output => $self->can('custom_drs_status_output'), + 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); + + $options{options}->add_options( + arguments => { + 'include-name:s' => { name => 'include_name', default => '' }, + 'exclude-name:s' => { name => 'exclude_name', default => '' } + } + ); + $options{options}->add_help(package => __PACKAGE__, sections => 'MODE', once => 1); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + # get the list of clusters response from /api/vcenter/cluster endpoint + my $response = $self->get_cluster(%options); + + for my $cluster (@{$response}) { + + # exclude cluster if not whitelisted + if (centreon::plugins::misc::is_excluded($cluster->{name}, $self->{option_results}->{include_name}, $self->{option_results}->{exclude_name})) { + $self->{output}->output_add(long_msg => "skipping excluded cluster '" . $cluster->{name} . "'", debug => 1); + next; + } + + # and now we store the information + $self->{cluster}->{$cluster->{cluster}} = { + name => $cluster->{name}, + cluster => $cluster->{cluster}, + drs_enabled => $cluster->{drs_enabled}, + ha_enabled => $cluster->{ha_enabled} + }; + } + if (!defined($self->{cluster}) || keys(%{$self->{cluster}}) == 0) { + $self->{output}->output_add( + severity => 'UNKNOWN', + short_msg => 'No clusters found.' + ); + } +} + +1; + +__END__ + +=head1 MODE + +Monitor the status of a vSphere cluster through vSphere 8 REST API. + +=over 8 + +=item B<--include-name> + +Filter by including only the clusters whose name matches the regular expression provided after this parameter. + +Example : C<--include-name='^Prod.*'> + +=item B<--exclude-name> + +Filter by excluding the clusters whose name matches the regular expression provided after this parameter. + +Example : C<--exclude-name='^Sandbox.*'> + +=item B<--warning-ha-status> + +Define the conditions to match for the status to be WARNING. You can use the following variables: C<%{name}>, C<%{ha_enabled}>, +C<%{cluster}>. +Default: C<%{ha_enabled} ne "true"> + +=item B<--critical-ha-status> + +Define the conditions to match for the status to be CRITICAL. You can use the following variables: C<%{name}>, C<%{ha_enabled}>, +C<%{cluster}>. + +=item B<--warning-drs-status> + +Define the conditions to match for the status to be WARNING. You can use the following variables: C<%{name}>, C<%{drs_enabled}>, +C<%{cluster}>. +Default: C<%{drs_enabled} ne "true"> + +=item B<--critical-drs-status> + +Define the conditions to match for the status to be CRITICAL. You can use the following variables: C<%{name}>, C<%{drs_enabled}>, +C<%{cluster}>. + + +=back + +=cut diff --git a/src/apps/vmware/vsphere8/vcenter/mode/datastoreusage.pm b/src/apps/vmware/vsphere8/vcenter/mode/datastoreusage.pm new file mode 100644 index 000000000..8ec8ba1b7 --- /dev/null +++ b/src/apps/vmware/vsphere8/vcenter/mode/datastoreusage.pm @@ -0,0 +1,229 @@ +# +# 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 apps::vmware::vsphere8::vcenter::mode::datastoreusage; + +use base qw(apps::vmware::vsphere8::vcenter::mode); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub custom_status_output { + my ($self, %options) = @_; + + return "'" . $self->{result_values}->{display} . "' accessible" if ($self->{result_values}->{accessible} eq 'true'); + return "'" . $self->{result_values}->{display} . "' NOT accessible" if ($self->{result_values}->{accessible} ne 'true'); +} + +sub custom_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total_space}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used_space}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free_space}); + my $msg = sprintf( + 'Used: %s (%.2f%%) - Free: %s (%.2f%%) - Total: %s', + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used_space}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free_space}, + $total_size_value . " " . $total_size_unit + ); + return $msg; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'datastore', type => 1, cb_prefix_output => 'prefix_datastore_output', message_multiple => 'All datastores are ok', skipped_code => { -10 => 1 } } + ]; + + $self->{maps_counters}->{datastore} = [ + { + label => 'status', + type => 2, + critical_default => '%{accessible} ne "true"', + set => { + key_values => [ { name => 'accessible' }, { name => 'display' }, + { name => 'thin_provisioning_supported' }, { name => 'multiple_host_access' } ], + closure_custom_output => $self->can('custom_status_output'), + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { + label => 'usage', + nlabel => 'datastore.space.usage.bytes', + type => 1, + set => { + key_values => [ + { name => 'used_space' }, { name => 'free_space' }, { name => 'prct_used_space' }, + { name => 'prct_free_space' }, { name => 'total_space' }, { name => 'display' } + ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { label => 'used', template => '%d', min => 0, max => 'total_space', + unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { + label => 'usage-free', + nlabel => 'datastore.space.free.bytes', + display_ok => 0, + type => 1, + set => { + key_values => [ + { name => 'free_space' }, { name => 'used_space' }, { name => 'prct_used_space' }, + { name => 'prct_free_space' }, { name => 'total_space' }, { name => 'display' } ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { label => 'free', template => '%d', min => 0, max => 'total_space', + unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'display' } + ] + } + }, + { + label => 'usage-prct', + nlabel => 'datastore.space.usage.percentage', + display_ok => 0, + type => 1, + set => { + key_values => [ + { name => 'prct_used_space' }, { name => 'free_space' }, { name => 'used_space' }, + { name => 'prct_free_space' }, { name => 'total_space' }, { name => 'display' } + ], + closure_custom_output => $self->can('custom_usage_output'), + perfdatas => [ + { label => 'used_prct', template => '%.2f', min => 0, max => 100, + unit => '%', label_extra_instance => 1, instance_use => 'display' } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + + $options{options}->add_options( + arguments => { + 'include-name:s' => { name => 'include_name', default => '' }, + 'exclude-name:s' => { name => 'exclude_name', default => '' } + } + ); + $options{options}->add_help(package => __PACKAGE__, sections => 'MODE', once => 1); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + # get the list of datastores response from /api/vcenter/datastore endpoint + my $response = $self->get_datastore(%options); + + for my $ds (@{$response}) { + + # exclude datastores not whitelisted + if ( centreon::plugins::misc::is_excluded($ds->{name}, $self->{option_results}->{include_name}, $self->{option_results}->{exclude_name}) ) { + $self->{output}->output_add(long_msg => "skipping excluded datastore '" . $ds->{name} . "'", debug => 1); + next; + } + + # at this point the current datastore must be monitored + # let's get the missing data for the current datastore with a new API request + my $detail = $self->get_datastore(%options, datastore_id => $ds->{datastore}); + # and now we store the information + $self->{datastore}->{$ds->{datastore}} = { + display => $ds->{name}, + type => $ds->{type}, + free_space => $ds->{free_space}, + total_space => $ds->{capacity}, + used_space => $ds->{capacity} - $ds->{free_space}, + prct_used_space => 100 * ($ds->{capacity} - $ds->{free_space}) / $ds->{capacity}, + prct_free_space => 100 * $ds->{free_space} / $ds->{capacity}, + thin_provisioning_supported => $detail->{thin_provisioning_supported}, + accessible => $detail->{accessible}, + multiple_host_access => $detail->{multiple_host_access} + }; + } +} + +1; + +__END__ + +=head1 MODE + +Monitor the usage of a vCenter's datastores through vSphere 8 REST API. + +=over 8 + +=item B<--include-name> + +Filter by including only the VMs whose name matches the regular expression provided after this parameter. + +Example : C<--include-name='^prod.*'> + +=item B<--exclude-name> + +Filter by excluding the VMs whose name matches the regular expression provided after this parameter. + +Example : C<--exclude-name='^sandbox.*'> + +=item B<--warning-status> + +Define the conditions to match for the status to be WARNING. You can use the following variables: C<%{accessible}>, +C<%{display}>, C<%{thin_provisioning_supported}>, C<%{multiple_host_access}>. + +=item B<--critical-status> + +Define the conditions to match for the status to be CRITICAL. You can use the following variables: C<%{accessible}>, +C<%{display}>, C<%{thin_provisioning_supported}>, C<%{multiple_host_access}>. +Default: C<%{accessible} ne "true"> + +=item B<--warning-usage> + +Threshold in bytes. + +=item B<--critical-usage> + +Threshold in bytes. + +=item B<--warning-usage-free> + +Threshold in bytes. + +=item B<--critical-usage-free> + +Threshold in bytes. + +=item B<--warning-usage-prct> + +Threshold in percentage. + +=item B<--critical-usage-prct> + +Threshold in percentage. + +=back + +=cut diff --git a/src/apps/vmware/vsphere8/vcenter/mode/listclusters.pm b/src/apps/vmware/vsphere8/vcenter/mode/listclusters.pm new file mode 100644 index 000000000..320ddf22d --- /dev/null +++ b/src/apps/vmware/vsphere8/vcenter/mode/listclusters.pm @@ -0,0 +1,95 @@ +# +# 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 apps::vmware::vsphere8::vcenter::mode::listclusters; + +use base qw(apps::vmware::vsphere8::vcenter::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub run { + my ($self, %options) = @_; + + my $response = $self->get_cluster(%options); + for my $cluster (@{$response}) { + $self->{output}->output_add( + long_msg => sprintf(" %s [id=%s] [drs_enabled=%s] [ha_enabled=%s]", + $cluster->{name}, + $cluster->{cluster}, + $cluster->{drs_enabled}, + $cluster->{ha_enabled}) + ); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List clusters:'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'cluster', 'drs_enabled', 'ha_enabled']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $response = $self->get_cluster(%options); + for my $ds (@{$response}) { + $self->{output}->add_disco_entry( + drs_enabled => $ds->{drs_enabled}, + cluster => $ds->{cluster}, + name => $ds->{name}, + ha_enabled => $ds->{ha_enabled} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List clusters for service discovery. + +=over 8 + +=back + +=cut diff --git a/src/apps/vmware/vsphere8/vcenter/mode/listdatastores.pm b/src/apps/vmware/vsphere8/vcenter/mode/listdatastores.pm new file mode 100644 index 000000000..19fcfa40b --- /dev/null +++ b/src/apps/vmware/vsphere8/vcenter/mode/listdatastores.pm @@ -0,0 +1,96 @@ +# +# 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 apps::vmware::vsphere8::vcenter::mode::listdatastores; + +use base qw(apps::vmware::vsphere8::vcenter::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub run { + my ($self, %options) = @_; + + my $response = $self->get_datastore(%options); + for my $ds (@{$response}) { + $self->{output}->output_add(long_msg => sprintf(" %s [%s] [%s] [%s free over %s]", + $ds->{name}, + $ds->{type}, + $ds->{datastore}, + $ds->{free_space}, + $ds->{capacity}, + )); + } + + $self->{output}->output_add(severity => 'OK', + short_msg => 'List datastore(s):'); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'datastore', 'free_space', 'capacity', 'type']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $response = $self->get_datastore(%options); + for my $ds (@{$response}) { + $self->{output}->add_disco_entry( + name => $ds->{name}, + datastore => $ds->{datastore}, + free_space => $ds->{free_space}, + capacity => $ds->{capacity}, + type => $ds->{type} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List datastores for service discovery. + +=over 8 + +=back + +=cut diff --git a/src/apps/vmware/vsphere8/vcenter/mode/vmcount.pm b/src/apps/vmware/vsphere8/vcenter/mode/vmcount.pm new file mode 100644 index 000000000..069c1f75d --- /dev/null +++ b/src/apps/vmware/vsphere8/vcenter/mode/vmcount.pm @@ -0,0 +1,218 @@ +# +# 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 apps::vmware::vsphere8::vcenter::mode::vmcount; + +use base qw(apps::vmware::vsphere8::vcenter::mode); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0 } + ]; + + $self->{maps_counters}->{global} = [ + { + label => 'on-count', + nlabel => 'vm.poweredon.count', + type => 1, + set => { + key_values => [ { name => 'POWERED_ON' }, { name => 'total' } ], + output_template => '%s VM(s) powered on', + perfdatas => [ + { label => 'POWERED_ON', template => '%s', min => 0, max => 'total' } + ] + } + }, + { + label => 'off-count', + nlabel => 'vm.poweredoff.count', + type => 1, + set => { + key_values => [ { name => 'POWERED_OFF' }, { name => 'total' } ], + output_template => '%s VM(s) powered off', + perfdatas => [ + { label => 'POWERED_OFF', template => '%s', min => 0, max => 'total' } + ] + } + }, + { + label => 'suspended-count', + nlabel => 'vm.suspended.count', + type => 1, + set => { + key_values => [ { name => 'SUSPENDED' }, { name => 'total' } ], + output_template => '%s VM(s) suspended', + perfdatas => [ + { label => 'SUSPENDED', template => '%s', min => 0, max => 'total' } + ] + } + }, + { + label => 'total-count', + nlabel => 'vm.total.count', + type => 1, + warning_default => '1:', + set => { + key_values => [ { name => 'total' } ], + output_template => '%s VM(s) in total', + perfdatas => [ + { label => 'total', template => '%s', min => 0 } + ] + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + + $options{options}->add_options( + arguments => { + 'include-name:s' => { name => 'include_name', default => '' }, + 'exclude-name:s' => { name => 'exclude_name', default => '' }, + 'include-state:s' => { name => 'include_state', default => '' }, + 'exclude-state:s' => { name => 'exclude_state', default => '' }, + } + ); + $options{options}->add_help(package => __PACKAGE__, sections => 'VMWARE 8 VCENTER OPTIONS', once => 1); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + # get the response from /api/vcenter/vm endpoint + my $response = $self->get_vms(%options); + + $self->{global} = { + 'POWERED_ON' => 0, + 'POWERED_OFF' => 0, + 'SUSPENDED' => 0, + 'total' => 0, + 'UNKNOWN' => 0 + }; + + for my $vm (@{$response}) { + # avoid undef values + my $entry = { + vm => $vm->{vm}, + name => $vm->{name}, + cpu_count => $vm->{cpu_count} // 0, + power_state => $vm->{power_state} // 'UNKNOWN', + memory_size_MiB => $vm->{memory_size_MiB} // 0 + }; + + my $entry_desc = sprintf( + "VM '%s' (%s) which is %s, has %d CPUs and %d MiB of RAM", + $entry->{name}, + $entry->{vm}, + $entry->{power_state}, + $entry->{cpu_count}, + $entry->{memory_size_MiB} + ); + if ( centreon::plugins::misc::is_excluded($entry->{name}, $self->{option_results}->{include_name}, $self->{option_results}->{exclude_name}) + || centreon::plugins::misc::is_excluded($entry->{power_state}, $self->{option_results}->{include_state}, $self->{option_results}->{exclude_state}) ) { + $self->{output}->output_add(long_msg => "skipping VM " . $entry_desc . " (excluded)", debug => 1); + next; + } + + $self->{output}->output_add(long_msg => $entry_desc); + $self->{global}->{ $entry->{power_state} }++; + $self->{global}->{total}++; + + } +} + +1; + +__END__ + +=head1 MODE + +Monitor the number of VMware VMs through vSphere 8 REST API. + +=over 8 + +=item B<--include-name> + +Filter by including only the VMs whose name matches the regular expression provided after this parameter. + +Example : C<--include-name='^prod.*'> + +=item B<--exclude-name> + +Filter by excluding the VMs whose name matches the regular expression provided after this parameter. + +Example : C<--exclude-name='^sandbox.*'> + +=item B<--include-state> + +Filter by including only the VMs whose power state matches the regular expression provided after this parameter. + +Example : C<--include-name='^POWERED_ON$'> + +=item B<--exclude-state> + +Filter by excluding the VMs whose state matches the regular expression provided after this parameter. + +Example : C<--exclude-name='^POWERED_OFF|SUSPENDED$'> + +=item B<--warning-on-count> + +Threshold. + +=item B<--critical-on-count> + +Threshold. + +=item B<--warning-off-count> + +Threshold. + +=item B<--critical-off-count> + +Threshold. + +=item B<--warning-suspended-count> + +Threshold. + +=item B<--critical-suspended-count> + +Threshold. + +=item B<--warning-total-count> + +Threshold. + +=item B<--critical-total-count> + +Threshold. + +=back + +=cut diff --git a/src/apps/vmware/vsphere8/vcenter/plugin.pm b/src/apps/vmware/vsphere8/vcenter/plugin.pm new file mode 100644 index 000000000..b488cc086 --- /dev/null +++ b/src/apps/vmware/vsphere8/vcenter/plugin.pm @@ -0,0 +1,53 @@ +# +# 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 apps::vmware::vsphere8::vcenter::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} = { + 'cluster-status' => 'apps::vmware::vsphere8::vcenter::mode::clusterstatus', + 'datastore-usage' => 'apps::vmware::vsphere8::vcenter::mode::datastoreusage', + 'list-clusters' => 'apps::vmware::vsphere8::vcenter::mode::listclusters', + 'list-datastores' => 'apps::vmware::vsphere8::vcenter::mode::listdatastores', + 'vm-count' => 'apps::vmware::vsphere8::vcenter::mode::vmcount', + }; + + $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 5bb56f0fa..21f57dbb4 100644 --- a/src/centreon/plugins/misc.pm +++ b/src/centreon/plugins/misc.pm @@ -767,18 +767,30 @@ sub check_security_whitelist { } sub json_decode { - my ($content) = @_; + my ($content, %options) = @_; $content =~ s/\r//mg; my $object; + + my $decoder = JSON::XS->new->utf8; + # this option + if ($options{booleans_as_strings}) { + # boolean_values() is not available on old versions of JSON::XS (Alma 8 still provides v3.04) + if (JSON::XS->can('boolean_values')) { + $decoder = $decoder->boolean_values("false", "true"); + } else { + # if boolean_values is not available, perform a dirty substitution of booleans + $content =~ s/"(\w+)"\s*:\s*(true|false)(\s*,?)/"$1": "$2"$3/gm; + } + } + eval { - $object = JSON::XS->new->utf8->decode($content); + $object = $decoder->decode($content); }; if ($@) { print STDERR "Cannot decode JSON string: $@" . "\n"; return undef; } - return $object; } @@ -798,6 +810,15 @@ sub json_encode { return $encoded; } +# function to assess if a string has to be excluded given an include regexp and an exclude regexp +sub is_excluded { + my ($string, $include_regexp, $exclude_regexp) = @_; + return 1 unless defined($string); + return 1 if (defined($exclude_regexp) && $exclude_regexp ne '' && $string =~ /$exclude_regexp/); + return 0 if (!defined($include_regexp) || $include_regexp eq '' || $string =~ /$include_regexp/); + + return 1; +} 1; @@ -1287,7 +1308,7 @@ Checks if a command is in the security whitelist. =head2 json_decode - my $decoded = centreon::plugins::misc::json_decode($content); + my $decoded = centreon::plugins::misc::json_decode($content, %options); Decodes a JSON string. @@ -1295,6 +1316,15 @@ Decodes a JSON string. =item * C<$content> - The JSON string to decode and transform into an object. +=item * C<%options> - Options passed to the function. + +=over 4 + +=item * C - Defines whether booleans must be converted to C/C strings instead of +JSON:::PP::Boolean values. C<1> => strings, C<0> => booleans. + +=back + =back =head2 json_encode @@ -1309,6 +1339,28 @@ Encodes an object to a JSON string. =back +=head2 is_excluded + + my $excluded = is_excluded($string, $include_regexp, $exclude_regexp); + +Determines whether a string should be excluded based on include and exclude regular expressions. + +=over 4 + +=item * C<$string> - The string to evaluate. If undefined, the function returns 1 (excluded). + +=item * C<$include_regexp> - A regular expression to include the string. + +=item * C<$exclude_regexp> - A regular expression to exclude the string. If defined and matches the string, the function returns 1 (excluded). + +=back + +Returns 1 if the string is excluded, 0 if it is included. +The string is excluded if $exclude_regexp is defined and matches the string, or if $include_regexp is defined and does +not match the string. The string will also be excluded if it is undefined. + +=cut + =head1 AUTHOR Centreon diff --git a/tests/apps/vmware/vsphere8/vcenter/clusterstatus.robot b/tests/apps/vmware/vsphere8/vcenter/clusterstatus.robot new file mode 100644 index 000000000..cf77a77fa --- /dev/null +++ b/tests/apps/vmware/vsphere8/vcenter/clusterstatus.robot @@ -0,0 +1,41 @@ +*** Settings *** + + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s +Test Setup Ctn Cleanup Cache + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin=apps::vmware::vsphere8::vcenter::plugin +... --mode=cluster-status +... --password=C3POR2P2 +... --username=obi-wan +... --hostname=127.0.0.1 +... --proto=http +... --port=3000 + +*** Test Cases *** +Cluster-Status ${tc} + [Tags] apps api vmware vsphere8 vcenter + ${command} Catenate ${CMD} ${extraoptions} + + Ctn Run Command And Check Result As Strings ${command} ${expected_result} + + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} WARNING: 'EXT-CLU01' has DRS disabled + ... 2 --warning-drs-status='\\\%{drs_enabled} ne "true"' WARNING: 'EXT-CLU01' has DRS disabled + ... 3 --critical-drs-status='\\\%{drs_enabled} ne "true"' CRITICAL: 'EXT-CLU01' has DRS disabled + ... 4 --warning-drs-status=0 OK: 'EXT-CLU01' has HA enabled, 'EXT-CLU01' has DRS disabled + ... 5 --warning-ha-status='\\\%{ha_enabled} ne "true"' WARNING: 'EXT-CLU01' has DRS disabled + ... 6 --warning-ha-status='\\\%{ha_enabled} eq "true"' WARNING: 'EXT-CLU01' has HA enabled, 'EXT-CLU01' has DRS disabled + ... 7 --critical-ha-status='\\\%{ha_enabled} ne "true"' WARNING: 'EXT-CLU01' has DRS disabled + ... 8 --critical-ha-status='\\\%{ha_enabled} eq "true"' CRITICAL: 'EXT-CLU01' has HA enabled, 'EXT-CLU01' has DRS disabled + ... 9 --include-name='EXT-CLU01' WARNING: 'EXT-CLU01' has DRS disabled + ... 10 --exclude-name='EXT-CLU01' UNKNOWN: No clusters found. + ... 11 --include-name='no match' UNKNOWN: No clusters found. diff --git a/tests/apps/vmware/vsphere8/vcenter/datastoreusage.robot b/tests/apps/vmware/vsphere8/vcenter/datastoreusage.robot new file mode 100644 index 000000000..7caf0d3c7 --- /dev/null +++ b/tests/apps/vmware/vsphere8/vcenter/datastoreusage.robot @@ -0,0 +1,42 @@ +*** Settings *** + + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s +Test Setup Ctn Cleanup Cache + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin=apps::vmware::vsphere8::vcenter::plugin +... --mode=datastore-usage +... --password=C3POR2P2 +... --username=obi-wan +... --hostname=127.0.0.1 +... --proto=http +... --port=3000 + +*** Test Cases *** +Datastore-Usage ${tc} + [Tags] apps api vmware vsphere8 vcenter + ${command_curl} Catenate ${CMD} --http-backend=curl ${extraoptions} + ${command_lwp} Catenate ${CMD} --http-backend=lwp ${extraoptions} + + Ctn Run Command And Check Result As Strings ${command_curl} ${expected_result} + Ctn Run Command And Check Result As Strings ${command_lwp} ${expected_result} + + + Examples: tc extraoptions expected_result -- + ... 1 --include-name=Prod OK: 'Datastore - Production' accessible, Used: 637.46 GB (52.66%) - Free: 573.04 GB (47.34%) - Total: 1.18 TB | 'Datastore - Production#datastore.space.usage.bytes'=684471615488B;;;0;1299764477952 'Datastore - Production#datastore.space.free.bytes'=615292862464B;;;0;1299764477952 'Datastore - Production#datastore.space.usage.percentage'=52.66%;;;0;100 + ... 2 --include-name=Prod --warning-usage=5 WARNING: Used: 637.46 GB (52.66%) - Free: 573.04 GB (47.34%) - Total: 1.18 TB | 'Datastore - Production#datastore.space.usage.bytes'=684471615488B;0:5;;0;1299764477952 'Datastore - Production#datastore.space.free.bytes'=615292862464B;;;0;1299764477952 'Datastore - Production#datastore.space.usage.percentage'=52.66%;;;0;100 + ... 3 --include-name=Prod --critical-usage=5 CRITICAL: Used: 637.46 GB (52.66%) - Free: 573.04 GB (47.34%) - Total: 1.18 TB | 'Datastore - Production#datastore.space.usage.bytes'=684471615488B;;0:5;0;1299764477952 'Datastore - Production#datastore.space.free.bytes'=615292862464B;;;0;1299764477952 'Datastore - Production#datastore.space.usage.percentage'=52.66%;;;0;100 + ... 4 --include-name=Prod --warning-usage-free=1000000000000: WARNING: Used: 637.46 GB (52.66%) - Free: 573.04 GB (47.34%) - Total: 1.18 TB | 'Datastore - Production#datastore.space.usage.bytes'=684471615488B;;;0;1299764477952 'Datastore - Production#datastore.space.free.bytes'=615292862464B;1000000000000:;;0;1299764477952 'Datastore - Production#datastore.space.usage.percentage'=52.66%;;;0;100 + ... 5 --include-name=Prod --critical-usage-free=1000000000000: CRITICAL: Used: 637.46 GB (52.66%) - Free: 573.04 GB (47.34%) - Total: 1.18 TB | 'Datastore - Production#datastore.space.usage.bytes'=684471615488B;;;0;1299764477952 'Datastore - Production#datastore.space.free.bytes'=615292862464B;;1000000000000:;0;1299764477952 'Datastore - Production#datastore.space.usage.percentage'=52.66%;;;0;100 + ... 6 --filter-counters=status CRITICAL: 'Datastore - Developpement 7200' NOT accessible + ... 7 --filter-counters=status --critical-status=0 OK: All datastores are ok + ... 8 --filter-counters=status --critical-status=0 --warning-status=1 WARNING: 'Datastore - Systeme' accessible - 'Datastore - ESX02' accessible - 'Datastore - ESX03' accessible - 'Datastore - Developpement 15000' accessible - 'Datastore - Developpement 7200' NOT accessible - 'Datastore - ESX01' accessible - 'Datastore - Developpement' accessible - 'Datastore - Production' accessible + ... 9 --include-name=Prod --warning-usage-prct=5 WARNING: Used: 637.46 GB (52.66%) - Free: 573.04 GB (47.34%) - Total: 1.18 TB | 'Datastore - Production#datastore.space.usage.bytes'=684471615488B;;;0;1299764477952 'Datastore - Production#datastore.space.free.bytes'=615292862464B;;;0;1299764477952 'Datastore - Production#datastore.space.usage.percentage'=52.66%;0:5;;0;100 + ... 10 --include-name=Prod --critical-usage-prct=5 CRITICAL: Used: 637.46 GB (52.66%) - Free: 573.04 GB (47.34%) - Total: 1.18 TB | 'Datastore - Production#datastore.space.usage.bytes'=684471615488B;;;0;1299764477952 'Datastore - Production#datastore.space.free.bytes'=615292862464B;;;0;1299764477952 'Datastore - Production#datastore.space.usage.percentage'=52.66%;;0:5;0;100 diff --git a/tests/apps/vmware/vsphere8/vcenter/listclusters.robot b/tests/apps/vmware/vsphere8/vcenter/listclusters.robot new file mode 100644 index 000000000..535f1a206 --- /dev/null +++ b/tests/apps/vmware/vsphere8/vcenter/listclusters.robot @@ -0,0 +1,72 @@ +*** Settings *** +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s +Test Setup Ctn Cleanup Cache + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin=apps::vmware::vsphere8::vcenter::plugin +... --mode=list-clusters +... --password=C3POR2P2 +... --username=obi-wan +... --hostname=127.0.0.1 +... --proto=http +... --port=3000 + +*** Test Cases *** +List-Datastores + [Tags] apps api vmware vsphere8 vcenter + ${command} Catenate ${CMD} --http-backend=curl --disco-show + +# expected disco-show +# + # + # + + ${root}= Ctn Run Command And Return Parsed XML ${command} + ${nb_clusters}= Get Element Count ${root} label + + # First check: are there 8 datastores as expected + Should Be Equal As Integers ${nb_clusters} 1 Number of clusters do not match + + # Get the list of cluster IDs + @{elem_list}= Get Elements ${root} label + @{found_clusters}= Create List + FOR ${item} IN @{elemList} + ${clusters_id}= Get Element Attribute ${item} cluster + Append To List ${found_clusters} ${clusters_id} + END + # Here is what is expected + @{expected_clusters}= Create List domain-c18 + # Compare obtained list with expected list + Lists Should Be Equal ${found_clusters} ${expected_clusters} + +Disco-format + ${command} Catenate ${CMD} --disco-format + ${root}= Ctn Run Command And Return Parsed XML ${command} + +# expected disco-format +# +# +# name +# cluster +# drs_enabled +# ha_enabled +# + + # Get the list of cluster IDs + @{elem_list}= Get Elements ${root} element + @{found_macros}= Create List + FOR ${item} IN @{elemList} + ${macro_name}= Get Element Text ${item} + Append To List ${found_macros} ${macro_name} + END + # Here is what is expected + @{expected_macros}= Create List name cluster drs_enabled ha_enabled + # Compare obtained list with expected list + Lists Should Be Equal ${found_macros} ${expected_macros} diff --git a/tests/apps/vmware/vsphere8/vcenter/listdatastores.robot b/tests/apps/vmware/vsphere8/vcenter/listdatastores.robot new file mode 100644 index 000000000..47a91fee1 --- /dev/null +++ b/tests/apps/vmware/vsphere8/vcenter/listdatastores.robot @@ -0,0 +1,53 @@ +*** Settings *** +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s +Test Setup Ctn Cleanup Cache + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin=apps::vmware::vsphere8::vcenter::plugin +... --mode=list-datastores +... --password=C3POR2P2 +... --username=obi-wan +... --hostname=127.0.0.1 +... --proto=http +... --port=3000 + +*** Test Cases *** +List-Datastores + [Tags] apps api vmware vsphere8 vcenter + ${command_curl} Catenate ${CMD} --http-backend=curl --disco-show + +# +# +# + + ${root}= Ctn Run Command And Return Parsed XML ${command_curl} + ${nb_ds}= Get Element Count ${root} label + + # First check: are there 8 datastores as expected + Should Be Equal As Integers ${nb_ds} 8 Number of datastores do not match + + # Get the list of datastore IDs + @{elem_list}= Get Elements ${root} label + @{found_ds}= Create List + FOR ${item} IN @{elemList} + ${ds_id}= Get Element Attribute ${item} datastore + Append To List ${found_ds} ${ds_id} + END + # Here is what is expected + @{expected_ds}= Create List datastore-14 datastore-25 datastore-31 datastore-38 datastore-39 datastore-40 datastore-45 datastore-46 + # Compare obtained list with expected list + Lists Should Be Equal ${found_ds} ${expected_ds} \ No newline at end of file diff --git a/tests/apps/vmware/vsphere8/vcenter/mockoon.json b/tests/apps/vmware/vsphere8/vcenter/mockoon.json new file mode 100644 index 000000000..2dba80246 --- /dev/null +++ b/tests/apps/vmware/vsphere8/vcenter/mockoon.json @@ -0,0 +1,907 @@ +{ + "uuid": "dd7d9589-c42b-42e9-8790-f11c8a0f344d", + "lastMigration": 33, + "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, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "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, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "31f2da05-1f2f-41fb-ae1e-b4f2ee277055", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/vm", + "responses": [ + { + "uuid": "96a6828b-5fa1-496d-bd7f-84df9a152f78", + "body": "[\n {\n \"memory_size_MiB\": 2048,\n \"vm\": \"vm-5678\",\n \"name\": \"my_other_vm\",\n \"power_state\": \"POWERED_OFF\",\n \"cpu_count\": 2\n },\n {\n \"memory_size_MiB\": 4096,\n \"vm\": \"vm-1234\",\n \"name\": \"my_favourite_vm\",\n \"power_state\": \"POWERED_ON\",\n \"cpu_count\": 4\n },\n {\n \"memory_size_MiB\": 4096,\n \"vm\": \"vm-12345\",\n \"name\": \"my_suspended_vm\",\n \"power_state\": \"SUSPENDED\",\n \"cpu_count\": 4\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "84be4ae5-fc86-4ed1-8066-e9c46cef691d", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore", + "responses": [ + { + "uuid": "e8adb942-d389-4265-85ad-b20c728095bd", + "body": "[\n {\n \"datastore\": \"datastore-14\",\n \"name\": \"Datastore - Systeme\",\n \"type\": \"VMFS\",\n \"free_space\": 610635087872,\n \"capacity\": 799937658880\n },\n {\n \"datastore\": \"datastore-25\",\n \"name\": \"Datastore - ESX02\",\n \"type\": \"VMFS\",\n \"free_space\": 340472627200,\n \"capacity\": 341986770944\n },\n {\n \"datastore\": \"datastore-31\",\n \"name\": \"Datastore - ESX03\",\n \"type\": \"VMFS\",\n \"free_space\": 340472627200,\n \"capacity\": 341986770944\n },\n {\n \"datastore\": \"datastore-38\",\n \"name\": \"Datastore - Developpement 15000\",\n \"type\": \"VMFS\",\n \"free_space\": 5586639912960,\n \"capacity\": 7794560335872\n },\n {\n \"datastore\": \"datastore-39\",\n \"name\": \"Datastore - Developpement 7200\",\n \"type\": \"VMFS\",\n \"free_space\": 5422671986688,\n \"capacity\": 5516885491712\n },\n {\n \"datastore\": \"datastore-40\",\n \"name\": \"Datastore - ESX01\",\n \"type\": \"VMFS\",\n \"free_space\": 340472627200,\n \"capacity\": 341986770944\n },\n {\n \"datastore\": \"datastore-45\",\n \"name\": \"Datastore - Developpement\",\n \"type\": \"VMFS\",\n \"free_space\": 4376304287744,\n \"capacity\": 7499818205184\n },\n {\n \"datastore\": \"datastore-46\",\n \"name\": \"Datastore - Production\",\n \"type\": \"VMFS\",\n \"free_space\": 615292862464,\n \"capacity\": 1299764477952\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "7b842f35-687c-45e7-8ffc-192a71cb1210", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-45", + "responses": [ + { + "uuid": "495c38c5-1618-4ecf-b4b4-c31960cc96fd", + "body": "{\n \"accessible\": true,\n \"multiple_host_access\": true,\n \"name\": \"Datastore - Developpement\",\n \"type\": \"VMFS\",\n \"free_space\": 4376304287744,\n \"thin_provisioning_supported\": true\n} \n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "8254cc34-56f6-45b7-b941-df16dc6ef757", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-14", + "responses": [ + { + "uuid": "b7d8fde6-32e1-45eb-b0b5-6ca3640dcb44", + "body": "{\n \"accessible\": true,\n \"multiple_host_access\": true,\n \"name\": \"Datastore - Systeme\",\n \"type\": \"VMFS\",\n \"free_space\": 610635087872,\n \"thin_provisioning_supported\": true\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "9a4bba96-1d68-4930-ad3e-d1f581df5b54", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-25", + "responses": [ + { + "uuid": "71a226a2-b39e-48fa-b135-67ffd533fb2e", + "body": "{\n \"accessible\": true,\n \"multiple_host_access\": false,\n \"name\": \"Datastore - ESX02\",\n \"type\": \"VMFS\",\n \"free_space\": 340472627200,\n \"thin_provisioning_supported\": true\n} \n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "78ced1c5-7ba2-414b-954f-115dd614c163", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-39", + "responses": [ + { + "uuid": "973dd70a-aa17-4ae7-b2d0-0dc0c792a2a7", + "body": "{\n \"accessible\": false,\n \"multiple_host_access\": true,\n \"name\": \"Datastore - Developpement 7200\",\n \"type\": \"VMFS\",\n \"free_space\": 5422671986688,\n \"thin_provisioning_supported\": true\n} \n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "dbafe65e-fe59-43c8-b072-a9a363c1d375", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-46", + "responses": [ + { + "uuid": "4d33cd7a-3919-43cb-9b7e-0ee4c797b8d6", + "body": "{\n \"accessible\": true,\n \"multiple_host_access\": true,\n \"name\": \"Datastore - Production\",\n \"type\": \"VMFS\",\n \"free_space\": 615292862464,\n \"thin_provisioning_supported\": true\n} \n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "689a3103-dca3-416c-a720-0a71a2011ca0", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-40", + "responses": [ + { + "uuid": "95cc3c0e-58ef-4a51-9d8f-e34e5c2ca3b7", + "body": "{\n \"accessible\": true,\n \"multiple_host_access\": false,\n \"name\": \"Datastore - ESX01\",\n \"type\": \"VMFS\",\n \"free_space\": 340472627200,\n \"thin_provisioning_supported\": true\n} \n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "f15307ab-2c42-4b06-bd36-a6562051ebe3", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-38", + "responses": [ + { + "uuid": "140e5e4a-b54a-4adb-a83a-7d456fcdb672", + "body": "{\n \"accessible\": true,\n \"multiple_host_access\": true,\n \"name\": \"Datastore - Developpement 15000\",\n \"type\": \"VMFS\",\n \"free_space\": 5586639912960,\n \"thin_provisioning_supported\": true\n} \n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "f7b2be2f-8055-4619-bee3-c81c12d4ca71", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/datastore/datastore-31", + "responses": [ + { + "uuid": "23576cfa-8eb7-4862-b6f5-2125c058da55", + "body": "{\n \"accessible\": true,\n \"multiple_host_access\": false,\n \"name\": \"Datastore - ESX03\",\n \"type\": \"VMFS\",\n \"free_space\": 340472627200,\n \"thin_provisioning_supported\": true\n} \n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "621ad4eb-05f8-4976-a236-a1e70c7714eb", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/cluster", + "responses": [ + { + "uuid": "ea14a01e-c806-4110-b444-a80d6267914f", + "body": "[\n {\n \"drs_enabled\": false,\n \"cluster\": \"domain-c18\",\n \"name\": \"EXT-CLU01\",\n \"ha_enabled\": true\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "f6e866b7-ede9-4bec-8f69-0513a88b730e", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/vcenter/cluster/domain-c18", + "responses": [ + { + "uuid": "d015157a-edef-4496-ab71-ca2a930daede", + "body": "{\n \"name\": \"EXT-CLU01\",\n \"resource_pool\": \"resgroup-19\"\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "24bee589-6166-4849-bc82-937ea7a5480c" + }, + { + "type": "route", + "uuid": "68acba14-1ccf-4597-a90c-69264b07d558" + }, + { + "type": "route", + "uuid": "31f2da05-1f2f-41fb-ae1e-b4f2ee277055" + }, + { + "type": "route", + "uuid": "84be4ae5-fc86-4ed1-8066-e9c46cef691d" + }, + { + "type": "route", + "uuid": "7b842f35-687c-45e7-8ffc-192a71cb1210" + }, + { + "type": "route", + "uuid": "8254cc34-56f6-45b7-b941-df16dc6ef757" + }, + { + "type": "route", + "uuid": "9a4bba96-1d68-4930-ad3e-d1f581df5b54" + }, + { + "type": "route", + "uuid": "78ced1c5-7ba2-414b-954f-115dd614c163" + }, + { + "type": "route", + "uuid": "dbafe65e-fe59-43c8-b072-a9a363c1d375" + }, + { + "type": "route", + "uuid": "689a3103-dca3-416c-a720-0a71a2011ca0" + }, + { + "type": "route", + "uuid": "f15307ab-2c42-4b06-bd36-a6562051ebe3" + }, + { + "type": "route", + "uuid": "f7b2be2f-8055-4619-bee3-c81c12d4ca71" + }, + { + "type": "route", + "uuid": "621ad4eb-05f8-4976-a236-a1e70c7714eb" + }, + { + "type": "route", + "uuid": "f6e866b7-ede9-4bec-8f69-0513a88b730e" + } + ], + "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/apps/vmware/vsphere8/vcenter/vmcount.robot b/tests/apps/vmware/vsphere8/vcenter/vmcount.robot new file mode 100644 index 000000000..01aa19681 --- /dev/null +++ b/tests/apps/vmware/vsphere8/vcenter/vmcount.robot @@ -0,0 +1,54 @@ +*** Settings *** + + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s +Test Setup Ctn Cleanup Cache + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin=apps::vmware::vsphere8::vcenter::plugin +... --mode=vm-count +... --password=C3POR2P2 +... --username=obi-wan +... --hostname=127.0.0.1 +... --proto=http +... --port=3000 + +*** Test Cases *** +Vm-Count ${tc} + [Tags] apps api vmware vsphere8 esx + ${command_curl} Catenate ${CMD} --http-backend=curl ${extraoptions} + ${command_lwp} Catenate ${CMD} --http-backend=lwp ${extraoptions} + + Ctn Run Command And Check Result As Strings ${command_curl} ${expected_result} + Ctn Run Command And Check Result As Strings ${command_lwp} ${expected_result} + + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 2 --include-name=toto WARNING: 0 VM(s) in total | 'vm.poweredon.count'=0;;;0;0 'vm.poweredoff.count'=0;;;0;0 'vm.suspended.count'=0;;;0;0 'vm.total.count'=0;1:;;0; + ... 3 --include-name=fav OK: 1 VM(s) powered on, 0 VM(s) powered off, 0 VM(s) suspended, 1 VM(s) in total | 'vm.poweredon.count'=1;;;0;1 'vm.poweredoff.count'=0;;;0;1 'vm.suspended.count'=0;;;0;1 'vm.total.count'=1;1:;;0; + ... 4 --warning-on-count=1: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;1:;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 5 --critical-on-count=1: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;1:;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 6 --warning-on-count=2: WARNING: 1 VM(s) powered on | 'vm.poweredon.count'=1;2:;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 7 --critical-on-count=2: CRITICAL: 1 VM(s) powered on | 'vm.poweredon.count'=1;;2:;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 8 --exclude-name=.* WARNING: 0 VM(s) in total | 'vm.poweredon.count'=0;;;0;0 'vm.poweredoff.count'=0;;;0;0 'vm.suspended.count'=0;;;0;0 'vm.total.count'=0;1:;;0; + ... 9 --exclude-name=fav OK: 0 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 2 VM(s) in total | 'vm.poweredon.count'=0;;;0;2 'vm.poweredoff.count'=1;;;0;2 'vm.suspended.count'=1;;;0;2 'vm.total.count'=2;1:;;0; + ... 10 --warning-off-count=1: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;1:;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 11 --critical-off-count=1: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;1:;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 12 --warning-off-count=2: WARNING: 1 VM(s) powered off | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;2:;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 13 --critical-off-count=2: CRITICAL: 1 VM(s) powered off | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;2:;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;;0; + ... 14 --warning-total-count=4: WARNING: 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;4:;;0; + ... 15 --critical-total-count=4: CRITICAL: 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;4:;0; + ... 16 --warning-total-count=2: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;2:;;0; + ... 17 --critical-total-count=2: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;;0;3 'vm.total.count'=3;1:;2:;0; + ... 18 --warning-suspended-count=1: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;1:;;0;3 'vm.total.count'=3;1:;;0; + ... 19 --critical-suspended-count=1: OK: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;1:;0;3 'vm.total.count'=3;1:;;0; + ... 20 --warning-suspended-count=2: WARNING: 1 VM(s) suspended | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;2:;;0;3 'vm.total.count'=3;1:;;0; + ... 21 --critical-suspended-count=2: CRITICAL: 1 VM(s) suspended | 'vm.poweredon.count'=1;;;0;3 'vm.poweredoff.count'=1;;;0;3 'vm.suspended.count'=1;;2:;0;3 'vm.total.count'=3;1:;;0; + ... 22 --critical-suspended-count=2: --warning-on-count=2: --critical-total-count=4: --warning-off-count=2: CRITICAL: 1 VM(s) powered on, 1 VM(s) powered off, 1 VM(s) suspended, 3 VM(s) in total | 'vm.poweredon.count'=1;2:;;0;3 'vm.poweredoff.count'=1;2:;;0;3 'vm.suspended.count'=1;;2:;0;3 'vm.total.count'=3;1:;4:;0; diff --git a/tests/centreon/plugins/misc/is_excluded.t b/tests/centreon/plugins/misc/is_excluded.t new file mode 100644 index 000000000..e31395732 --- /dev/null +++ b/tests/centreon/plugins/misc/is_excluded.t @@ -0,0 +1,100 @@ +use strict; +use warnings; +use Test2::V0; +use Test2::Tools::Compare qw{is like match}; +use FindBin; +use lib "$FindBin::RealBin/../../../../src"; +use centreon::plugins::misc; + +sub test { + + my @tests = ( + { + string => undef, + include_regexp => undef, + exclude_regexp => undef, + expected_boolean => 1, + msg => 'Test undef string with undef include and exclude regexp' + }, + { + string => undef, + include_regexp => 'test', + exclude_regexp => undef, + expected_boolean => 1, + msg => 'Test undef string with non-empty include and undef exclude regexp' + }, + { + string => 'test', + include_regexp => undef, + exclude_regexp => undef, + expected_boolean => 0, + msg => 'Test string with undef include and exclude regexp' + }, + { + string => 'test', + include_regexp => '^t.*t$', + exclude_regexp => undef, + expected_boolean => 0, + msg => 'Test string with include regexp and undef exclude regexp' + }, + { + string => 'test', + include_regexp => undef, + exclude_regexp => '^t.*t$', + expected_boolean => 1, + msg => 'Test string with undef include regex and non-empty exclude regexp' + }, + { + string => '', + include_regexp => '', + exclude_regexp => '', + expected_boolean => 0, + msg => 'Test empty string with empty include and exclude regexp' + }, + { + string => '', + include_regexp => 'test', + exclude_regexp => '', + expected_boolean => 1, + msg => 'Test empty string with non-empty include and empty exclude regexp' + }, + { + string => 'test', + include_regexp => '', + exclude_regexp => '', + expected_boolean => 0, + msg => 'Test string with empty include and exclude regexp' + }, + { + string => 'test', + include_regexp => '^t.*t$', + exclude_regexp => '', + expected_boolean => 0, + msg => 'Test string with include regexp and empty exclude regexp' + }, + { + string => 'test', + include_regexp => '', + exclude_regexp => '^t.*t$', + expected_boolean => 1, + msg => 'Test string with empty include regex and non-empty exclude regexp' + }, + { + string => 'test', + include_regexp => '^t.*t$', + exclude_regexp => '^t.*t$', + expected_boolean => 1, + msg => 'Test string with both include and exclude regexp matching the string' + }, + ); + for my $test (@tests) { + my $is_excluded = centreon::plugins::misc::is_excluded($test->{string}, $test->{include_regexp}, $test->{exclude_regexp}); + is($is_excluded, $test->{expected_boolean}, $test->{msg}); + + } + +} + +test(); +done_testing(); + diff --git a/tests/centreon/plugins/misc/json_decode.t b/tests/centreon/plugins/misc/json_decode.t new file mode 100644 index 000000000..3d97bdf8a --- /dev/null +++ b/tests/centreon/plugins/misc/json_decode.t @@ -0,0 +1,65 @@ +use strict; +use warnings; +use Test2::V0; +use Test2::Tools::Compare qw{is like match}; +use FindBin; +use lib "$FindBin::RealBin/../../../../src"; +use centreon::plugins::misc; +use centreon::plugins::output; +use centreon::plugins::options; + +sub test { + my $mock_output = mock 'centreon::plugins::output'; # this is from Test2::Tools::Mock, included by Test2::V0 + + my $option = centreon::plugins::options->new(); + my $output = centreon::plugins::output->new(options => $option); + + my @tests = ( + { + json_string => '[ { "datastore": "datastore-14", "name": "Datastore - Systeme", "type": "VMFS", "free_space": 610635087872, "capacity": 799937658880 }]', + expected_object => [ { 'name' => 'Datastore - Systeme', 'capacity' => '799937658880', 'datastore' => 'datastore-14', 'free_space' => '610635087872', 'type' => 'VMFS' } ], + booleans_as_strings => 0, + msg => 'Objects without booleans' + }, + { + json_string => '[ { "datastore": "datastore-14", "name": "Datastore - Systeme", "type": "VMFS", "free_space": 610635087872, "capacity": 799937658880 }, { "datastore": "datastore-25", "name": "Datastore - ESX02", "type": "VMFS", "free_space": 340472627200, "capacity": 341986770944 }, { "datastore": "datastore-31", "name": "Datastore - ESX03", "type": "VMFS", "free_space": 340472627200, "capacity": 341986770944 }, { "datastore": "datastore-38", "name": "Datastore - Developpement 15000", "type": "VMFS", "free_space": 5586639912960, "capacity": 7794560335872 }, { "datastore": "datastore-39", "name": "Datastore - Developpement 7200", "type": "VMFS", "free_space": 5422671986688, "capacity": 5516885491712 }, { "datastore": "datastore-40", "name": "Datastore - ESX01", "type": "VMFS", "free_space": 340472627200, "capacity": 341986770944 }, { "datastore": "datastore-45", "name": "Datastore - Developpement", "type": "VMFS", "free_space": 4376304287744, "capacity": 7499818205184 }, { "datastore": "datastore-46", "name": "Datastore - Production", "type": "VMFS", "free_space": 615292862464, "capacity": 1299764477952 }]', + expected_object => [ { 'name' => 'Datastore - Systeme', 'capacity' => '799937658880', 'datastore' => 'datastore-14', 'free_space' => '610635087872', 'type' => 'VMFS' }, { 'free_space' => '340472627200', 'type' => 'VMFS', 'name' => 'Datastore - ESX02', 'capacity' => '341986770944', 'datastore' => 'datastore-25' }, { 'name' => 'Datastore - ESX03', 'capacity' => '341986770944', 'datastore' => 'datastore-31', 'free_space' => '340472627200', 'type' => 'VMFS' }, { 'free_space' => '5586639912960', 'type' => 'VMFS', 'name' => 'Datastore - Developpement 15000', 'capacity' => '7794560335872', 'datastore' => 'datastore-38' }, { 'capacity' => '5516885491712', 'name' => 'Datastore - Developpement 7200', 'datastore' => 'datastore-39', 'type' => 'VMFS', 'free_space' => '5422671986688' }, { 'capacity' => '341986770944', 'name' => 'Datastore - ESX01', 'datastore' => 'datastore-40', 'type' => 'VMFS', 'free_space' => '340472627200' }, { 'datastore' => 'datastore-45', 'capacity' => '7499818205184', 'name' => 'Datastore - Developpement', 'free_space' => '4376304287744', 'type' => 'VMFS' }, { 'capacity' => '1299764477952', 'name' => 'Datastore - Production', 'datastore' => 'datastore-46', 'free_space' => '615292862464', 'type' => 'VMFS' } ], + booleans_as_strings => 1, + msg => 'Objects without booleans' + }, + { + json_string => '{ "accessible": false, "multiple_host_access": true, "name": "Datastore - Developpement 7200", "type": "VMFS", "free_space": 5422671986688, "thin_provisioning_supported": true } ', + expected_object => { 'multiple_host_access' => bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ), 'thin_provisioning_supported' => bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ), 'name' => 'Datastore - Developpement 7200', 'free_space' => '5422671986688', 'type' => 'VMFS', 'accessible' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ) }, + booleans_as_strings => 0, + msg => 'Object with booleans as booleans' + }, + { + json_string => '{ "accessible": false, "multiple_host_access": true, "name": "Datastore - Developpement 7200", "type": "VMFS", "free_space": 5422671986688, "thin_provisioning_supported": true } ', + expected_object => { 'multiple_host_access' => 'true', 'thin_provisioning_supported' => 'true', 'name' => 'Datastore - Developpement 7200', 'free_space' => '5422671986688', 'type' => 'VMFS', 'accessible' => 'false' }, + booleans_as_strings => 1, + msg => 'Object with booleans as strings' + }, + { + json_string => '{ "accessible": false, "multiple_host_access": true, "name": "Trick: true, we\'re still in the string Developpement 7200", "type": "VMFS", "free_space": 5422671986688, "thin_provisioning_supported": true } ', + expected_object => { 'multiple_host_access' => 'true', 'thin_provisioning_supported' => 'true', 'name' => 'Trick: true, we\'re still in the string Developpement 7200', 'free_space' => '5422671986688', 'type' => 'VMFS', 'accessible' => 'false' }, + booleans_as_strings => 1, + msg => 'Object with tricky booleans as strings' + }, + { + json_string => '{ "accessible": false, "multiple_host_access": true, "name": ": true, we\'re still in the string Developpement 7200", "type": "VMFS", "free_space": 5422671986688, "thin_provisioning_supported": true } ', + expected_object => { 'multiple_host_access' => 'true', 'thin_provisioning_supported' => 'true', 'name' => ': true, we\'re still in the string Developpement 7200', 'free_space' => '5422671986688', 'type' => 'VMFS', 'accessible' => 'false' }, + booleans_as_strings => 1, + msg => 'Object with more tricky booleans as strings' + } + ); + for my $test (@tests) { + my ($object, $exit_code) = centreon::plugins::misc::json_decode($test->{json_string}, booleans_as_strings => $test->{booleans_as_strings}); + is($object, $test->{expected_object}, $test->{msg}); + + } + +} + +test(); +done_testing(); + diff --git a/tests/centreon/plugins/perfdata.t b/tests/centreon/plugins/perfdata/trim.t similarity index 97% rename from tests/centreon/plugins/perfdata.t rename to tests/centreon/plugins/perfdata/trim.t index 1fc027ccb..df75ee35e 100644 --- a/tests/centreon/plugins/perfdata.t +++ b/tests/centreon/plugins/perfdata/trim.t @@ -5,7 +5,7 @@ use Test2::V0; use Test2::Plugin::NoWarnings echo => 1; use FindBin; -use lib "$FindBin::RealBin/../../../src"; +use lib "$FindBin::RealBin/../../../../src"; use centreon::plugins::perfdata; my $perfdata = centreon::plugins::perfdata->new(); diff --git a/tests/resources/import.resource b/tests/resources/import.resource index 72d9d38fb..0323ec5d4 100644 --- a/tests/resources/import.resource +++ b/tests/resources/import.resource @@ -6,4 +6,5 @@ Library DateTime Library OperatingSystem Library String Library XML +Library Collections Resource resources.resource diff --git a/tests/resources/resources.resource b/tests/resources/resources.resource index e3e40ee14..c50c74ae8 100644 --- a/tests/resources/resources.resource +++ b/tests/resources/resources.resource @@ -94,4 +94,11 @@ Ctn Run Command And Check Result As Json ${json_output}= evaluate json.loads('''${output}''') json ${json_expected}= evaluate json.loads('''${expected}''') json Dictionaries Should Be Equal ${json_output} ${json_expected} ignore_keys=['end_time', 'start_time', 'duration'] - Log Dictionary ${json_output} \ No newline at end of file + Log Dictionary ${json_output} + +Ctn Run Command And Return Parsed XML + [Arguments] ${command} + ${output} Run ${command} + ${output} Strip String ${output} + ${parsed}= Parse XML ${output} + RETURN ${parsed} \ No newline at end of file diff --git a/tests/resources/spellcheck/stopwords.txt b/tests/resources/spellcheck/stopwords.txt index 27c11eaad..31f6dabe7 100644 --- a/tests/resources/spellcheck/stopwords.txt +++ b/tests/resources/spellcheck/stopwords.txt @@ -57,6 +57,8 @@ CX Cyberoam Datacore datasource +datastore +datastores dBm DC4 dcdiag @@ -308,12 +310,16 @@ v1 v2 v2c v3 +vCenter +VCENTER +vCPU vdom vdomain VDSL2 Veeam VeloCloud VM +VMs VMware VMWARE vpn