From 490945440b668dd9700b3ea25d5571c682ce6307 Mon Sep 17 00:00:00 2001 From: omercier Date: Thu, 20 Mar 2025 11:29:56 +0100 Subject: [PATCH 01/12] fix(ci): add missing image + fix path for tests on noble --- .github/actions/test-plugins/action.yml | 4 +++- .github/docker/testing/Dockerfile.testing-plugins-alma8 | 2 +- .github/docker/testing/Dockerfile.testing-plugins-alma9 | 2 +- .github/docker/testing/Dockerfile.testing-plugins-bookworm | 2 +- .github/docker/testing/Dockerfile.testing-plugins-bullseye | 2 +- .github/docker/testing/Dockerfile.testing-plugins-jammy | 2 +- .github/docker/testing/Dockerfile.testing-plugins-noble | 4 ++-- .github/workflows/plugins.yml | 4 ++-- 8 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/actions/test-plugins/action.yml b/.github/actions/test-plugins/action.yml index bbcf5bfd76..ac24b7d1c6 100644 --- a/.github/actions/test-plugins/action.yml +++ b/.github/actions/test-plugins/action.yml @@ -24,4 +24,6 @@ runs: - name: Install, test and remove plugin shell: bash - run: python3 .github/scripts/test-all-plugins.py ${{ inputs.package-extension }} ${{ inputs.plugin-list }} + run: | + [[ -f /.venv/bin/activate ]] && source /.venv/bin/activate + python3 .github/scripts/test-all-plugins.py ${{ inputs.package-extension }} ${{ inputs.plugin-list }} diff --git a/.github/docker/testing/Dockerfile.testing-plugins-alma8 b/.github/docker/testing/Dockerfile.testing-plugins-alma8 index 22d2d9c07a..71abc16087 100644 --- a/.github/docker/testing/Dockerfile.testing-plugins-alma8 +++ b/.github/docker/testing/Dockerfile.testing-plugins-alma8 @@ -12,7 +12,7 @@ dnf clean all dnf install -y python3.11 python3.11-pip pip3.11 install robotframework robotframework-examples # Install snmpsim -pip3.11 install snmpsim-lextudio +pip3.11 install snmpsim # Install node curl -fsSL https://rpm.nodesource.com/setup_21.x | bash - diff --git a/.github/docker/testing/Dockerfile.testing-plugins-alma9 b/.github/docker/testing/Dockerfile.testing-plugins-alma9 index d5cead3a20..95edbe7640 100644 --- a/.github/docker/testing/Dockerfile.testing-plugins-alma9 +++ b/.github/docker/testing/Dockerfile.testing-plugins-alma9 @@ -12,7 +12,7 @@ dnf clean all dnf install -y python3.11 python3.11-pip pip3.11 install robotframework robotframework-examples # Install snmpsim -pip3.11 install snmpsim-lextudio +pip3.11 install snmpsim # Install node curl -fsSL https://rpm.nodesource.com/setup_21.x | bash - diff --git a/.github/docker/testing/Dockerfile.testing-plugins-bookworm b/.github/docker/testing/Dockerfile.testing-plugins-bookworm index f5e70048ea..48d6e00221 100644 --- a/.github/docker/testing/Dockerfile.testing-plugins-bookworm +++ b/.github/docker/testing/Dockerfile.testing-plugins-bookworm @@ -28,7 +28,7 @@ apt-get install -y python3-dev python3-pip rm -rf /usr/lib/python3.11/EXTERNALLY-MANAGED pip3 install robotframework robotframework-examples # Install snmpsim -pip3 install snmpsim-lextudio +pip3 install snmpsim # Install nodejs curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\ diff --git a/.github/docker/testing/Dockerfile.testing-plugins-bullseye b/.github/docker/testing/Dockerfile.testing-plugins-bullseye index 171240fb7f..87700be3ac 100644 --- a/.github/docker/testing/Dockerfile.testing-plugins-bullseye +++ b/.github/docker/testing/Dockerfile.testing-plugins-bullseye @@ -27,7 +27,7 @@ apt-get update apt-get install -y python3 python3-dev python3-pip pip3 install robotframework robotframework-examples # Install snmpsim -pip3 install snmpsim-lextudio +pip3 install snmpsim # Install nodejs curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\ diff --git a/.github/docker/testing/Dockerfile.testing-plugins-jammy b/.github/docker/testing/Dockerfile.testing-plugins-jammy index 0678ac512b..7519cd7103 100644 --- a/.github/docker/testing/Dockerfile.testing-plugins-jammy +++ b/.github/docker/testing/Dockerfile.testing-plugins-jammy @@ -27,7 +27,7 @@ apt-get update apt-get install -y python3 python3-dev python3-pip pip3 install robotframework robotframework-examples # Install snmpsim -pip3 install snmpsim-lextudio +pip3 install snmpsim # Install nodejs curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\ diff --git a/.github/docker/testing/Dockerfile.testing-plugins-noble b/.github/docker/testing/Dockerfile.testing-plugins-noble index 77260b47ff..4b89178e3d 100644 --- a/.github/docker/testing/Dockerfile.testing-plugins-noble +++ b/.github/docker/testing/Dockerfile.testing-plugins-noble @@ -29,9 +29,9 @@ python3 -m venv .venv source .venv/bin/activate # Install Robotframework apt-get install -y python3 python3-dev python3-pip -.venv/bin/pip3 install robotframework robotframework-examples +pip3 install robotframework robotframework-examples # Install snmpsim -.venv/bin/pip3 install snmpsim-lextudio +pip3 install snmpsim # Install nodejs curl -fsSL https://deb.nodesource.com/setup_21.x | bash - &&\ diff --git a/.github/workflows/plugins.yml b/.github/workflows/plugins.yml index e4d67fe5b8..2052a609d2 100644 --- a/.github/workflows/plugins.yml +++ b/.github/workflows/plugins.yml @@ -98,7 +98,7 @@ jobs: strategy: fail-fast: false matrix: - image: [unit-tests-alma8, unit-tests-alma9, unit-tests-bullseye, unit-tests-bullseye-arm64, unit-tests-bookworm, unit-tests-jammy] + image: [unit-tests-alma8, unit-tests-alma9, unit-tests-bullseye, unit-tests-bullseye-arm64, unit-tests-bookworm, unit-tests-jammy, unit-tests-noble] include: - runner_name: ubuntu-24.04 - package_extension: rpm @@ -317,7 +317,7 @@ jobs: strategy: fail-fast: false matrix: - image: [testing-plugins-alma8, testing-plugins-alma9, testing-plugins-jammy, testing-plugins-bullseye, testing-plugins-bookworm] + image: [testing-plugins-alma8, testing-plugins-alma9, testing-plugins-jammy, testing-plugins-bullseye, testing-plugins-bookworm, testing-plugins-noble, testing-plugins-bullseye-arm64] include: - runner_name: ubuntu-24.04 - package_extension: rpm From 8849bef34586045d3c45bbd924b8182e00840718 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Thu, 9 Jul 2026 07:45:29 -0400 Subject: [PATCH 02/12] wip --- src/cloud/zscaler/ztb/restapi/custom/api.pm | 486 ++++++++++++++++++ src/cloud/zscaler/ztb/restapi/mode/.pm | 202 ++++++++ src/cloud/zscaler/ztb/restapi/mode/cache.pm | 66 +++ .../zscaler/ztb/restapi/mode/gatewaymemory.pm | 293 +++++++++++ .../zscaler/ztb/restapi/mode/gatewaystatus.pm | 293 +++++++++++ src/cloud/zscaler/ztb/restapi/plugin.pm | 49 ++ 6 files changed, 1389 insertions(+) create mode 100644 src/cloud/zscaler/ztb/restapi/custom/api.pm create mode 100644 src/cloud/zscaler/ztb/restapi/mode/.pm create mode 100644 src/cloud/zscaler/ztb/restapi/mode/cache.pm create mode 100644 src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm create mode 100644 src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm create mode 100644 src/cloud/zscaler/ztb/restapi/plugin.pm diff --git a/src/cloud/zscaler/ztb/restapi/custom/api.pm b/src/cloud/zscaler/ztb/restapi/custom/api.pm new file mode 100644 index 0000000000..2745ba7d2a --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/custom/api.pm @@ -0,0 +1,486 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::custom::api; + +use strict; +use warnings; +use centreon::plugins::http; +use centreon::plugins::statefile; +use centreon::plugins::misc qw/json_decode is_empty/; +use centreon::plugins::constants qw(:messages); +use Digest::SHA qw(sha256_hex); + +sub new { + my ($class, %options) = @_; + my $self = {}; + bless $self, $class; + + if (!defined($options{output})) { + print "Class Custom: Need to specify 'output' argument.\n"; + exit 3; + } + $options{output}->option_exit(short_msg => "Class Custom: Need to specify 'options' argument.") + unless $options{options}; + + if (!defined($options{noptions})) { + $options{options}->add_options(arguments => { + 'client-id:s' => { name => 'client_id', default => '' }, + 'client-secret:s' => { name => 'client_secret', default => '' }, + 'login-domain:s' => { name => 'login_domain', default => '' }, + 'api-domain:s' => { name => 'api_domain', default => '' }, + 'port:s' => { name => 'port', default => 443 }, + 'proto:s' => { name => 'proto', default => 'https' }, + 'timeout:s' => { name => 'timeout', default => 50 }, + 'api-path:s' => { name => 'api_path', default => '/api' }, + 'unknown-http-status:s' => { name => 'unknown_http_status', default => '%{http_code} < 200 or %{http_code} >= 300' }, + 'warning-http-status:s' => { name => 'warning_http_status' }, + 'critical-http-status:s' => { name => 'critical_http_status' }, + 'cache-use' => { name => 'cache_use' }, + 'cache-lifetime:s' => { name => 'cache_lifetime', default => 1800 } + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl'); + $self->{cache_connect} = centreon::plugins::statefile->new(%options); + $self->{cache} = centreon::plugins::statefile->new(%options); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults {} + +sub check_options { + my ($self, %options) = @_; + + $self->{$_} = $self->{option_results}->{$_} foreach qw/login_domain api_domain api_path unknown_http_status warning_http_status critical_http_status client_id client_secret cache_lifetime/; + + $self->{output}->option_exit(short_msg => "Need to specify --login-domain option.") + if ($self->{option_results}->{login_domain} eq ''); + $self->{login_domain} .= '.zslogin.net' + if ($self->{login_domain} !~ /\./); + + $self->{output}->option_exit(short_msg => "Need to specify --api-domain option.") + if ($self->{option_results}->{api_domain} eq ''); + $self->{api_domain} .= '-api.goairgap.com' + if ($self->{api_domain} !~ /\./); + + $self->{output}->option_exit(short_msg => "Need to specify --client-id option.") + if ($self->{client_id} eq ''); + $self->{output}->option_exit(short_msg => "Need to specify --client-secret option.") + if ($self->{client_secret} eq ''); + + $self->{cache_connect}->check_options(option_results => $self->{option_results}); + $self->{cache}->check_options(option_results => $self->{option_results}); + + return 0; +} + +sub get_connection_info { + my ($self, %options) = @_; + + return $self->{api_domain} . $self->{client_id}; +} + +sub settings { + my ($self, %options) = @_; + + return if $self->{settings_done}; + $self->{http}->set_options(%{$self->{option_results}}); + $self->{settings_done} = 1; +} + +sub clean_access_token { + my ($self, %options) = @_; + + my $datas = { updated => time() }; + $self->{cache_connect}->write(data => $datas); +} + +sub get_access_token { + my ($self, %options) = @_; + + my $has_cache_file = $self->{cache_connect}->read(statefile => 'zscaler_ztb_' . sha256_hex($self->get_connection_info())); + my $token = $self->{cache_connect}->get(name => 'token'); + my $sha_secret_cache = $self->{cache_connect}->get(name => 'sha_secret'); + my $sha_secret = sha256_hex($self->{client_id} . $self->{client_secret}); + + if ($has_cache_file == 0 || + !defined($token) || + (defined($sha_secret_cache) && $sha_secret_cache ne $sha_secret) + ) { + my $content = $self->{http}->request( + method => 'POST', + hostname => $self->{login_domain}, + url_path => '/oauth2/v1/token', + post_param => [ + 'grant_type=client_credentials', + 'client_id=' . $self->{client_id}, + 'client_secret=' . $self->{client_secret}, + 'audience=https://api.zscaler.com' + ], + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + + my $decoded = json_decode($content, output => $self->{output}); + + $self->{output}->option_exit(short_msg => "Cannot find access token") + unless ref $decoded eq 'HASH' && $decoded->{access_token}; + + $token = $decoded->{access_token}; + + my $datas = { + updated => time(), + token => $token, + sha_secret => $sha_secret + }; + + $self->{cache_connect}->write(data => $datas); + } + + return $token; +} + +sub request_api { + my ($self, %options) = @_; + + $self->settings(); + my $token = $self->get_access_token(); + my ($content) = $self->{http}->request( + hostname => $self->{api_domain}, + url_path => $self->{api_path} . $options{endpoint}, + get_param => $options{get_param}, + header => [ + 'Accept: application/json', + 'Authorization: Bearer ' . $token + ], + unknown_status => '', + warning_status => '', + critical_status => '' + ); + + return $content + if (defined($options{forceReturn}) && defined($options{forceReturn}->{ $self->{http}->get_code() })); + + # Maybe token is invalid. so we retry + if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { + $self->clean_access_token(); + $token = $self->get_access_token(); + $content = $self->{http}->request( + hostname => $self->{api_domain}, + url_path => $self->{api_path} . $options{endpoint}, + get_param => $options{get_param}, + header => [ + 'Accept: application/json', + 'Authorization: Bearer ' . $token + ], + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + } + + $self->{output}->option_exit(short_msg => "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']") + if is_empty($content); + + my $decoded = json_decode($content, output => $self->{output}, no_exit => 1); + $self->{output}->option_exit(short_msg => MSG_JSON_DECODE_ERROR) + unless $decoded; + + return $decoded; +} + +sub write_cache_file { + my ($self, %options) = @_; + + $self->{cache}->read(statefile => 'cache_zscaler_ztb_' . $options{statefile} . '_' . sha256_hex($self->get_connection_info())); + $self->{cache}->write(data => { + update_time => time(), + response => $options{response} + }); +} + +sub get_cache_file_response { + my ($self, %options) = @_; + + $self->{cache}->read(statefile => 'cache_zscaler_ztb_' . $options{statefile} . '_' . sha256_hex($self->get_connection_info())); + my $response = $self->{cache}->get(name => 'response'); + $self->{output}->option_exit(short_msg => 'Cache file missing') + unless $response; + + my $update_time = $self->{cache}->get(name => 'update_time'); + $self->{output}->option_exit(short_msg => 'Cache file expired') + if (time() - $self->{cache_lifetime}) > $update_time; + + return $response; +} + +sub cache_sites { + my ($self, %options) = @_; + + my $response = $self->get_sites(disable_cache => 1); + $self->write_cache_file( + statefile => 'sites', + response => $response + ); + + return $response; +} + +sub cache_gateways { + my ($self, %options) = @_; + + my $response = $self->get_gateways(disable_cache => 1); + $self->write_cache_file( + statefile => 'gateways', + response => $response + ); + + return $response; +} + +sub cache_gateway_resource { + my ($self, %options) = @_; + + my $response = $self->get_gateway_resource(disable_cache => 1, gateway_id => $options{gateway_id}); + $self->write_cache_file( + statefile => 'gateway_resource_' . $options{gateway_id}, + response => $response + ); + + return $response; +} + +sub cache_gateway_interfaces { + my ($self, %options) = @_; + + my $response = $self->get_gateway_interfaces(disable_cache => 1, gateway_id => $options{gateway_id}); + $self->write_cache_file( + statefile => 'gateway_interfaces_' . $options{gateway_id}, + response => $response + ); + + return $response; +} + +sub get_sites { + my ($self, %options) = @_; + + return $self->get_cache_file_response(statefile => 'sites') + if $self->{option_results}->{cache_use} && !$options{disable_cache}; + + my $data = $self->request_api( + endpoint => '/v2/Site/' + ); + + my $response = []; + foreach (@{$data->{result}->{rows}}) { + push @$response, { + id => $_->{id}, + name => $_->{name}, + site_status => $_->{site_status} + }; + } + + return $response; +} + +sub get_gateways { + my ($self, %options) = @_; + + return $self->get_cache_file_response(statefile => 'gateways') + if $self->{option_results}->{cache_use} && !$options{disable_cache}; + + my $data = $self->request_api( + endpoint => '/v3/Gateway/' + ); + my $response = []; + foreach my $site (@{$data->{rows}}) { + my $item = { + site_id => $site->{cluster_info}->{site_id}, + cluster_id => $site->{cluster_info}->{cluster_id}, + cluster_name => $site->{cluster_info}->{cluster_name}, + }; + foreach my $gateway (@{$site->{gateways}}) { + my $gw = { %$item }; + $gw->{gw_id} = $gateway->{gateway_id}; + $gw->{gw_name} = $gateway->{gateway_name}; + $gw->{gw_display_name} = $gateway->{display_name}; + $gw->{gw_ip_address} = $gateway->{gateway_ip_address}; + $gw->{gw_health_color} = $gateway->{health_color}; + $gw->{gw_vrrp_state} = $gateway->{vrrp_state}; + $gw->{gw_desired_state} = $gateway->{desired_state}; + $gw->{gw_operational_state} = $gateway->{operational_state}; + push @$response, $gw; + } + } + + return $response; +} + +sub get_gateway_resource { + my ($self, %options) = @_; + + return $self->get_cache_file_response(statefile => 'gateway_resource_' . $options{gateway_id}) + if $self->{option_results}->{cache_use} && !$options{disable_cache}; + + # in some case, you have 400 return with message: 'sql: no rows in result set' + my $data = $self->request_api( + endpoint => '/v3/debug-manager/resource-status', + get_param => ['gateway_id=' . $options{gateway_id}], + forceReturn => { 400 => 1 } + ); + + my $response = []; + if (ref($data) eq 'HASH') { + foreach (@{$data->{result}->{rows}}) { + push @$response, { + containers_stats => $_->{containers_stats}, + system_stats => $_->{system_stats}, + hourly_stat_last => $_->{average_values}->{hourly_stats}->[0], + uptime => $_->{device_uptime}, + wanmon_avg_values_last => $_->{wanmon_avg_values}->[0] + }; + } + } + + return $response; +} + +sub get_gateway_interfaces { + my ($self, %options) = @_; + + return $self->get_cache_file_response(statefile => 'gateway_interfaces_' . $options{gateway_id}) + if $self->{option_results}->{cache_use} && !$options{disable_cache}; + + # in some case, you have 400 return with message: 'sql: no rows in result set' + my $data = $self->request_api( + endpoint => '/v2/Gateway/interfaces', + get_param => ['gatewayId=' . $options{gateway_id}], + forceReturn => { 400 => 1 } + ); + + my $response = []; + if (ref($data) eq 'ARRAY') { + foreach my $item (@$data) { + foreach my $int (@{$item->{interfaces}}) { + push @$response, { + name => $int->{name}, + description => $int->{description}, + admin_state => $int->{admin_state}, + operational_state => $int->{operational_state}, + if_stats => $int->{if_stats} + }; + } + } + } + + return $response; +} + +sub get_cluster_ipsec_status { + my ($self, %options) = @_; + + my $data = $self->request_api( + endpoint => '/v2/cluster/ipsec-status', + get_param => ['cluster_id=' . $options{cluster_id}], + ); + + return $data->{tun_status}; +} + +sub get_appconnector_config { + my ($self, %options) = @_; + + my $data = $self->request_api( + endpoint => '/v3/appconnector/config', + get_param => ['cluster_id=' . $options{cluster_id}], + ); + + return $data->{result}; +} + +1; + +__END__ + +=head1 NAME + +ZTB Rest API + +=head1 REST API OPTIONS + +Zero Trust Branch Rest API + +=over 8 + +=item B<--login-domain> + +Define the domain for OAuth2 authentification (e.g.: 'test.zslogin.net') + +=item B<--api-domain> + +Define the domain for ZTB API (e.g.: 'totalenergies-api.goairgap.com') + +=item B<--client-id> + +Define client ID. + +=item B<--client-secret> + +Define client secret. + +=item B<--port> + +Port used (default: 443) + +=item B<--proto> + +Define https if needed (default: 'https') + +=item B<--timeout> + +Set timeout in seconds (default: 50). + +=item B<--cache-use> + +Use the cache file (created with cache mode). + +=item B<--cache-lifetime> + +Define the cache lifetime before raising an error (default: 1800 seconds). + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/.pm b/src/cloud/zscaler/ztb/restapi/mode/.pm new file mode 100644 index 0000000000..36c0c9b55c --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/.pm @@ -0,0 +1,202 @@ +# +# Copyright 2026-Present 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::backup::veeam::vone::restapi::mode::proxies; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +my $map_repository_state_numeric = { + unknown => 0, + ok => 1, + inaccessible => 2, + disconnected => 3, + outOfDate => 4, + warning => 5 +}; + +sub custom_status_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => 'proxy.state.count', + instances => $self->{result_values}->{name}, + value => $map_repository_state_numeric->{ $self->{result_values}->{state} }, + min => 0 + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of proxies '; +} + +sub prefix_proxy_output { + my ($self, %options) = @_; + + return sprintf( + "proxy '%s' [type: %s] ", + $options{instance_value}->{name}, + $options{instance_value}->{type} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { name => 'proxies', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_proxy_output', message_multiple => 'All proxies are ok' } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'proxies-detected', display_ok => 0, nlabel => 'proxies.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{proxies} = [ + { + label => 'proxy-status', + type => COUNTER_KIND_TEXT, + unknown_default => '%{state} =~ /unknown/', + warning_default => '%{state} =~ /warning|outofdate/', + critical_default => '%{state} =~ /inaccessible|disconnected/', + set => { + key_values => [ + { name => 'state' }, { name => 'name' }, { name => 'type' } + ], + output_template => 'state: %s', + closure_custom_perfdata => $self->can('custom_status_perfdata'), + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-uid:s' => { name => 'filter_uid', default => '' }, + 'filter-name:s' => { name => 'filter_name', default => '' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $repositories = $options{custom}->get_proxies(); + + $self->{global} = { detected => 0 }; + $self->{proxies} = {}; + foreach my $repo (@{$repositories->{items}}) { + my $enabled = $repo->{enabled} =~ /true|1/i ? 1 : 0; + next unless $enabled || $options{keep_disabled}; + next if is_excluded($repo->{proxyUidInVbr}, $self->{option_results}->{filter_uid}); + next if is_excluded($repo->{name}, $self->{option_results}->{filter_name}); + + $self->{proxies}->{ $repo->{name} } = { + uid => $repo->{proxyUidInVbr}, + name => $repo->{name}, + type => $repo->{type}, + state => lc $repo->{state}, + enabled => $enabled + }; + $self->{global}->{detected}++; + } +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['uid', 'name', 'type', 'state', 'enabled']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $repos = $self->manage_selection(custom => $options{custom}, keep_disabled => 1); + foreach (sort { $a->{name} cmp $b->{name} } values %{ $self->{proxies} } ) { + $self->{output}->add_disco_entry(%$_); + } +} + +1; + +__END__ + +=head1 MODE + +Check proxies. + +=over 8 + +=item B<--filter-uid> + +Filter proxies by UID (can be a regexp). + +=item B<--filter-name> + +Filter proxies by name (can be a regexp). + +=item B<--unknown-proxy-status> + +Define the conditions to match for the status to be UNKNOWN (default: '%{state} =~ /unknown/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--warning-proxy-status> + +Define the conditions to match for the status to be WARNING (default: '%{state} =~ /warning|outofdate/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--critical-proxy-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{state} =~ /inaccessible|disconnected/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--warning-proxies-detected> + +Thresholds. + +=item B<--critical-proxies-detected> + +Thresholds. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/cache.pm b/src/cloud/zscaler/ztb/restapi/mode/cache.pm new file mode 100644 index 0000000000..447eeb2703 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/cache.pm @@ -0,0 +1,66 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::mode::cache; + +use base qw(centreon::plugins::templates::counter); + +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 manage_selection { + my ($self, %options) = @_; + + $options{custom}->cache_sites(); + my $gateways = $options{custom}->cache_gateways(); + foreach (@$gateways) { + $options{custom}->cache_gateway_resource(gateway_id => $_->{gw_id}); + $options{custom}->cache_gateway_interfaces(gateway_id => $_->{gw_id}); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'Cache files created successfully' + ); +} + +1; + +__END__ + +=head1 MODE + +Create cache files (other modes could use it with --cache-use option). + +=over 8 + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm new file mode 100644 index 0000000000..7191eedd9d --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm @@ -0,0 +1,293 @@ +# +# Copyright 2026-Present 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::backup::veeam::vone::restapi::mode::repositories; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +my $map_repository_state_numeric = { + unknown => 0, + ok => 1, + inaccessible => 2, + disconnected => 3, + outOfDate => 4, + warning => 5 +}; + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + return sprintf( + "space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} + ); +} + +sub custom_status_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => 'repository.state.count', + instances => $self->{result_values}->{name}, + value => $map_repository_state_numeric->{ $self->{result_values}->{state} }, + min => 0 + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of repositories '; +} + +sub repository_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking repository '%s' [type: %s]", + $options{instance_value}->{name}, + $options{instance_value}->{type} + ); +} + +sub prefix_repository_output { + my ($self, %options) = @_; + + return sprintf( + "repository '%s' [type: %s] ", + $options{instance_value}->{name}, + $options{instance_value}->{type} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { + name => 'repositories', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_repository_output', cb_long_output => 'repository_long_output', indent_long_output => ' ', message_multiple => 'All repositories are ok', + group => [ + { name => 'status', type => COUNTER_MULTIPLE_INSTANCE }, + { name => 'space', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } }, + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'repositories-detected', display_ok => 0, nlabel => 'repositories.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{space} = [ + { label => 'space-usage', nlabel => 'repository.space.usage.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } + ] + } + }, + { label => 'space-usage-free', display_ok => 0, nlabel => 'repository.space.free.bytes', set => { + key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } + ] + } + }, + { label => 'space-usage-prct', display_ok => 0, nlabel => 'repository.space.usage.percentage', set => { + key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'name' } + ] + } + } + ]; + + $self->{maps_counters}->{status} = [ + { + label => 'repository-status', + type => COUNTER_KIND_TEXT, + unknown_default => '%{state} =~ /unknown/', + warning_default => '%{state} =~ /warning|outofdate/', + critical_default => '%{state} =~ /inaccessible|disconnected/', + set => { + key_values => [ + { name => 'state' }, { name => 'name' }, { name => 'type' } + ], + output_template => 'state: %s', + closure_custom_perfdata => $self->can('custom_status_perfdata'), + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-uid:s' => { name => 'filter_uid', default => '' }, + 'filter-name:s' => { name => 'filter_name', default => '' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $repositories = $options{custom}->get_repositories(); + + $self->{global} = { detected => 0 }; + $self->{repositories} = {}; + + foreach my $repo (@{$repositories->{items}}) { + next if is_excluded($repo->{repositoryUidInVbr}, $self->{option_results}->{filter_uid}); + next if is_excluded($repo->{name}, $self->{option_results}->{filter_name}); + + $self->{repositories}->{ $repo->{name} } = { + name => $repo->{name}, + type => $repo->{type}, + uid => $repo->{repositoryUidInVbr}, + space => { + name => $repo->{name}, + total => $repo->{capacityBytes}, + free => $repo->{freeSpaceBytes}, + used => $repo->{capacityBytes} - $repo->{freeSpaceBytes}, + prct_used => 100 - ($repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes}), + prct_free => $repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes} + }, + status => { + name => $repo->{name}, + type => $repo->{type}, + state => lc $repo->{state} + } + }; + $self->{global}->{detected}++; + } +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['uid', 'name', 'type', 'state']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $repos = $self->manage_selection(custom => $options{custom}); + foreach (values %{$self->{repositories}}) { + $self->{output}->add_disco_entry(uid => $_->{uid}, name => $_->{name}, type => $_->{type}, state => $_->{status}->{state}); + } +} + +1; + +__END__ + +=head1 MODE + +Check repositories. + +=over 8 + +=item B<--filter-uid> + +Filter repositories by UID (can be a regexp). + +=item B<--filter-name> + +Filter repositories by name (can be a regexp). + +=item B<--unknown-repository-status> + +Define the conditions to match for the status to be UNKNOWN (default: '%{state} =~ /unknown/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--warning-repository-status> + +Define the conditions to match for the status to be WARNING (default: '%{state} =~ /warning|outofdate/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--critical-repository-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{state} =~ /inaccessible|disconnected/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--warning-repositories-detected> + +Threshold. + +=item B<--critical-repositories-detected> + +Threshold. + +=item B<--warning-space-usage> + +Threshold in bytes. + +=item B<--critical-space-usage> + +Threshold in bytes. + +=item B<--warning-space-usage-free> + +Threshold in bytes. + +=item B<--critical-space-usage-free> + +Threshold in bytes. + +=item B<--warning-space-usage-prct> + +Threshold in percentage. + +=item B<--critical-space-usage-prct> + +Threshold in percentage. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm new file mode 100644 index 0000000000..7191eedd9d --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm @@ -0,0 +1,293 @@ +# +# Copyright 2026-Present 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::backup::veeam::vone::restapi::mode::repositories; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +my $map_repository_state_numeric = { + unknown => 0, + ok => 1, + inaccessible => 2, + disconnected => 3, + outOfDate => 4, + warning => 5 +}; + +sub custom_space_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + return sprintf( + "space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} + ); +} + +sub custom_status_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => 'repository.state.count', + instances => $self->{result_values}->{name}, + value => $map_repository_state_numeric->{ $self->{result_values}->{state} }, + min => 0 + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of repositories '; +} + +sub repository_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking repository '%s' [type: %s]", + $options{instance_value}->{name}, + $options{instance_value}->{type} + ); +} + +sub prefix_repository_output { + my ($self, %options) = @_; + + return sprintf( + "repository '%s' [type: %s] ", + $options{instance_value}->{name}, + $options{instance_value}->{type} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { + name => 'repositories', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_repository_output', cb_long_output => 'repository_long_output', indent_long_output => ' ', message_multiple => 'All repositories are ok', + group => [ + { name => 'status', type => COUNTER_MULTIPLE_INSTANCE }, + { name => 'space', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } }, + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'repositories-detected', display_ok => 0, nlabel => 'repositories.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{space} = [ + { label => 'space-usage', nlabel => 'repository.space.usage.bytes', set => { + key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } + ] + } + }, + { label => 'space-usage-free', display_ok => 0, nlabel => 'repository.space.free.bytes', set => { + key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } + ] + } + }, + { label => 'space-usage-prct', display_ok => 0, nlabel => 'repository.space.usage.percentage', set => { + key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], + closure_custom_output => $self->can('custom_space_usage_output'), + perfdatas => [ + { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'name' } + ] + } + } + ]; + + $self->{maps_counters}->{status} = [ + { + label => 'repository-status', + type => COUNTER_KIND_TEXT, + unknown_default => '%{state} =~ /unknown/', + warning_default => '%{state} =~ /warning|outofdate/', + critical_default => '%{state} =~ /inaccessible|disconnected/', + set => { + key_values => [ + { name => 'state' }, { name => 'name' }, { name => 'type' } + ], + output_template => 'state: %s', + closure_custom_perfdata => $self->can('custom_status_perfdata'), + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-uid:s' => { name => 'filter_uid', default => '' }, + 'filter-name:s' => { name => 'filter_name', default => '' } + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $repositories = $options{custom}->get_repositories(); + + $self->{global} = { detected => 0 }; + $self->{repositories} = {}; + + foreach my $repo (@{$repositories->{items}}) { + next if is_excluded($repo->{repositoryUidInVbr}, $self->{option_results}->{filter_uid}); + next if is_excluded($repo->{name}, $self->{option_results}->{filter_name}); + + $self->{repositories}->{ $repo->{name} } = { + name => $repo->{name}, + type => $repo->{type}, + uid => $repo->{repositoryUidInVbr}, + space => { + name => $repo->{name}, + total => $repo->{capacityBytes}, + free => $repo->{freeSpaceBytes}, + used => $repo->{capacityBytes} - $repo->{freeSpaceBytes}, + prct_used => 100 - ($repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes}), + prct_free => $repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes} + }, + status => { + name => $repo->{name}, + type => $repo->{type}, + state => lc $repo->{state} + } + }; + $self->{global}->{detected}++; + } +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['uid', 'name', 'type', 'state']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $repos = $self->manage_selection(custom => $options{custom}); + foreach (values %{$self->{repositories}}) { + $self->{output}->add_disco_entry(uid => $_->{uid}, name => $_->{name}, type => $_->{type}, state => $_->{status}->{state}); + } +} + +1; + +__END__ + +=head1 MODE + +Check repositories. + +=over 8 + +=item B<--filter-uid> + +Filter repositories by UID (can be a regexp). + +=item B<--filter-name> + +Filter repositories by name (can be a regexp). + +=item B<--unknown-repository-status> + +Define the conditions to match for the status to be UNKNOWN (default: '%{state} =~ /unknown/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--warning-repository-status> + +Define the conditions to match for the status to be WARNING (default: '%{state} =~ /warning|outofdate/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--critical-repository-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{state} =~ /inaccessible|disconnected/'). +You can use the following variables: %{state}, %{name}, %{type} + +=item B<--warning-repositories-detected> + +Threshold. + +=item B<--critical-repositories-detected> + +Threshold. + +=item B<--warning-space-usage> + +Threshold in bytes. + +=item B<--critical-space-usage> + +Threshold in bytes. + +=item B<--warning-space-usage-free> + +Threshold in bytes. + +=item B<--critical-space-usage-free> + +Threshold in bytes. + +=item B<--warning-space-usage-prct> + +Threshold in percentage. + +=item B<--critical-space-usage-prct> + +Threshold in percentage. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm new file mode 100644 index 0000000000..08f793cc19 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -0,0 +1,49 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_custom); + +sub new { + my ( $class, %options ) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $self->{modes} = { + 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', + 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory' + }; + + $self->{custom_modes}->{api} = 'cloud::zscaler::ztb::restapi::custom::api'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Zscaler Zero Trust Branch using API. + +=cut From 33ec541e1edaf56c566d95756a00585ad48b0085 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Thu, 9 Jul 2026 08:29:21 -0400 Subject: [PATCH 03/12] wip --- src/cloud/zscaler/ztb/restapi/custom/api.pm | 19 +- .../zscaler/ztb/restapi/mode/gatewaymemory.pm | 334 +++++++++++------- 2 files changed, 214 insertions(+), 139 deletions(-) diff --git a/src/cloud/zscaler/ztb/restapi/custom/api.pm b/src/cloud/zscaler/ztb/restapi/custom/api.pm index 2745ba7d2a..617431ea7b 100644 --- a/src/cloud/zscaler/ztb/restapi/custom/api.pm +++ b/src/cloud/zscaler/ztb/restapi/custom/api.pm @@ -325,6 +325,7 @@ sub get_gateways { foreach my $site (@{$data->{rows}}) { my $item = { site_id => $site->{cluster_info}->{site_id}, + site_name => $site->{location}, cluster_id => $site->{cluster_info}->{cluster_id}, cluster_name => $site->{cluster_info}->{cluster_name}, }; @@ -358,17 +359,15 @@ sub get_gateway_resource { forceReturn => { 400 => 1 } ); - my $response = []; + my $response = {}; if (ref($data) eq 'HASH') { - foreach (@{$data->{result}->{rows}}) { - push @$response, { - containers_stats => $_->{containers_stats}, - system_stats => $_->{system_stats}, - hourly_stat_last => $_->{average_values}->{hourly_stats}->[0], - uptime => $_->{device_uptime}, - wanmon_avg_values_last => $_->{wanmon_avg_values}->[0] - }; - } + $response = { + containers_stats => $data->{containers_stats}, + system_stats => $data->{system_stats}, + hourly_stat_last => $data->{average_values}->{hourly_stats}->[0], + uptime => $data->{device_uptime}, + wanmon_avg_values_last => $data->{wanmon_avg_values}->[0] + }; } return $response; diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm index 7191eedd9d..53c39fa77c 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm @@ -18,7 +18,7 @@ # limitations under the License. # -package apps::backup::veeam::vone::restapi::mode::repositories; +package cloud::zscaler::ztb::restapi::mode::gatewaymemory; use base qw(centreon::plugins::templates::counter); @@ -26,65 +26,66 @@ use strict; use warnings; use centreon::plugins::constants qw(:counters :values); use centreon::plugins::misc qw/is_excluded/; -use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); - -my $map_repository_state_numeric = { - unknown => 0, - ok => 1, - inaccessible => 2, - disconnected => 3, - outOfDate => 4, - warning => 5 -}; - -sub custom_space_usage_output { + +sub custom_memory_perfdata { + my ($self) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'B', + instances => $instances, + value => $self->{result_values}->{ $self->{key_values}->[0]->{name} }, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + total => $self->{result_values}->{total} + ); +} + +sub custom_memory_usage_output { my ($self, %options) = @_; my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); return sprintf( - "space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", + "memory usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", $total_size_value . " " . $total_size_unit, $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} ); } -sub custom_status_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - nlabel => 'repository.state.count', - instances => $self->{result_values}->{name}, - value => $map_repository_state_numeric->{ $self->{result_values}->{state} }, - min => 0 - ); -} - sub prefix_global_output { my ($self, %options) = @_; - return 'Number of repositories '; + return 'Number of gateways '; } -sub repository_long_output { +sub gateway_long_output { my ($self, %options) = @_; return sprintf( - "checking repository '%s' [type: %s]", - $options{instance_value}->{name}, - $options{instance_value}->{type} + "checking gateway '%s' [site: %s, cluster: %s]", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} ); } -sub prefix_repository_output { +sub prefix_gateway_output { my ($self, %options) = @_; return sprintf( - "repository '%s' [type: %s] ", - $options{instance_value}->{name}, - $options{instance_value}->{type} + "gateway '%s' [site: %s, cluster: %s] ", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} ); } @@ -94,16 +95,16 @@ sub set_counters { $self->{maps_counters_type} = [ { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, { - name => 'repositories', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_repository_output', cb_long_output => 'repository_long_output', indent_long_output => ' ', message_multiple => 'All repositories are ok', + name => 'gateways', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_gateway_output', cb_long_output => 'gateway_long_output', indent_long_output => ' ', message_multiple => 'All gateways are ok', group => [ - { name => 'status', type => COUNTER_MULTIPLE_INSTANCE }, - { name => 'space', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } }, + { name => 'memory', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } }, + { name => 'swap', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } } ] } ]; $self->{maps_counters}->{global} = [ - { label => 'repositories-detected', display_ok => 0, nlabel => 'repositories.detected.count', + { label => 'gateways-detected', display_ok => 0, nlabel => 'gateways.detected.count', unknown_default => '@0', set => { key_values => [ { name => 'detected' } ], @@ -115,47 +116,80 @@ sub set_counters { } ]; - $self->{maps_counters}->{space} = [ - { label => 'space-usage', nlabel => 'repository.space.usage.bytes', set => { - key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], - closure_custom_output => $self->can('custom_space_usage_output'), - perfdatas => [ - { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } - ] + $self->{maps_counters}->{memory} = [ + { label => 'memory-usage', nlabel => 'gateway.memory.usage.bytes', set => { + key_values => [ + { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + closure_custom_output => $self->can('custom_memory_usage_output'), + closure_custom_perfdata => $self->can('custom_memory_perfdata') } }, - { label => 'space-usage-free', display_ok => 0, nlabel => 'repository.space.free.bytes', set => { - key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], - closure_custom_output => $self->can('custom_space_usage_output'), - perfdatas => [ - { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } - ] + { label => 'memory-usage-free', display_ok => 0, nlabel => 'gateway.memory.free.bytes', set => { + key_values => [ + { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + closure_custom_output => $self->can('custom_memory_usage_output'), + closure_custom_perfdata => $self->can('custom_memory_perfdata') } }, - { label => 'space-usage-prct', display_ok => 0, nlabel => 'repository.space.usage.percentage', set => { - key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], - closure_custom_output => $self->can('custom_space_usage_output'), - perfdatas => [ - { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'name' } - ] + { label => 'memory-usage-prct', display_ok => 0, nlabel => 'gateway.memory.usage.percentage', set => { + key_values => [ + { name => 'prct_used' }, { name => 'free' }, { name => 'used' }, { name => 'prct_free' }, { name => 'total' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + closure_custom_output => $self->can('custom_memory_usage_output'), + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => $instances, + value => sprintf('%.2f', $self->{result_values}->{prct_used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => 100 + ); + } } } ]; - $self->{maps_counters}->{status} = [ - { - label => 'repository-status', - type => COUNTER_KIND_TEXT, - unknown_default => '%{state} =~ /unknown/', - warning_default => '%{state} =~ /warning|outofdate/', - critical_default => '%{state} =~ /inaccessible|disconnected/', + $self->{maps_counters}->{swap} = [ + { label => 'swap-usage', nlabel => 'gateway.swap.usage.bytes', set => { key_values => [ - { name => 'state' }, { name => 'name' }, { name => 'type' } + { name => 'used' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } ], - output_template => 'state: %s', - closure_custom_perfdata => $self->can('custom_status_perfdata'), - closure_custom_threshold_check => \&catalog_status_threshold_ng + output_template => 'swap used: %s %s', + output_change_bytes => 1, + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'B', + instances => $instances, + value => sprintf('%d', $self->{result_values}->{used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); + } } } ]; @@ -167,59 +201,80 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - 'filter-uid:s' => { name => 'filter_uid', default => '' }, - 'filter-name:s' => { name => 'filter_name', default => '' } + 'include-site-name:s' => { name => 'include_site_name', default => '' }, + 'exclude-site-name:s' => { name => 'exclude_site_name', default => '' }, + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-gateway-name:s' => { name => 'include_gateway_name', default => '' }, + 'exclude-gateway-name:s' => { name => 'exclude_gateway_name', default => '' }, + 'include-gateway-id:s' => { name => 'include_gateway_id', default => '' }, + 'exclude-gateway-id:s' => { name => 'exclude_gateway_id', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' } }); return $self; } -sub manage_selection { +sub check_options { my ($self, %options) = @_; + $self->SUPER::check_options(%options); - my $repositories = $options{custom}->get_repositories(); - - $self->{global} = { detected => 0 }; - $self->{repositories} = {}; - - foreach my $repo (@{$repositories->{items}}) { - next if is_excluded($repo->{repositoryUidInVbr}, $self->{option_results}->{filter_uid}); - next if is_excluded($repo->{name}, $self->{option_results}->{filter_name}); - - $self->{repositories}->{ $repo->{name} } = { - name => $repo->{name}, - type => $repo->{type}, - uid => $repo->{repositoryUidInVbr}, - space => { - name => $repo->{name}, - total => $repo->{capacityBytes}, - free => $repo->{freeSpaceBytes}, - used => $repo->{capacityBytes} - $repo->{freeSpaceBytes}, - prct_used => 100 - ($repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes}), - prct_free => $repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes} - }, - status => { - name => $repo->{name}, - type => $repo->{type}, - state => lc $repo->{state} - } - }; - $self->{global}->{detected}++; + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(gatewayName)'; } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { siteName => 1, clusterName => 1, gatewayName => 1 } + ); } -sub disco_format { +sub manage_selection { my ($self, %options) = @_; - $self->{output}->add_disco_format(elements => ['uid', 'name', 'type', 'state']); -} + my $gateways = $options{custom}->get_gateways(); -sub disco_show { - my ($self, %options) = @_; + $self->{global} = { detected => 0 }; + $self->{gateways} = {}; + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + $self->{gateways}->{ $gw->{gw_id} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name} + }; + + my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); + if (defined($resource->{system_stats})) { + my $total = $resource->{system_stats}->{memory}->{total} * 1024 * 1024 * 1024; + my $free = $resource->{system_stats}->{memory}->{free} * 1024 * 1024 * 1024; + my $used = $resource->{system_stats}->{memory}->{used} * 1024 * 1024 * 1024; + $self->{gateways}->{ $gw->{gw_id} }->{memory} = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + total => $total, + used => $used, + free => $free, + prct_used => 100 - ($free * 100 / $total), + prct_free => $free * 100 / $total + }; + + my $swap_used = $resource->{system_stats}->{swap}->{used} * 1024 * 1024 * 1024; + $self->{gateways}->{ $gw->{gw_id} }->{swap} = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + used => $swap_used + }; + } - my $repos = $self->manage_selection(custom => $options{custom}); - foreach (values %{$self->{repositories}}) { - $self->{output}->add_disco_entry(uid => $_->{uid}, name => $_->{name}, type => $_->{type}, state => $_->{status}->{state}); + $self->{global}->{detected}++; } } @@ -229,65 +284,86 @@ __END__ =head1 MODE -Check repositories. +Check gateway memory. =over 8 -=item B<--filter-uid> +=item B<--include-site-name> + +Include site names (regexp). -Filter repositories by UID (can be a regexp). +=item B<--exclude-site-name> -=item B<--filter-name> +Exclude site names (regexp). -Filter repositories by name (can be a regexp). +=item B<--include-cluster-name> -=item B<--unknown-repository-status> +Include cluster names (regexp). -Define the conditions to match for the status to be UNKNOWN (default: '%{state} =~ /unknown/'). -You can use the following variables: %{state}, %{name}, %{type} +=item B<--exclude-cluster-name> -=item B<--warning-repository-status> +Exclude cluster names (regexp). -Define the conditions to match for the status to be WARNING (default: '%{state} =~ /warning|outofdate/'). -You can use the following variables: %{state}, %{name}, %{type} +=item B<--include-gateway-name> -=item B<--critical-repository-status> +Include gateway names (regexp). -Define the conditions to match for the status to be CRITICAL (default: '%{state} =~ /inaccessible|disconnected/'). -You can use the following variables: %{state}, %{name}, %{type} +=item B<--exclude-gateway-name> -=item B<--warning-repositories-detected> +Exclude gateway names (regexp). + +=item B<--include-gateway-id> + +Include gateway IDs (regexp). + +=item B<--exclude-gateway-id> + +Exclude gateway IDs (regexp). + +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(gatewayName)') + +=item B<--warning-gateways-detected> Threshold. -=item B<--critical-repositories-detected> +=item B<--critical-gateways-detected> Threshold. -=item B<--warning-space-usage> +=item B<--warning-memory-usage> Threshold in bytes. -=item B<--critical-space-usage> +=item B<--critical-memory-usage> Threshold in bytes. -=item B<--warning-space-usage-free> +=item B<--warning-memory-usage-free> Threshold in bytes. -=item B<--critical-space-usage-free> +=item B<--critical-memory-usage-free> Threshold in bytes. -=item B<--warning-space-usage-prct> +=item B<--warning-memory-usage-prct> Threshold in percentage. -=item B<--critical-space-usage-prct> +=item B<--critical-memory-usage-prct> Threshold in percentage. +=item B<--warning-swap-usage> + +Threshold in bytes. + +=item B<--critical-swap-usage> + +Threshold in bytes. + =back =cut From 8c5c5ca27c75d9ed09f7e15336bbb1b7fbbda684 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Thu, 9 Jul 2026 08:47:54 -0400 Subject: [PATCH 04/12] wip --- .../zscaler/ztb/restapi/mode/gatewaystatus.pm | 292 +++++++++--------- src/cloud/zscaler/ztb/restapi/plugin.pm | 3 +- 2 files changed, 155 insertions(+), 140 deletions(-) diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm index 7191eedd9d..27c8b612d8 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm @@ -18,7 +18,7 @@ # limitations under the License. # -package apps::backup::veeam::vone::restapi::mode::repositories; +package cloud::zscaler::ztb::restapi::mode::gatewaystatus; use base qw(centreon::plugins::templates::counter); @@ -28,63 +28,41 @@ use centreon::plugins::constants qw(:counters :values); use centreon::plugins::misc qw/is_excluded/; use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); -my $map_repository_state_numeric = { - unknown => 0, - ok => 1, - inaccessible => 2, - disconnected => 3, - outOfDate => 4, - warning => 5 -}; - -sub custom_space_usage_output { +sub custom_status_output { my ($self, %options) = @_; - my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); - my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); - my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); return sprintf( - "space usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", - $total_size_value . " " . $total_size_unit, - $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, - $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} - ); -} - -sub custom_status_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - nlabel => 'repository.state.count', - instances => $self->{result_values}->{name}, - value => $map_repository_state_numeric->{ $self->{result_values}->{state} }, - min => 0 + "operational state: %s [desired state: %s]", + $self->{result_values}->{operationalState}, + $self->{result_values}->{desiredState} ); } sub prefix_global_output { my ($self, %options) = @_; - return 'Number of repositories '; + return 'Number of gateways '; } -sub repository_long_output { +sub gateway_long_output { my ($self, %options) = @_; return sprintf( - "checking repository '%s' [type: %s]", - $options{instance_value}->{name}, - $options{instance_value}->{type} + "checking gateway '%s' [site: %s, cluster: %s]", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} ); } -sub prefix_repository_output { +sub prefix_gateway_output { my ($self, %options) = @_; return sprintf( - "repository '%s' [type: %s] ", - $options{instance_value}->{name}, - $options{instance_value}->{type} + "gateway '%s' [site: %s, cluster: %s] ", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} ); } @@ -94,16 +72,17 @@ sub set_counters { $self->{maps_counters_type} = [ { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, { - name => 'repositories', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_repository_output', cb_long_output => 'repository_long_output', indent_long_output => ' ', message_multiple => 'All repositories are ok', + name => 'gateways', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_gateway_output', cb_long_output => 'gateway_long_output', indent_long_output => ' ', message_multiple => 'All gateways are ok', group => [ - { name => 'status', type => COUNTER_MULTIPLE_INSTANCE }, - { name => 'space', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } }, + { name => 'status', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } }, + { name => 'health', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } }, + { name => 'vrrp', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } } ] } ]; $self->{maps_counters}->{global} = [ - { label => 'repositories-detected', display_ok => 0, nlabel => 'repositories.detected.count', + { label => 'gateways-detected', display_ok => 0, nlabel => 'gateways.detected.count', unknown_default => '@0', set => { key_values => [ { name => 'detected' } ], @@ -115,46 +94,52 @@ sub set_counters { } ]; - $self->{maps_counters}->{space} = [ - { label => 'space-usage', nlabel => 'repository.space.usage.bytes', set => { - key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], - closure_custom_output => $self->can('custom_space_usage_output'), - perfdatas => [ - { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } - ] - } - }, - { label => 'space-usage-free', display_ok => 0, nlabel => 'repository.space.free.bytes', set => { - key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], - closure_custom_output => $self->can('custom_space_usage_output'), - perfdatas => [ - { template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1, label_extra_instance => 1, instance_use => 'name' } - ] + $self->{maps_counters}->{status} = [ + { + label => 'gateway-status', + type => COUNTER_KIND_TEXT, + critical_default => '%{desiredState} ne %{operationalState}', + set => { + key_values => [ + { name => 'desiredState' }, { name => 'operationalState' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng } - }, - { label => 'space-usage-prct', display_ok => 0, nlabel => 'repository.space.usage.percentage', set => { - key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'name' } ], - closure_custom_output => $self->can('custom_space_usage_output'), - perfdatas => [ - { template => '%.2f', min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'name' } - ] + } + ]; + + $self->{maps_counters}->{health} = [ + { + label => 'gateway-health', + type => COUNTER_KIND_TEXT, + critical_default => '%{healthColor} !~ /green/', + set => { + key_values => [ + { name => 'healthColor' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + output_template => 'health color: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng } } ]; - $self->{maps_counters}->{status} = [ + $self->{maps_counters}->{vrrp} = [ { - label => 'repository-status', + label => 'gateway-vrrp-status', type => COUNTER_KIND_TEXT, - unknown_default => '%{state} =~ /unknown/', - warning_default => '%{state} =~ /warning|outofdate/', - critical_default => '%{state} =~ /inaccessible|disconnected/', + critical_default => '%{vrrpState} =~ /fault/i', set => { key_values => [ - { name => 'state' }, { name => 'name' }, { name => 'type' } + { name => 'vrrpState' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } ], - output_template => 'state: %s', - closure_custom_perfdata => $self->can('custom_status_perfdata'), + output_template => 'VRRP state: %s', + closure_custom_perfdata => sub { return 0; }, closure_custom_threshold_check => \&catalog_status_threshold_ng } } @@ -167,8 +152,14 @@ sub new { bless $self, $class; $options{options}->add_options(arguments => { - 'filter-uid:s' => { name => 'filter_uid', default => '' }, - 'filter-name:s' => { name => 'filter_name', default => '' } + 'include-site-name:s' => { name => 'include_site_name', default => '' }, + 'exclude-site-name:s' => { name => 'exclude_site_name', default => '' }, + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-gateway-name:s' => { name => 'include_gateway_name', default => '' }, + 'exclude-gateway-name:s' => { name => 'exclude_gateway_name', default => '' }, + 'include-gateway-id:s' => { name => 'include_gateway_id', default => '' }, + 'exclude-gateway-id:s' => { name => 'exclude_gateway_id', default => '' }, }); return $self; @@ -177,49 +168,42 @@ sub new { sub manage_selection { my ($self, %options) = @_; - my $repositories = $options{custom}->get_repositories(); + my $gateways = $options{custom}->get_gateways(); $self->{global} = { detected => 0 }; - $self->{repositories} = {}; - - foreach my $repo (@{$repositories->{items}}) { - next if is_excluded($repo->{repositoryUidInVbr}, $self->{option_results}->{filter_uid}); - next if is_excluded($repo->{name}, $self->{option_results}->{filter_name}); - - $self->{repositories}->{ $repo->{name} } = { - name => $repo->{name}, - type => $repo->{type}, - uid => $repo->{repositoryUidInVbr}, - space => { - name => $repo->{name}, - total => $repo->{capacityBytes}, - free => $repo->{freeSpaceBytes}, - used => $repo->{capacityBytes} - $repo->{freeSpaceBytes}, - prct_used => 100 - ($repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes}), - prct_free => $repo->{freeSpaceBytes} * 100 / $repo->{capacityBytes} - }, + $self->{gateways} = {}; + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + $self->{gateways}->{ $gw->{gw_id} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, status => { - name => $repo->{name}, - type => $repo->{type}, - state => lc $repo->{state} + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + operationalState => $gw->{gw_operational_state}, + desiredState => $gw->{gw_desired_state} + }, + health => { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + healthColor => $gw->{gw_health_color} + }, + vrrp => { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + vrrpState => $gw->{gw_vrrp_state} } }; - $self->{global}->{detected}++; - } -} - -sub disco_format { - my ($self, %options) = @_; - - $self->{output}->add_disco_format(elements => ['uid', 'name', 'type', 'state']); -} - -sub disco_show { - my ($self, %options) = @_; - my $repos = $self->manage_selection(custom => $options{custom}); - foreach (values %{$self->{repositories}}) { - $self->{output}->add_disco_entry(uid => $_->{uid}, name => $_->{name}, type => $_->{type}, state => $_->{status}->{state}); + $self->{global}->{detected}++; } } @@ -229,64 +213,94 @@ __END__ =head1 MODE -Check repositories. +Check gateway status. =over 8 -=item B<--filter-uid> +=item B<--include-site-name> + +Include site names (regexp). + +=item B<--exclude-site-name> + +Exclude site names (regexp). + +=item B<--include-cluster-name> + +Include cluster names (regexp). + +=item B<--exclude-cluster-name> -Filter repositories by UID (can be a regexp). +Exclude cluster names (regexp). -=item B<--filter-name> +=item B<--include-gateway-name> -Filter repositories by name (can be a regexp). +Include gateway names (regexp). -=item B<--unknown-repository-status> +=item B<--exclude-gateway-name> -Define the conditions to match for the status to be UNKNOWN (default: '%{state} =~ /unknown/'). -You can use the following variables: %{state}, %{name}, %{type} +Exclude gateway names (regexp). -=item B<--warning-repository-status> +=item B<--include-gateway-id> -Define the conditions to match for the status to be WARNING (default: '%{state} =~ /warning|outofdate/'). -You can use the following variables: %{state}, %{name}, %{type} +Include gateway IDs (regexp). -=item B<--critical-repository-status> +=item B<--exclude-gateway-id> -Define the conditions to match for the status to be CRITICAL (default: '%{state} =~ /inaccessible|disconnected/'). -You can use the following variables: %{state}, %{name}, %{type} +Exclude gateway IDs (regexp). -=item B<--warning-repositories-detected> +=item B<--warning-gateways-detected> Threshold. -=item B<--critical-repositories-detected> +=item B<--critical-gateways-detected> Threshold. -=item B<--warning-space-usage> +=item B<--unknown-gateway-status> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{desiredState}, %{operationalState} + +=item B<--warning-gateway-status> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{desiredState}, %{operationalState} + +=item B<--critical-gateway-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{desiredState} ne %{operationalState}'). +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{desiredState}, %{operationalState} + +=item B<--unknown-gateway-health> -Threshold in bytes. +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{healthColor} -=item B<--critical-space-usage> +=item B<--warning-gateway-health> -Threshold in bytes. +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{healthColor} -=item B<--warning-space-usage-free> +=item B<--critical-gateway-health> -Threshold in bytes. +Define the conditions to match for the status to be CRITICAL (default: '%{healthColor} !~ /green/'). +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{healthColor} -=item B<--critical-space-usage-free> +=item B<--unknown-gateway-vrrp-status> -Threshold in bytes. +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{vrrpState} -=item B<--warning-space-usage-prct> +=item B<--warning-gateway-vrrp-status> -Threshold in percentage. +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{vrrpState} -=item B<--critical-space-usage-prct> +=item B<--critical-gateway-vrrp-status> -Threshold in percentage. +Define the conditions to match for the status to be CRITICAL (default: '%{vrrpState} =~ /fault/i'). +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{vrrpState} =back diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm index 08f793cc19..da0b9c2327 100644 --- a/src/cloud/zscaler/ztb/restapi/plugin.pm +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -31,7 +31,8 @@ sub new { $self->{modes} = { 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', - 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory' + 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory', + 'gateway-status' => 'cloud::zscaler::ztb::restapi::mode::gatewaystatus' }; $self->{custom_modes}->{api} = 'cloud::zscaler::ztb::restapi::custom::api'; From cf808107d3634cb166fd121d85d44929d2b27e39 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Thu, 9 Jul 2026 10:56:46 -0400 Subject: [PATCH 05/12] wip --- .../zscaler/ztb/restapi/mode/gatewaycpu.pm | 225 +++++++++++++ .../zscaler/ztb/restapi/mode/gatewaydisk.pm | 301 ++++++++++++++++++ .../zscaler/ztb/restapi/mode/gatewaymemory.pm | 2 +- src/cloud/zscaler/ztb/restapi/plugin.pm | 2 + 4 files changed, 529 insertions(+), 1 deletion(-) create mode 100644 src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm create mode 100644 src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm new file mode 100644 index 0000000000..9b2ff77e5c --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm @@ -0,0 +1,225 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::mode::gatewaycpu; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of gateways '; +} + +sub prefix_gateway_output { + my ($self, %options) = @_; + + return sprintf( + "gateway '%s' [site: %s, cluster: %s] ", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { name => 'gateways', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_gateway_output', message_multiple => 'All gateways are ok', skipped_code => { NO_VALUE() => 1 } } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'gateways-detected', display_ok => 0, nlabel => 'gateways.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{gateways} = [ + { label => 'cpu-utilization', nlabel => 'gateway.cpu.utilization.percentage', set => { + key_values => [ + { name => 'prct_used' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + output_template => 'CPU average usage: %.2f %%', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => $instances, + value => sprintf('%.2f', $self->{result_values}->{prct_used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => 100 + ); + } + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'include-site-name:s' => { name => 'include_site_name', default => '' }, + 'exclude-site-name:s' => { name => 'exclude_site_name', default => '' }, + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-gateway-name:s' => { name => 'include_gateway_name', default => '' }, + 'exclude-gateway-name:s' => { name => 'exclude_gateway_name', default => '' }, + 'include-gateway-id:s' => { name => 'include_gateway_id', default => '' }, + 'exclude-gateway-id:s' => { name => 'exclude_gateway_id', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(gatewayName)'; + } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { siteName => 1, clusterName => 1, gatewayName => 1 } + ); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + + $self->{global} = { detected => 0 }; + $self->{gateways} = {}; + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + $self->{gateways}->{ $gw->{gw_id} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name} + }; + + my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); + if (defined($resource->{hourly_stat_last})) { + $self->{gateways}->{ $gw->{gw_id} }->{prct_used} = $resource->{hourly_stat_last}->{cpu_used_avg}; + } + + $self->{global}->{detected}++; + } +} + +1; + +__END__ + +=head1 MODE + +Check gateway CPU. + +=over 8 + +=item B<--include-site-name> + +Include site names (regexp). + +=item B<--exclude-site-name> + +Exclude site names (regexp). + +=item B<--include-cluster-name> + +Include cluster names (regexp). + +=item B<--exclude-cluster-name> + +Exclude cluster names (regexp). + +=item B<--include-gateway-name> + +Include gateway names (regexp). + +=item B<--exclude-gateway-name> + +Exclude gateway names (regexp). + +=item B<--include-gateway-id> + +Include gateway IDs (regexp). + +=item B<--exclude-gateway-id> + +Exclude gateway IDs (regexp). + +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(gatewayName)') + +=item B<--warning-gateways-detected> + +Threshold. + +=item B<--critical-gateways-detected> + +Threshold. + +=item B<--warning-cpu-utilization> + +Threshold in percentage. + +=item B<--critical-cpu-utilization> + +Threshold in percentage. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm new file mode 100644 index 0000000000..61909f0cc4 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm @@ -0,0 +1,301 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::mode::gatewaydisk; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; + +sub custom_disk_perfdata { + my ($self) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'B', + instances => $instances, + value => sprintf('%d', $self->{result_values}->{ $self->{key_values}->[0]->{name} }), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + total => $self->{result_values}->{total} + ); +} + +sub custom_disk_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + return sprintf( + "disk usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of gateways '; +} + +sub prefix_gateway_output { + my ($self, %options) = @_; + + return sprintf( + "gateway '%s' [site: %s, cluster: %s] ", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { name => 'gateways', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_gateway_output', message_multiple => 'All gateways are ok', skipped_code => { NO_VALUE() => 1 } } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'gateways-detected', display_ok => 0, nlabel => 'gateways.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{gateways} = [ + { label => 'disk-usage', nlabel => 'gateway.disk.usage.bytes', set => { + key_values => [ + { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + closure_custom_output => $self->can('custom_disk_usage_output'), + closure_custom_perfdata => $self->can('custom_disk_perfdata') + } + }, + { label => 'disk-usage-free', display_ok => 0, nlabel => 'gateway.disk.free.bytes', set => { + key_values => [ + { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + closure_custom_output => $self->can('custom_disk_usage_output'), + closure_custom_perfdata => $self->can('custom_disk_perfdata') + } + }, + { label => 'disk-usage-prct', display_ok => 0, nlabel => 'gateway.disk.usage.percentage', set => { + key_values => [ + { name => 'prct_used' }, { name => 'free' }, { name => 'used' }, { name => 'prct_free' }, { name => 'total' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + closure_custom_output => $self->can('custom_memory_usage_output'), + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => $instances, + value => sprintf('%.2f', $self->{result_values}->{prct_used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => 100 + ); + } + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'include-site-name:s' => { name => 'include_site_name', default => '' }, + 'exclude-site-name:s' => { name => 'exclude_site_name', default => '' }, + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-gateway-name:s' => { name => 'include_gateway_name', default => '' }, + 'exclude-gateway-name:s' => { name => 'exclude_gateway_name', default => '' }, + 'include-gateway-id:s' => { name => 'include_gateway_id', default => '' }, + 'exclude-gateway-id:s' => { name => 'exclude_gateway_id', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(gatewayName)'; + } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { siteName => 1, clusterName => 1, gatewayName => 1 } + ); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + + $self->{global} = { detected => 0 }; + $self->{gateways} = {}; + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + $self->{gateways}->{ $gw->{gw_id} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name} + }; + + my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); + if (defined($resource->{system_stats})) { + my $total = $resource->{system_stats}->{disk}->{total} * 1024 * 1024 * 1024; + my $free = $resource->{system_stats}->{disk}->{free} * 1024 * 1024 * 1024; + my $used = $resource->{system_stats}->{disk}->{used} * 1024 * 1024 * 1024; + $self->{gateways}->{ $gw->{gw_id} }->{total} = $total; + $self->{gateways}->{ $gw->{gw_id} }->{used} = $used; + $self->{gateways}->{ $gw->{gw_id} }->{free} = $free; + $self->{gateways}->{ $gw->{gw_id} }->{prct_used} = 100 - ($free * 100 / $total); + $self->{gateways}->{ $gw->{gw_id} }->{prct_free} = $free * 100 / $total; + } + + $self->{global}->{detected}++; + } +} + +1; + +__END__ + +=head1 MODE + +Check gateway disk. + +=over 8 + +=item B<--include-site-name> + +Include site names (regexp). + +=item B<--exclude-site-name> + +Exclude site names (regexp). + +=item B<--include-cluster-name> + +Include cluster names (regexp). + +=item B<--exclude-cluster-name> + +Exclude cluster names (regexp). + +=item B<--include-gateway-name> + +Include gateway names (regexp). + +=item B<--exclude-gateway-name> + +Exclude gateway names (regexp). + +=item B<--include-gateway-id> + +Include gateway IDs (regexp). + +=item B<--exclude-gateway-id> + +Exclude gateway IDs (regexp). + +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(gatewayName)') + +=item B<--warning-gateways-detected> + +Threshold. + +=item B<--critical-gateways-detected> + +Threshold. + +=item B<--warning-disk-usage> + +Threshold in bytes. + +=item B<--critical-disk-usage> + +Threshold in bytes. + +=item B<--warning-disk-usage-free> + +Threshold in bytes. + +=item B<--critical-disk-usage-free> + +Threshold in bytes. + +=item B<--warning-disk-usage-prct> + +Threshold in percentage. + +=item B<--critical-disk-usage-prct> + +Threshold in percentage. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm index 53c39fa77c..9cdc442eee 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm @@ -39,7 +39,7 @@ sub custom_memory_perfdata { nlabel => $self->{nlabel}, unit => 'B', instances => $instances, - value => $self->{result_values}->{ $self->{key_values}->[0]->{name} }, + value => sprintf('%d', $self->{result_values}->{ $self->{key_values}->[0]->{name} }), warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), min => 0, diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm index da0b9c2327..c30c2c3935 100644 --- a/src/cloud/zscaler/ztb/restapi/plugin.pm +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -31,6 +31,8 @@ sub new { $self->{modes} = { 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', + 'gateway-cpu' => 'cloud::zscaler::ztb::restapi::mode::gatewaycpu', + 'gateway-disk' => 'cloud::zscaler::ztb::restapi::mode::gatewaydisk', 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory', 'gateway-status' => 'cloud::zscaler::ztb::restapi::mode::gatewaystatus' }; From 880e1b42c8e0734df7f464ca129fe0f7fdd99386 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Thu, 9 Jul 2026 11:25:28 -0400 Subject: [PATCH 06/12] wip --- src/cloud/zscaler/ztb/restapi/custom/api.pm | 2 +- .../ztb/restapi/mode/gatewaycontainers.pm | 373 ++++++++++++++++++ src/cloud/zscaler/ztb/restapi/plugin.pm | 11 +- 3 files changed, 380 insertions(+), 6 deletions(-) create mode 100644 src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm diff --git a/src/cloud/zscaler/ztb/restapi/custom/api.pm b/src/cloud/zscaler/ztb/restapi/custom/api.pm index 617431ea7b..ade80e34ea 100644 --- a/src/cloud/zscaler/ztb/restapi/custom/api.pm +++ b/src/cloud/zscaler/ztb/restapi/custom/api.pm @@ -362,7 +362,7 @@ sub get_gateway_resource { my $response = {}; if (ref($data) eq 'HASH') { $response = { - containers_stats => $data->{containers_stats}, + container_stats => $data->{containers_stats}->{container_stats}, system_stats => $data->{system_stats}, hourly_stat_last => $data->{average_values}->{hourly_stats}->[0], uptime => $data->{device_uptime}, diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm new file mode 100644 index 0000000000..56fc827521 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm @@ -0,0 +1,373 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::mode::gatewaycontainers; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; + +sub custom_memory_perfdata { + my ($self) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'B', + instances => $instances, + value => sprintf('%d', $self->{result_values}->{ $self->{key_values}->[0]->{name} }), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + total => $self->{result_values}->{total} + ); +} + +sub custom_memory_usage_output { + my ($self, %options) = @_; + + my ($total_size_value, $total_size_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{total}); + my ($total_used_value, $total_used_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{used}); + my ($total_free_value, $total_free_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{free}); + return sprintf( + "memory usage total: %s used: %s (%.2f%%) free: %s (%.2f%%)", + $total_size_value . " " . $total_size_unit, + $total_used_value . " " . $total_used_unit, $self->{result_values}->{prct_used}, + $total_free_value . " " . $total_free_unit, $self->{result_values}->{prct_free} + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of gateways '; +} + +sub gateway_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking gateway '%s' [site: %s, cluster: %s]", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} + ); +} + +sub prefix_gateway_output { + my ($self, %options) = @_; + + return sprintf( + "gateway '%s' [site: %s, cluster: %s] ", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} + ); +} + +sub prefix_container_output { + my ($self, %options) = @_; + + return sprintf( + "container '%s' ", + $options{instance_value}->{containerName} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { + name => 'gateways', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_gateway_output', cb_long_output => 'gateway_long_output', indent_long_output => ' ', message_multiple => 'All gateways are ok', + group => [ + { name => 'containers', type => COUNTER_MULTIPLE_SUBINSTANCE, cb_prefix_output => 'prefix_container_output', skipped_code => { NO_VALUE() => 1 } } + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'gateways-detected', display_ok => 0, nlabel => 'gateways.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{containers} = [ + { label => 'cpu-utilization', nlabel => 'gateway.cpu.utilization.percentage', set => { + key_values => [ + { name => 'cpu_used' }, { name => 'containerName' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + output_template => 'CPU usage: %.2f %%', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => $instances, + value => sprintf('%.2f', $self->{result_values}->{cpu_used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => 100 + ); + } + } + }, + { label => 'memory-usage', nlabel => 'gateway.memory.usage.bytes', set => { + key_values => [ + { name => 'mem_used' }, { name => 'containerName' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + output_template => 'memory used: %s %s', + output_change_bytes => 1, + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'B', + instances => $instances, + value => sprintf('%d', $self->{result_values}->{mem_used}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); + } + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'include-site-name:s' => { name => 'include_site_name', default => '' }, + 'exclude-site-name:s' => { name => 'exclude_site_name', default => '' }, + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-gateway-name:s' => { name => 'include_gateway_name', default => '' }, + 'exclude-gateway-name:s' => { name => 'exclude_gateway_name', default => '' }, + 'include-gateway-id:s' => { name => 'include_gateway_id', default => '' }, + 'exclude-gateway-id:s' => { name => 'exclude_gateway_id', default => '' }, + 'include-container-name:s' => { name => 'include_container_name', default => '' }, + 'exclude-container-name:s' => { name => 'exclude_container_name', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(gatewayName) %(containerName)'; + } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { siteName => 1, clusterName => 1, gatewayName => 1, containerName => 1 } + ); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + + $self->{global} = { detected => 0 }; + $self->{gateways} = {}; + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + $self->{gateways}->{ $gw->{gw_id} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + containers => {} + }; + + my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); + if (defined($resource->{container_stats})) { + foreach my $cstat (@{$resource->{container_stats}}) { + next if is_excluded($cstat->{cname}, $self->{option_results}->{include_container_name}, $self->{option_results}->{exclude_container_name}); + + my $mem_used = centreon::plugins::misc::convert_bytes_ng(value => $cstat->{memory}); + $self->{gateways}->{ $gw->{gw_id} }->{containers}->{ $cstat->{cname} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + containerName => $cstat->{cname}, + mem_used => $mem_used, + cpu_used => $cstat->{cpu} + }; + } + } + + $self->{global}->{detected}++; + } +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['siteName', 'clusterName', 'gatewayName', 'containerName']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); + if (defined($resource->{container_stats})) { + foreach my $cstat (@{$resource->{container_stats}}) { + next if is_excluded($cstat->{cname}, $self->{option_results}->{include_container_name}, $self->{option_results}->{exclude_container_name}); + + $self->{output}->add_disco_entry( + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + containerName => $cstat->{cname}, + ); + } + } + } +} + +1; + +__END__ + +=head1 MODE + +Check gateway containers CPU and memory usage. + +=over 8 + +=item B<--include-container-name> + +Include container names (regexp). + +=item B<--exclude-container-name> + +Exclude container names (regexp). + +=item B<--include-site-name> + +Include site names (regexp). + +=item B<--exclude-site-name> + +Exclude site names (regexp). + +=item B<--include-cluster-name> + +Include cluster names (regexp). + +=item B<--exclude-cluster-name> + +Exclude cluster names (regexp). + +=item B<--include-gateway-name> + +Include gateway names (regexp). + +=item B<--exclude-gateway-name> + +Exclude gateway names (regexp). + +=item B<--include-gateway-id> + +Include gateway IDs (regexp). + +=item B<--exclude-gateway-id> + +Exclude gateway IDs (regexp). + +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(gatewayName) %(containerName)') + +=item B<--warning-gateways-detected> + +Threshold. + +=item B<--critical-gateways-detected> + +Threshold. + +=item B<--warning-memory-usage> + +Threshold in bytes. + +=item B<--critical-memory-usage> + +Threshold in bytes. + +=item B<--warning-cpu-utilization> + +Threshold in percentage. + +=item B<--critical-cpu-utilization> + +Threshold in percentage. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm index c30c2c3935..3902ac1b85 100644 --- a/src/cloud/zscaler/ztb/restapi/plugin.pm +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -30,11 +30,12 @@ sub new { bless $self, $class; $self->{modes} = { - 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', - 'gateway-cpu' => 'cloud::zscaler::ztb::restapi::mode::gatewaycpu', - 'gateway-disk' => 'cloud::zscaler::ztb::restapi::mode::gatewaydisk', - 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory', - 'gateway-status' => 'cloud::zscaler::ztb::restapi::mode::gatewaystatus' + 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', + 'gateway-containers' => 'cloud::zscaler::ztb::restapi::mode::gatewaycontainers', + 'gateway-cpu' => 'cloud::zscaler::ztb::restapi::mode::gatewaycpu', + 'gateway-disk' => 'cloud::zscaler::ztb::restapi::mode::gatewaydisk', + 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory', + 'gateway-status' => 'cloud::zscaler::ztb::restapi::mode::gatewaystatus' }; $self->{custom_modes}->{api} = 'cloud::zscaler::ztb::restapi::custom::api'; From f6a12105fc811dca24d51d6b5dcf4b408704bd19 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Fri, 10 Jul 2026 03:52:58 -0400 Subject: [PATCH 07/12] wip --- .../ztb/restapi/mode/gatewayinterfaces.pm | 598 ++++++++++++++++++ src/cloud/zscaler/ztb/restapi/plugin.pm | 1 + 2 files changed, 599 insertions(+) create mode 100644 src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm new file mode 100644 index 0000000000..8c09f46b86 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm @@ -0,0 +1,598 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::mode::gatewayinterfaces; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use Digest::SHA qw/sha256_hex/; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub custom_traffic_perfdata { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + my ($warning, $critical); + if ($self->{instance_mode}->{option_results}->{traffic_unit} eq 'percent_delta' && defined($self->{result_values}->{speed})) { + $warning = $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}, total => $self->{result_values}->{speed}, cast_int => 1); + $critical = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}, total => $self->{result_values}->{speed}, cast_int => 1); + } elsif ($self->{instance_mode}->{option_results}->{traffic_unit} =~ /bps|counter/) { + $warning = $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}); + $critical = $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}); + } + + if ($self->{instance_mode}->{option_results}->{traffic_unit} eq 'counter') { + my $nlabel = $self->{nlabel}; + $nlabel =~ s/bitspersecond/bits/; + $self->{output}->perfdata_add( + nlabel => $nlabel, + unit => 'b', + instances => $instances, + value => $self->{result_values}->{traffic_counter}, + warning => $warning, + critical => $critical, + min => 0 + ); + } else { + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => $instances, + value => sprintf('%.2f', $self->{result_values}->{traffic_per_seconds}), + warning => $warning, + critical => $critical, + min => 0, max => $self->{result_values}->{speed} + ); + } +} + +sub custom_traffic_threshold { + my ($self, %options) = @_; + + my $exit = 'ok'; + if ($self->{instance_mode}->{option_results}->{traffic_unit} eq 'percent_delta' && defined($self->{result_values}->{speed})) { + $exit = $self->{perfdata}->threshold_check(value => $self->{result_values}->{traffic_prct}, threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]); + } elsif ($self->{instance_mode}->{option_results}->{traffic_unit} eq 'bps') { + $exit = $self->{perfdata}->threshold_check(value => $self->{result_values}->{traffic_per_seconds}, threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]); + } elsif ($self->{instance_mode}->{option_results}->{traffic_unit} eq 'counter') { + $exit = $self->{perfdata}->threshold_check(value => $self->{result_values}->{traffic_counter}, threshold => [ { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, { label => 'warning-' . $self->{thlabel}, exit_litteral => 'warning' } ]); + } + return $exit; +} + +sub custom_traffic_calc { + my ($self, %options) = @_; + + $self->{result_values}->{traffic_per_seconds} = ($options{new_datas}->{ $self->{instance} . '_' . $options{extra_options}->{label_ref} } - $options{old_datas}->{ $self->{instance} . '_' . $options{extra_options}->{label_ref} }) / $options{delta_time}; + $self->{result_values}->{traffic_counter} = $options{new_datas}->{ $self->{instance} . '_' . $options{extra_options}->{label_ref} }; + + $self->{result_values}->{traffic_per_seconds} = sprintf('%d', $self->{result_values}->{traffic_per_seconds}); + + if (defined($options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}}) && + $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}} ne '' && + $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}} > 0) { + $self->{result_values}->{traffic_prct} = $self->{result_values}->{traffic_per_seconds} * 100 / $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}}; + $self->{result_values}->{speed} = $options{new_datas}->{$self->{instance} . '_speed_' . $options{extra_options}->{label_ref}}; + } + + $self->{result_values}->{label} = $options{extra_options}->{label_ref}; + $self->{result_values}->{gatewayName} = $options{new_datas}->{$self->{instance} . '_gatewayName'}; + $self->{result_values}->{clusterName} = $options{new_datas}->{$self->{instance} . '_clusterName'}; + $self->{result_values}->{siteName} = $options{new_datas}->{$self->{instance} . '_siteName'}; + $self->{result_values}->{interfaceName} = $options{new_datas}->{$self->{instance} . '_interfaceName'}; + return 0; +} + +sub custom_traffic_output { + my ($self, %options) = @_; + + my ($traffic_value, $traffic_unit) = $self->{perfdata}->change_bytes(value => $self->{result_values}->{traffic_per_seconds}, network => 1); + return sprintf( + 'traffic %s: %s/s (%s)', + $self->{result_values}->{label}, $traffic_value . $traffic_unit, + defined($self->{result_values}->{traffic_prct}) ? sprintf('%.2f%%', $self->{result_values}->{traffic_prct}) : '-' + ); +} + +sub custom_interface_status_output { + my ($self, %options) = @_; + + return sprintf( + "operational state: %s [admin state: %s]", + $self->{result_values}->{operationalState}, + $self->{result_values}->{adminState} + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of gateways '; +} + +sub gateway_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking gateway '%s' [site: %s, cluster: %s]", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} + ); +} + +sub prefix_gateway_output { + my ($self, %options) = @_; + + return sprintf( + "gateway '%s' [site: %s, cluster: %s] ", + $options{instance_value}->{gatewayName}, + $options{instance_value}->{siteName}, + $options{instance_value}->{clusterName} + ); +} + +sub prefix_interface_output { + my ($self, %options) = @_; + + return sprintf( + "interface '%s' ", + $options{instance_value}->{interfaceName} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { + name => 'gateways', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_gateway_output', cb_long_output => 'gateway_long_output', indent_long_output => ' ', message_multiple => 'All gateways are ok', + group => [ + { name => 'interfaces', type => COUNTER_MULTIPLE_SUBINSTANCE, cb_prefix_output => 'prefix_interface_output', skipped_code => { NO_VALUE() => 1 } } + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'gateways-detected', display_ok => 0, nlabel => 'gateways.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{interfaces} = [ + { + label => 'interface-status', + type => COUNTER_KIND_TEXT, + critical_default => '%{adminState} =~ /up/ and %{operationalState} !~ /up/', + set => { + key_values => [ + { name => 'adminState' }, { name => 'operationalState' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' }, { name => 'interfaceName' } + ], + closure_custom_output => $self->can('custom_interface_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'interface-traffic-in', nlabel => 'gateway.interface.traffic.in.bitspersecond', set => { + key_values => [ + { name => 'in', diff => 1 }, { name => 'speed_in' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' }, { name => 'interfaceName' } + ], + closure_custom_calc => $self->can('custom_traffic_calc'), closure_custom_calc_extra_options => { label_ref => 'in' }, + closure_custom_output => $self->can('custom_traffic_output'), + closure_custom_perfdata => $self->can('custom_traffic_perfdata'), + closure_custom_threshold_check => $self->can('custom_traffic_threshold') + } + }, + { label => 'interface-traffic-out', nlabel => 'gateway.interface.traffic.out.bitspersecond', set => { + key_values => [ + { name => 'out', diff => 1 }, { name => 'speed_out' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' }, { name => 'interfaceName' } + ], + closure_custom_calc => $self->can('custom_traffic_calc'), closure_custom_calc_extra_options => { label_ref => 'out' }, + closure_custom_output => $self->can('custom_traffic_output'), + closure_custom_perfdata => $self->can('custom_traffic_perfdata'), + closure_custom_threshold_check => $self->can('custom_traffic_threshold') + } + }, + { label => 'interface-jitter', nlabel => 'gateway.interface.jitter.milliseconds', set => { + key_values => [ + { name => 'jitter' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' }, { name => 'interfaceName' } + ], + output_template => 'jitter: %s ms', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'ms', + instances => $instances, + value => $self->{result_values}->{jitter}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); + } + } + }, + { label => 'interface-latency', nlabel => 'gateway.interface.latency.milliseconds', set => { + key_values => [ + { name => 'latency' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' }, { name => 'interfaceName' } + ], + output_template => 'latency: %s ms', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => 'ms', + instances => $instances, + value => $self->{result_values}->{latency}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); + } + } + }, + { label => 'interface-loss', nlabel => 'gateway.interface.loss.percentage', set => { + key_values => [ + { name => 'loss' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' }, { name => 'interfaceName' } + ], + output_template => 'loss: %.2f %%', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => $instances, + value => sprintf('%.2f', $self->{result_values}->{loss}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => 100 + ); + } + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'include-site-name:s' => { name => 'include_site_name', default => '' }, + 'exclude-site-name:s' => { name => 'exclude_site_name', default => '' }, + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-gateway-name:s' => { name => 'include_gateway_name', default => '' }, + 'exclude-gateway-name:s' => { name => 'exclude_gateway_name', default => '' }, + 'include-gateway-id:s' => { name => 'include_gateway_id', default => '' }, + 'exclude-gateway-id:s' => { name => 'exclude_gateway_id', default => '' }, + 'include-interface-name:s' => { name => 'include_interface_name', default => '' }, + 'exclude-interface-name:s' => { name => 'exclude_interface_name', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' }, + 'traffic-unit:s' => { name => 'traffic_unit', default => 'percent_delta' }, + 'speed:s' => { name => 'speed' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(gatewayName) %(interfaceName)'; + } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { siteName => 1, clusterName => 1, gatewayName => 1, interfaceName => 1 } + ); + + if (defined($self->{option_results}->{speed}) && $self->{option_results}->{speed} ne '') { + if ($self->{option_results}->{speed} !~ /^[0-9]+(\.[0-9]+){0,1}$/) { + $self->{output}->add_option_msg(short_msg => "Speed must be a positive number '" . $self->{option_results}->{speed} . "' (can be a float also)"); + $self->{output}->option_exit(); + } else { + $self->{option_results}->{speed} *= 1000000; + } + } + + $self->{option_results}->{traffic_unit} = 'percent_delta' + if (!defined($self->{option_results}->{traffic_unit}) || + $self->{option_results}->{traffic_unit} eq '' || + $self->{option_results}->{traffic_unit} eq '%'); + if ($self->{option_results}->{traffic_unit} !~ /^(?:percent|percent_delta|bps|counter)$/) { + $self->{output}->add_option_msg(short_msg => 'Wrong option --traffic-unit'); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + + $self->{global} = { detected => 0 }; + $self->{gateways} = {}; + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + $self->{gateways}->{ $gw->{gw_id} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + interfaces => {} + }; + + my $interfaces = $options{custom}->get_gateway_interfaces(gateway_id => $gw->{gw_id}); + my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); + foreach my $int (@$interfaces) { + next if is_excluded($int->{name}, $self->{option_results}->{include_interface_name}, $self->{option_results}->{exclude_interface_name}); + + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} } = { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + interfaceName => $int->{name}, + adminState => $int->{admin_state}, + operationalState => $int->{operational_state} + }; + + if (defined($int->{if_stats}->{rx_bytes})) { + # format: 1G + my $speed = centreon::plugins::misc::convert_bytes_ng(value => $int->{if_stats}->{if_speed}); + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} }->{in} = $int->{if_stats}->{rx_bytes} * 8; + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} }->{out} = $int->{if_stats}->{tx_bytes} * 8; + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} }->{speed_in} = defined($self->{option_results}->{speed}) && $self->{option_results}->{speed} ne '' ? $self->{option_results}->{speed} : $speed; + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} }->{speed_out} = defined($self->{option_results}->{speed}) && $self->{option_results}->{speed} ne '' ? $self->{option_results}->{speed} : $speed; + } + + if (defined($resource->{wanmon_avg_values_last}->{interfaces})) { + foreach (@{$resource->{wanmon_avg_values_last}->{interfaces}}) { + if ($int->{name} eq $_->{wan_intf_name}) { + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} }->{loss} = $_->{loss}; + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} }->{jitter} = $_->{jitter}; + $self->{gateways}->{ $gw->{gw_id} }->{interfaces}->{ $int->{name} }->{latency} = $_->{latency}; + last; + } + } + } + } + + $self->{global}->{detected}++; + } + + $self->{cache_name} = 'zscaler_ztb_' . $options{custom}->get_connection_info() . '_' . $self->{mode} . '_' . + sha256_hex( + (defined($self->{option_results}->{filter_counters}) ? $self->{option_results}->{filter_counters} : '') . '_' . + (defined($self->{option_results}->{include_site_name}) ? $self->{option_results}->{include_site_name} : '') . '_' . + (defined($self->{option_results}->{exclude_site_name}) ? $self->{option_results}->{exclude_site_name} : '') . '_' . + (defined($self->{option_results}->{include_cluster_name}) ? $self->{option_results}->{include_cluster_name} : '') . '_' . + (defined($self->{option_results}->{exclude_cluster_name}) ? $self->{option_results}->{exclude_cluster_name} : '') . '_' . + (defined($self->{option_results}->{include_gateway_name}) ? $self->{option_results}->{include_gateway_name} : '') . '_' . + (defined($self->{option_results}->{exclude_gateway_name}) ? $self->{option_results}->{exclude_gateway_name} : '') . '_' . + (defined($self->{option_results}->{include_interface_name}) ? $self->{option_results}->{include_interface_name} : '') . '_' . + (defined($self->{option_results}->{exclude_interface_name}) ? $self->{option_results}->{exclude_interface_name} : '') + ); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['siteName', 'clusterName', 'gatewayName', 'interfaceName', 'adminState', 'operationalState']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + foreach my $gw (@$gateways) { + next if is_excluded($gw->{site_name}, $self->{option_results}->{include_site_name}, $self->{option_results}->{exclude_site_name}); + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{gw_name}, $self->{option_results}->{include_gateway_name}, $self->{option_results}->{exclude_gateway_name}); + next if is_excluded($gw->{gw_id}, $self->{option_results}->{include_gateway_id}, $self->{option_results}->{exclude_gateway_id}); + + my $interfaces = $options{custom}->get_gateway_interfaces(gateway_id => $gw->{gw_id}); + foreach my $int (@$interfaces) { + next if is_excluded($int->{name}, $self->{option_results}->{include_interface_name}, $self->{option_results}->{exclude_interface_name}); + + $self->{output}->add_disco_entry( + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + interfaceName => $int->{name}, + adminState => $int->{admin_state}, + operationalState => $int->{operational_state} + ); + } + } +} + +1; + +__END__ + +=head1 MODE + +Check gateway interfaces. + +=over 8 + +=item B<--include-interface-name> + +Include interface names (regexp). + +=item B<--exclude-interface-name> + +Exclude interface names (regexp). + +=item B<--include-site-name> + +Include site names (regexp). + +=item B<--exclude-site-name> + +Exclude site names (regexp). + +=item B<--include-cluster-name> + +Include cluster names (regexp). + +=item B<--exclude-cluster-name> + +Exclude cluster names (regexp). + +=item B<--include-gateway-name> + +Include gateway names (regexp). + +=item B<--exclude-gateway-name> + +Exclude gateway names (regexp). + +=item B<--include-gateway-id> + +Include gateway IDs (regexp). + +=item B<--exclude-gateway-id> + +Exclude gateway IDs (regexp). + +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(gatewayName) %(interfaceName)') + +=item B<--traffic-unit> + +Units of thresholds for the traffic (default: 'percent_delta') ('percent_delta', 'bps', 'counter'). + +=item B<--speed> + +Set interface speed (in Mb). + +=item B<--unknown-gateway-interface-status> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{interfaceName}, %{adminState}, %{operationalState} + +=item B<--warning-gateway-interface-status> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{interfaceName}, %{adminState}, %{operationalState} + +=item B<--critical-gateway-interface-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{adminState} =~ /up/ and %{operationalState} !~ /up/'). +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{interfaceName}, %{adminState}, %{operationalState} + +=item B<--warning-gateways-detected> + +Threshold. + +=item B<--critical-gateways-detected> + +Threshold. + +=item B<--warning-interface-traffic-in> + +Threshold. + +=item B<--critical-interface-traffic-in> + +Threshold. + +=item B<--warning-interface-traffic-out> + +Threshold. + +=item B<--critical-interface-traffic-out> + +Threshold. + +=item B<--warning-interface-jitter> + +Threshold. + +=item B<--critical-interface-jitter> + +Threshold. + +=item B<--warning-interface-latency> + +Threshold. + +=item B<--critical-interface-latency> + +Threshold. + +=item B<--warning-interface-loss> + +Threshold. + +=item B<--critical-interface-loss> + +Threshold. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm index 3902ac1b85..fc22b62c71 100644 --- a/src/cloud/zscaler/ztb/restapi/plugin.pm +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -34,6 +34,7 @@ sub new { 'gateway-containers' => 'cloud::zscaler::ztb::restapi::mode::gatewaycontainers', 'gateway-cpu' => 'cloud::zscaler::ztb::restapi::mode::gatewaycpu', 'gateway-disk' => 'cloud::zscaler::ztb::restapi::mode::gatewaydisk', + 'gateway-interfaces' => 'cloud::zscaler::ztb::restapi::mode::gatewayinterfaces', 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory', 'gateway-status' => 'cloud::zscaler::ztb::restapi::mode::gatewaystatus' }; From 705768e9d04864b3db8eda4feccd595a38f4e9b4 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Fri, 10 Jul 2026 04:04:01 -0400 Subject: [PATCH 08/12] wip --- src/cloud/zscaler/ztb/restapi/mode/.pm | 202 ------------------ .../zscaler/ztb/restapi/mode/discovery.pm | 159 ++++++++++++++ src/cloud/zscaler/ztb/restapi/plugin.pm | 1 + 3 files changed, 160 insertions(+), 202 deletions(-) delete mode 100644 src/cloud/zscaler/ztb/restapi/mode/.pm create mode 100644 src/cloud/zscaler/ztb/restapi/mode/discovery.pm diff --git a/src/cloud/zscaler/ztb/restapi/mode/.pm b/src/cloud/zscaler/ztb/restapi/mode/.pm deleted file mode 100644 index 36c0c9b55c..0000000000 --- a/src/cloud/zscaler/ztb/restapi/mode/.pm +++ /dev/null @@ -1,202 +0,0 @@ -# -# Copyright 2026-Present 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::backup::veeam::vone::restapi::mode::proxies; - -use base qw(centreon::plugins::templates::counter); - -use strict; -use warnings; -use centreon::plugins::constants qw(:counters :values); -use centreon::plugins::misc qw/is_excluded/; -use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); - -my $map_repository_state_numeric = { - unknown => 0, - ok => 1, - inaccessible => 2, - disconnected => 3, - outOfDate => 4, - warning => 5 -}; - -sub custom_status_perfdata { - my ($self, %options) = @_; - - $self->{output}->perfdata_add( - nlabel => 'proxy.state.count', - instances => $self->{result_values}->{name}, - value => $map_repository_state_numeric->{ $self->{result_values}->{state} }, - min => 0 - ); -} - -sub prefix_global_output { - my ($self, %options) = @_; - - return 'Number of proxies '; -} - -sub prefix_proxy_output { - my ($self, %options) = @_; - - return sprintf( - "proxy '%s' [type: %s] ", - $options{instance_value}->{name}, - $options{instance_value}->{type} - ); -} - -sub set_counters { - my ($self, %options) = @_; - - $self->{maps_counters_type} = [ - { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, - { name => 'proxies', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_proxy_output', message_multiple => 'All proxies are ok' } - ]; - - $self->{maps_counters}->{global} = [ - { label => 'proxies-detected', display_ok => 0, nlabel => 'proxies.detected.count', - unknown_default => '@0', - set => { - key_values => [ { name => 'detected' } ], - output_template => 'detected: %s', - perfdatas => [ - { template => '%s', min => 0 } - ] - } - } - ]; - - $self->{maps_counters}->{proxies} = [ - { - label => 'proxy-status', - type => COUNTER_KIND_TEXT, - unknown_default => '%{state} =~ /unknown/', - warning_default => '%{state} =~ /warning|outofdate/', - critical_default => '%{state} =~ /inaccessible|disconnected/', - set => { - key_values => [ - { name => 'state' }, { name => 'name' }, { name => 'type' } - ], - output_template => 'state: %s', - closure_custom_perfdata => $self->can('custom_status_perfdata'), - closure_custom_threshold_check => \&catalog_status_threshold_ng - } - } - ]; -} - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); - bless $self, $class; - - $options{options}->add_options(arguments => { - 'filter-uid:s' => { name => 'filter_uid', default => '' }, - 'filter-name:s' => { name => 'filter_name', default => '' } - }); - - return $self; -} - -sub manage_selection { - my ($self, %options) = @_; - - my $repositories = $options{custom}->get_proxies(); - - $self->{global} = { detected => 0 }; - $self->{proxies} = {}; - foreach my $repo (@{$repositories->{items}}) { - my $enabled = $repo->{enabled} =~ /true|1/i ? 1 : 0; - next unless $enabled || $options{keep_disabled}; - next if is_excluded($repo->{proxyUidInVbr}, $self->{option_results}->{filter_uid}); - next if is_excluded($repo->{name}, $self->{option_results}->{filter_name}); - - $self->{proxies}->{ $repo->{name} } = { - uid => $repo->{proxyUidInVbr}, - name => $repo->{name}, - type => $repo->{type}, - state => lc $repo->{state}, - enabled => $enabled - }; - $self->{global}->{detected}++; - } -} - -sub disco_format { - my ($self, %options) = @_; - - $self->{output}->add_disco_format(elements => ['uid', 'name', 'type', 'state', 'enabled']); -} - -sub disco_show { - my ($self, %options) = @_; - - my $repos = $self->manage_selection(custom => $options{custom}, keep_disabled => 1); - foreach (sort { $a->{name} cmp $b->{name} } values %{ $self->{proxies} } ) { - $self->{output}->add_disco_entry(%$_); - } -} - -1; - -__END__ - -=head1 MODE - -Check proxies. - -=over 8 - -=item B<--filter-uid> - -Filter proxies by UID (can be a regexp). - -=item B<--filter-name> - -Filter proxies by name (can be a regexp). - -=item B<--unknown-proxy-status> - -Define the conditions to match for the status to be UNKNOWN (default: '%{state} =~ /unknown/'). -You can use the following variables: %{state}, %{name}, %{type} - -=item B<--warning-proxy-status> - -Define the conditions to match for the status to be WARNING (default: '%{state} =~ /warning|outofdate/'). -You can use the following variables: %{state}, %{name}, %{type} - -=item B<--critical-proxy-status> - -Define the conditions to match for the status to be CRITICAL (default: '%{state} =~ /inaccessible|disconnected/'). -You can use the following variables: %{state}, %{name}, %{type} - -=item B<--warning-proxies-detected> - -Thresholds. - -=item B<--critical-proxies-detected> - -Thresholds. - -=back - -=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/discovery.pm b/src/cloud/zscaler/ztb/restapi/mode/discovery.pm new file mode 100644 index 0000000000..634c4aee81 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/discovery.pm @@ -0,0 +1,159 @@ +# +# Copyright 2024 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package cloud::zscaler::ztb::restapi::mode::discovery; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; +use JSON::XS; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'resource-type:s' => { name => 'resource_type' }, + 'prettify' => { name => 'prettify' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); + + if (!defined($self->{option_results}->{resource_type}) || $self->{option_results}->{resource_type} eq '') { + $self->{option_results}->{resource_type} = 'gateway'; + } + if ($self->{option_results}->{resource_type} !~ /^gateway|site|cluster$/) { + $self->{output}->add_option_msg(short_msg => 'unknown resource type'); + $self->{output}->option_exit(); + } +} + +sub discovery_gateway { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + + my $disco_data = []; + foreach my $gw (@$gateways) { + my $node = { %$gw }; + $node->{uuid} = $gw->{gw_id}; + push @$disco_data, $node; + } + + return $disco_data; +} + +sub discovery_site { + my ($self, %options) = @_; + + my $sites = $options{custom}->get_sites(); + + my $disco_data = []; + foreach my $site (@$sites) { + my $node = { %$site }; + $node->{uuid} = $site->{id}; + + push @$disco_data, $node; + } + + return $disco_data; +} + +sub discovery_cluster { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + + my $cluster = {}; + my $disco_data = []; + foreach my $gw (@$gateways) { + next if (defined($cluster->{ $gw->{cluster_id} })); + $cluster->{ $gw->{cluster_id} } = 1; + + my $node = {}; + $node->{uuid} = $gw->{cluster_id}; + $node->{name} = $gw->{cluster_name}; + push @$disco_data, $node; + } + + return $disco_data; +} + +sub run { + my ($self, %options) = @_; + + my $disco_stats; + $disco_stats->{start_time} = time(); + + my $results = []; + if ($self->{option_results}->{resource_type} eq 'gateway') { + $results = $self->discovery_gateway(custom => $options{custom}); + } elsif ($self->{option_results}->{resource_type} eq 'site') { + $results = $self->discovery_site(custom => $options{custom}); + } else { + $results = $self->discovery_cluster(custom => $options{custom}); + } + + $disco_stats->{end_time} = time(); + $disco_stats->{duration} = $disco_stats->{end_time} - $disco_stats->{start_time}; + $disco_stats->{discovered_items} = scalar(@$results); + $disco_stats->{results} = $results; + + my $encoded_data; + eval { + if (defined($self->{option_results}->{prettify})) { + $encoded_data = JSON::XS->new->utf8->pretty->encode($disco_stats); + } else { + $encoded_data = JSON::XS->new->utf8->encode($disco_stats); + } + }; + if ($@) { + $encoded_data = '{"code":"encode_error","message":"Cannot encode discovered data into JSON format"}'; + } + + $self->{output}->output_add(short_msg => $encoded_data); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1); + $self->{output}->exit(); +} + +1; + +__END__ + +=head1 MODE + +Resources discovery. + +=over 8 + +=item B<--resource-type> + +Choose the type of resources to discover (can be: 'gateway', 'site', 'cluster'). + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm index fc22b62c71..0bb28ba7e8 100644 --- a/src/cloud/zscaler/ztb/restapi/plugin.pm +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -31,6 +31,7 @@ sub new { $self->{modes} = { 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', + 'discovery' => 'cloud::zscaler::ztb::restapi::mode::discovery', 'gateway-containers' => 'cloud::zscaler::ztb::restapi::mode::gatewaycontainers', 'gateway-cpu' => 'cloud::zscaler::ztb::restapi::mode::gatewaycpu', 'gateway-disk' => 'cloud::zscaler::ztb::restapi::mode::gatewaydisk', From ea14edd8a2d1f62b7ccaacc984c33297bc7c72ce Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Fri, 10 Jul 2026 05:21:27 -0400 Subject: [PATCH 09/12] wip --- .../ztb/restapi/mode/clusterappconnector.pm | 277 +++++++++++++++ .../zscaler/ztb/restapi/mode/clusteripsec.pm | 323 ++++++++++++++++++ src/cloud/zscaler/ztb/restapi/plugin.pm | 18 +- 3 files changed, 610 insertions(+), 8 deletions(-) create mode 100644 src/cloud/zscaler/ztb/restapi/mode/clusterappconnector.pm create mode 100644 src/cloud/zscaler/ztb/restapi/mode/clusteripsec.pm diff --git a/src/cloud/zscaler/ztb/restapi/mode/clusterappconnector.pm b/src/cloud/zscaler/ztb/restapi/mode/clusterappconnector.pm new file mode 100644 index 0000000000..ef3bf98dae --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/clusterappconnector.pm @@ -0,0 +1,277 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::mode::clusterappconnector; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of App Connectors '; +} + +sub prefix_appconnector_output { + my ($self, %options) = @_; + + return sprintf( + "App Connector '%s' [cluster: %s] ", + $options{instance_value}->{appConnectorName}, + $options{instance_value}->{clusterName} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { name => 'appconnectors', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_appconnector_output', message_multiple => 'All App Connectors are ok', skipped_code => { NO_VALUE() => 1 } } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'appconnectors-detected', display_ok => 0, nlabel => 'appconnectors.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{appconnectors} = [ + { + label => 'appconnector-status', + type => COUNTER_KIND_TEXT, + critical_default => '%{status} ne "connected"', + set => { + key_values => [ + { name => 'status' }, + { name => 'clusterName' }, { name => 'appConnectorName' } + ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'appconnector-running', nlabel => 'appconnector.running.count', set => { + key_values => [ + { name => 'running' }, { name => 'total' }, { name => 'clusterName' }, { name => 'appConnectorName' } + ], + output_template => 'running on gateways: %s', + closure_custom_perfdata => sub { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + instances => $instances, + value => $self->{result_values}->{running}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0, + max => $self->{result_values}->{total} + ); + } + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-appconnector-name:s' => { name => 'include_appconnector_name', default => '' }, + 'exclude-appconnector-name:s' => { name => 'exclude_appconnector_name', default => '' }, + 'include-cluster-id:s' => { name => 'include_cluster_id', default => '' }, + 'exclude-cluster-id:s' => { name => 'exclude_cluster_id', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(clusterName) %(appConnectorName)'; + } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { clusterName => 1, appConnectorName => 1 } + ); +} + +sub search_appconnector_container_started { + my ($self, %options) = @_; + + my ($running, $total) = (0, 0); + foreach my $gw (@{$options{gateways}}) { + next if ($options{cluster_id} ne $gw->{cluster_id}); + + my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); + if (defined($resource->{container_stats})) { + foreach my $cstat (@{$resource->{container_stats}}) { + if ($cstat->{cname} eq $options{name}) { + $running++; + last; + } + } + } + + $total++; + } + + return ($running, $total); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + my $cluster = {}; + + $self->{global} = { detected => 0 }; + $self->{appconnectors} = {}; + foreach my $gw (@$gateways) { + next if (defined($cluster->{ $gw->{cluster_id} })); + $cluster->{ $gw->{cluster_id} } = 1; + + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{cluster_id}, $self->{option_results}->{include_cluster_id}, $self->{option_results}->{exclude_cluster_id}); + + my $config = $options{custom}->get_appconnector_config(cluster_id => $gw->{cluster_id}); + foreach (@$config) { + next if is_excluded($_->{name}, $self->{option_results}->{include_appconnector_name}, $self->{option_results}->{exclude_appconnector_name}); + + my ($running, $total) = $self->search_appconnector_container_started( + custom => $options{custom}, + gateways => $gateways, + name => $_->{name}, + cluster_id => $gw->{cluster_id} + ); + + $self->{appconnectors}->{ $gw->{cluster_id} . $_->{name} } = { + clusterName => $gw->{cluster_name}, + appConnectorName => $_->{name}, + status => $_->{status}, + running => $running, + total => $total + }; + + $self->{global}->{detected}++; + } + } +} + +1; + +__END__ + +=head1 MODE + +Check clusters App Connector. + +=over 8 + +=item B<--include-cluster-name> + +Include cluster names (regexp). + +=item B<--exclude-cluster-name> + +Exclude cluster names (regexp). + +=item B<--include-appconnector-name> + +Include App Connector names (regexp). + +=item B<--exclude-appconnector-name> + +Exclude App Connector names (regexp). + +=item B<--include-cluster-id> + +Include cluster IDs (regexp). + +=item B<--exclude-cluster-id> + +Exclude cluster IDs (regexp). + +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(clusterName) %(appConnectorName)') + +=item B<--unknown-appconnector-status> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{clusterName}, %{appConnectorName}, %{status} + +=item B<--warning-appconnector-status> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{clusterName}, %{appConnectorName}, %{status} + +=item B<--critical-appconnector-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{status} ne "connected"'). +You can use the following variables: %{clusterName}, %{appConnectorName}, %{status} + +=item B<--warning-appconnectors-detected> + +Threshold. + +=item B<--critical-appconnectors-detected> + +Threshold. + +=item B<--warning-appconnector-running> + +Threshold. + +=item B<--critical-appconnector-running> + +Threshold. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/mode/clusteripsec.pm b/src/cloud/zscaler/ztb/restapi/mode/clusteripsec.pm new file mode 100644 index 0000000000..b7b4429702 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/clusteripsec.pm @@ -0,0 +1,323 @@ +# +# Copyright 2026-Present 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 cloud::zscaler::ztb::restapi::mode::clusteripsec; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::constants qw(:counters :values); +use centreon::plugins::misc qw/is_excluded/; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use POSIX; +use DateTime; + +my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 }; +my $unitdiv_long = { s => 'seconds', w => 'weeks', d => 'days', h => 'hours', m => 'minutes' }; + +sub custom_last_refresh_perfdata { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} }, + unit => $self->{instance_mode}->{option_results}->{unit}, + instances => $instances, + value => $self->{result_values}->{lastRefreshTime} >= 0 ? floor($self->{result_values}->{lastRefreshTime} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastRefreshTime}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); +} + +sub custom_last_refresh_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => $self->{result_values}->{lastRefreshTime} >= 0 ? floor($self->{result_values}->{lastRefreshTime} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastRefreshTime}, + threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' }, + { label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' } + ] + ); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of IPsec tunnels '; +} + +sub prefix_tunnel_output { + my ($self, %options) = @_; + + return sprintf( + "tunnel '%s' [cluster: %s] ", + $options{instance_value}->{tunnelName}, + $options{instance_value}->{clusterName} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, + { name => 'tunnels', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_tunnel_output', message_multiple => 'All IPsec tunnels are ok', skipped_code => { NO_VALUE() => 1 } } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'tunnels-ipsec-detected', display_ok => 0, nlabel => 'tunnels.ipsec.detected.count', + unknown_default => '@0', + set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{tunnels} = [ + { + label => 'ike-status', + type => COUNTER_KIND_TEXT, + critical_default => '%{ikeStatus} !~ /established/', + set => { + key_values => [ + { name => 'ikeStatus' }, + { name => 'clusterName' }, { name => 'tunnelName' } + ], + output_template => 'IKE status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { + label => 'sa-status', + type => COUNTER_KIND_TEXT, + critical_default => '%{saStatus} !~ /established/', + set => { + key_values => [ + { name => 'saStatus' }, + { name => 'clusterName' }, { name => 'tunnelName' } + ], + output_template => 'SA status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'last-refresh-time', nlabel => 'tunnel.ipsec.refresh.time.last', set => { + key_values => [ + { name => 'lastRefreshTime' }, { name => 'lastRefreshTimeHuman' }, + { name => 'clusterName' }, { name => 'tunnelName' } + ], + output_template => 'last refresh: %s', + output_use => 'lastRefreshTimeHuman', + closure_custom_perfdata => $self->can('custom_last_refresh_perfdata'), + closure_custom_threshold_check => $self->can('custom_last_refresh_threshold') + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'include-cluster-name:s' => { name => 'include_cluster_name', default => '' }, + 'exclude-cluster-name:s' => { name => 'exclude_cluster_name', default => '' }, + 'include-tunnel-name:s' => { name => 'include_tunnel_name', default => '' }, + 'exclude-tunnel-name:s' => { name => 'exclude_tunnel_name', default => '' }, + 'include-cluster-id:s' => { name => 'include_cluster_id', default => '' }, + 'exclude-cluster-id:s' => { name => 'exclude_cluster_id', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' }, + 'unit:s' => { name => 'unit', default => 's' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(clusterName) %(tunnelName)'; + } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { clusterName => 1, tunnelName => 1 } + ); + + if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) { + $self->{option_results}->{unit} = 's'; + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $gateways = $options{custom}->get_gateways(); + my $cluster = {}; + + $self->{global} = { detected => 0 }; + $self->{tunnels} = {}; + foreach my $gw (@$gateways) { + next if (defined($cluster->{ $gw->{cluster_id} })); + $cluster->{ $gw->{cluster_id} } = 1; + + next if is_excluded($gw->{cluster_name}, $self->{option_results}->{include_cluster_name}, $self->{option_results}->{exclude_cluster_name}); + next if is_excluded($gw->{cluster_id}, $self->{option_results}->{include_cluster_id}, $self->{option_results}->{exclude_cluster_id}); + + my $ctime = time(); + my $tunnels = $options{custom}->get_cluster_ipsec_status(cluster_id => $gw->{cluster_id}); + foreach my $tun (@$tunnels) { + next if ($tun->{status} !~ /^(.*?):/); + my $name = $1; + + next if is_excluded($name, $self->{option_results}->{include_tunnel_name}, $self->{option_results}->{exclude_tunnel_name}); + + my $ikeStatus = 'unknown'; + $ikeStatus = $1 if ($tun->{status} =~ /^.*?:.*?,\s*(.*?)\s*,\s*IKEv2/); + + my ($lastRefreshTime, $lastRefreshTimeHuman); + if ($tun->{time_stamp} =~ /(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)/) { + my $dt = DateTime->new(year => $1, month => $2, day => $3, hour => $4, minute => $5, second => $6, time_zone => 'UTC'); + $lastRefreshTime = $ctime - $dt->epoch(); + $lastRefreshTimeHuman = centreon::plugins::misc::change_seconds(value => $lastRefreshTime); + } + + $self->{tunnels}->{ $gw->{cluster_id} . $name } = { + clusterName => $gw->{cluster_name}, + tunnelName => $name, + saStatus => lc($tun->{child_sa_status}), + ikeStatus => lc($ikeStatus), + lastRefreshTime => $lastRefreshTime, + lastRefreshTimeHuman => $lastRefreshTimeHuman + }; + + $self->{global}->{detected}++; + } + } +} + +1; + +__END__ + +=head1 MODE + +Check clusters IPsec tunnel. + +=over 8 + +=item B<--include-cluster-name> + +Include cluster names (regexp). + +=item B<--exclude-cluster-name> + +Exclude cluster names (regexp). + +=item B<--include-tunnel-name> + +Include tunnel names (regexp). + +=item B<--exclude-tunnel-name> + +Exclude tunnel names (regexp). + +=item B<--include-cluster-id> + +Include cluster IDs (regexp). + +=item B<--exclude-cluster-id> + +Exclude cluster IDs (regexp). + +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(clusterName) %(tunnelName)') + +=item B<--unit> + +Select the time unit for refresh thresholds. May be 's' for seconds, 'm' for minutes, 'h' for hours, 'd' for days, 'w' for weeks (default: 's'). + +=item B<--unknown-sa-status> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{clusterName}, %{tunnelName}, %{saStatus} + +=item B<--warning-sa-status> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{clusterName}, %{tunnelName}, %{saStatus} + +=item B<--critical-sa-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{saStatus} !~ /established/'). +You can use the following variables: %{clusterName}, %{tunnelName}, %{saStatus} + +=item B<--unknown-ike-status> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{clusterName}, %{tunnelName}, %{ikeStatus} + +=item B<--warning-ike-status> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{clusterName}, %{tunnelName}, %{ikeStatus} + +=item B<--critical-ike-status> + +Define the conditions to match for the status to be CRITICAL (default: '%{ikeStatus} !~ /established/'). +You can use the following variables: %{clusterName}, %{tunnelName}, %{ikeStatus} + +=item B<--warning-tunnels-ipsec-detected> + +Threshold. + +=item B<--critical-tunnels-ipsec-detected> + +Threshold. + +=item B<--warning-last-refresh-time> + +Threshold. + +=item B<--critical-last-refresh-time> + +Threshold. + +=back + +=cut diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm index 0bb28ba7e8..b15c99565e 100644 --- a/src/cloud/zscaler/ztb/restapi/plugin.pm +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -30,14 +30,16 @@ sub new { bless $self, $class; $self->{modes} = { - 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', - 'discovery' => 'cloud::zscaler::ztb::restapi::mode::discovery', - 'gateway-containers' => 'cloud::zscaler::ztb::restapi::mode::gatewaycontainers', - 'gateway-cpu' => 'cloud::zscaler::ztb::restapi::mode::gatewaycpu', - 'gateway-disk' => 'cloud::zscaler::ztb::restapi::mode::gatewaydisk', - 'gateway-interfaces' => 'cloud::zscaler::ztb::restapi::mode::gatewayinterfaces', - 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory', - 'gateway-status' => 'cloud::zscaler::ztb::restapi::mode::gatewaystatus' + 'cache' => 'cloud::zscaler::ztb::restapi::mode::cache', + 'cluster-appconnector' => 'cloud::zscaler::ztb::restapi::mode::clusterappconnector', + 'cluster-ipsec' => 'cloud::zscaler::ztb::restapi::mode::clusteripsec', + 'discovery' => 'cloud::zscaler::ztb::restapi::mode::discovery', + 'gateway-containers' => 'cloud::zscaler::ztb::restapi::mode::gatewaycontainers', + 'gateway-cpu' => 'cloud::zscaler::ztb::restapi::mode::gatewaycpu', + 'gateway-disk' => 'cloud::zscaler::ztb::restapi::mode::gatewaydisk', + 'gateway-interfaces' => 'cloud::zscaler::ztb::restapi::mode::gatewayinterfaces', + 'gateway-memory' => 'cloud::zscaler::ztb::restapi::mode::gatewaymemory', + 'gateway-status' => 'cloud::zscaler::ztb::restapi::mode::gatewaystatus' }; $self->{custom_modes}->{api} = 'cloud::zscaler::ztb::restapi::custom::api'; From d4d9899ccf17e42649679660acae3080323b1426 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Mon, 13 Jul 2026 04:45:01 -0400 Subject: [PATCH 10/12] wip --- .../centreon-plugin-Cloud-Zscaler-Ztb-Restapi/deb.json | 5 +++++ .../centreon-plugin-Cloud-Zscaler-Ztb-Restapi/pkg.json | 9 +++++++++ .../centreon-plugin-Cloud-Zscaler-Ztb-Restapi/rpm.json | 5 +++++ 3 files changed, 19 insertions(+) create mode 100644 packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/deb.json create mode 100644 packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/pkg.json create mode 100644 packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/rpm.json diff --git a/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/deb.json b/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/deb.json new file mode 100644 index 0000000000..8133a85e5e --- /dev/null +++ b/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/deb.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "libdatetime-perl" + ] +} diff --git a/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/pkg.json b/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/pkg.json new file mode 100644 index 0000000000..41ff1501ff --- /dev/null +++ b/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/pkg.json @@ -0,0 +1,9 @@ +{ + "pkg_name": "centreon-plugin-Cloud-Zscaler-Ztb-Restapi", + "pkg_summary": "Centreon Plugin", + "plugin_name": "centreon_zscaler_ztb_restapi.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "cloud/zscaler/ztb/restapi/" + ] +} diff --git a/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/rpm.json b/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/rpm.json new file mode 100644 index 0000000000..e9dff7552f --- /dev/null +++ b/packaging/centreon-plugin-Cloud-Zscaler-Ztb-Restapi/rpm.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "perl(DateTime)" + ] +} From 2b2d278429ff7b31e7f374c62ca31dfcdea4923c Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Mon, 20 Jul 2026 07:53:18 -0400 Subject: [PATCH 11/12] wip --- src/cloud/zscaler/ztb/restapi/custom/api.pm | 1 + .../zscaler/ztb/restapi/mode/gatewaystatus.pm | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/src/cloud/zscaler/ztb/restapi/custom/api.pm b/src/cloud/zscaler/ztb/restapi/custom/api.pm index ade80e34ea..88a0807b1d 100644 --- a/src/cloud/zscaler/ztb/restapi/custom/api.pm +++ b/src/cloud/zscaler/ztb/restapi/custom/api.pm @@ -339,6 +339,7 @@ sub get_gateways { $gw->{gw_vrrp_state} = $gateway->{vrrp_state}; $gw->{gw_desired_state} = $gateway->{desired_state}; $gw->{gw_operational_state} = $gateway->{operational_state}; + $gw->{gw_last_poll_update} = $gateway->{last_poll_updated}; push @$response, $gw; } } diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm index 27c8b612d8..17f2cdf697 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm @@ -27,6 +27,43 @@ use warnings; use centreon::plugins::constants qw(:counters :values); use centreon::plugins::misc qw/is_excluded/; use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use POSIX; +use DateTime; + +my $unitdiv = { s => 1, w => 604800, d => 86400, h => 3600, m => 60 }; +my $unitdiv_long = { s => 'seconds', w => 'weeks', d => 'days', h => 'hours', m => 'minutes' }; + +sub custom_last_update_perfdata { + my ($self, %options) = @_; + + my $instances = []; + foreach (@{$self->{instance_mode}->{custom_perfdata_instances}}) { + push @$instances, $self->{result_values}->{$_}; + } + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} }, + unit => $self->{instance_mode}->{option_results}->{unit}, + instances => $instances, + value => $self->{result_values}->{lastUpdateTime} >= 0 ? floor($self->{result_values}->{lastUpdateTime} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastUpdateTime}, + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}), + min => 0 + ); +} + +sub custom_last_update_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => $self->{result_values}->{lastUpdateTime} >= 0 ? floor($self->{result_values}->{lastUpdateTime} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastUpdateTime}, + threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' }, + { label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' } + ] + ); +} sub custom_status_output { my ($self, %options) = @_; @@ -108,6 +145,17 @@ sub set_counters { closure_custom_perfdata => sub { return 0; }, closure_custom_threshold_check => \&catalog_status_threshold_ng } + }, + { label => 'last-update-time', nlabel => 'gateway.update.time.last', set => { + key_values => [ + { name => 'lastUpdateTime' }, { name => 'lastUpdateTimeHuman' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + output_template => 'last update: %s', + output_use => 'lastUpdateTimeHuman', + closure_custom_perfdata => $self->can('custom_last_update_perfdata'), + closure_custom_threshold_check => $self->can('custom_last_update_threshold') + } } ]; @@ -160,14 +208,36 @@ sub new { 'exclude-gateway-name:s' => { name => 'exclude_gateway_name', default => '' }, 'include-gateway-id:s' => { name => 'include_gateway_id', default => '' }, 'exclude-gateway-id:s' => { name => 'exclude_gateway_id', default => '' }, + 'custom-perfdata-instances:s' => { name => 'custom_perfdata_instances' }, + 'unit:s' => { name => 'unit', default => 's' } }); return $self; } +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{custom_perfdata_instances}) || $self->{option_results}->{custom_perfdata_instances} eq '') { + $self->{option_results}->{custom_perfdata_instances} = '%(gatewayName)'; + } + + $self->{custom_perfdata_instances} = $self->custom_perfdata_instances( + option_name => '--custom-perfdata-instances', + instances => $self->{option_results}->{custom_perfdata_instances}, + labels => { siteName => 1, clusterName => 1, gatewayName => 1 } + ); + + if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) { + $self->{option_results}->{unit} = 's'; + } +} + sub manage_selection { my ($self, %options) = @_; + my $ctime = time(); my $gateways = $options{custom}->get_gateways(); $self->{global} = { detected => 0 }; @@ -203,6 +273,14 @@ sub manage_selection { } }; + if ($gw->{gw_last_poll_update} =~ /(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)/) { + my $dt = DateTime->new(year => $1, month => $2, day => $3, hour => $4, minute => $5, second => $6, time_zone => 'UTC'); + $self->{gateways}->{ $gw->{gw_id} }->{status}->{lastUpdateTime} = $ctime - $dt->epoch(); + $self->{gateways}->{ $gw->{gw_id} }->{status}->{lastUpdateTimeHuman} = centreon::plugins::misc::change_seconds( + value => $self->{gateways}->{ $gw->{gw_id} }->{status}->{lastUpdateTime} + ); + } + $self->{global}->{detected}++; } } @@ -249,6 +327,14 @@ Include gateway IDs (regexp). Exclude gateway IDs (regexp). +=item B<--custom-perfdata-instances> + +Define perfdatas instance (default: '%(gatewayName)') + +=item B<--unit> + +Select the time unit for update thresholds. May be 's' for seconds, 'm' for minutes, 'h' for hours, 'd' for days, 'w' for weeks (default: 's'). + =item B<--warning-gateways-detected> Threshold. @@ -302,6 +388,14 @@ You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName} Define the conditions to match for the status to be CRITICAL (default: '%{vrrpState} =~ /fault/i'). You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{vrrpState} +=item B<--warning-last-update-time> + +Threshold. + +=item B<--critical-last-update-time> + +Threshold. + =back =cut From 274d4c3df6536c90d99e84eafc7730a78527ece2 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Thu, 23 Jul 2026 05:24:51 -0400 Subject: [PATCH 12/12] wip --- src/cloud/zscaler/ztb/restapi/custom/api.pm | 1 + .../ztb/restapi/mode/gatewaycontainers.pm | 6 +-- .../zscaler/ztb/restapi/mode/gatewaycpu.pm | 31 ++++++++--- .../zscaler/ztb/restapi/mode/gatewaydisk.pm | 39 ++++++++++---- .../ztb/restapi/mode/gatewayinterfaces.pm | 6 +-- .../zscaler/ztb/restapi/mode/gatewaymemory.pm | 6 +-- .../zscaler/ztb/restapi/mode/gatewaystatus.pm | 51 ++++++++++++++----- 7 files changed, 100 insertions(+), 40 deletions(-) diff --git a/src/cloud/zscaler/ztb/restapi/custom/api.pm b/src/cloud/zscaler/ztb/restapi/custom/api.pm index 88a0807b1d..a3db9c399b 100644 --- a/src/cloud/zscaler/ztb/restapi/custom/api.pm +++ b/src/cloud/zscaler/ztb/restapi/custom/api.pm @@ -340,6 +340,7 @@ sub get_gateways { $gw->{gw_desired_state} = $gateway->{desired_state}; $gw->{gw_operational_state} = $gateway->{operational_state}; $gw->{gw_last_poll_update} = $gateway->{last_poll_updated}; + $gw->{gw_running_version} = $gateway->{running_version}; push @$response, $gw; } } diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm index 56fc827521..aec870e3e7 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm @@ -82,10 +82,8 @@ sub prefix_gateway_output { my ($self, %options) = @_; return sprintf( - "gateway '%s' [site: %s, cluster: %s] ", - $options{instance_value}->{gatewayName}, - $options{instance_value}->{siteName}, - $options{instance_value}->{clusterName} + "gateway '%s' ", + $options{instance_value}->{gatewayName} ); } diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm index 9b2ff77e5c..dd6d1f27a7 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm @@ -33,23 +33,37 @@ sub prefix_global_output { return 'Number of gateways '; } -sub prefix_gateway_output { +sub gateway_long_output { my ($self, %options) = @_; return sprintf( - "gateway '%s' [site: %s, cluster: %s] ", + "checking gateway '%s' [site: %s, cluster: %s]", $options{instance_value}->{gatewayName}, $options{instance_value}->{siteName}, $options{instance_value}->{clusterName} ); } +sub prefix_gateway_output { + my ($self, %options) = @_; + + return sprintf( + "gateway '%s' ", + $options{instance_value}->{gatewayName} + ); +} + sub set_counters { my ($self, %options) = @_; $self->{maps_counters_type} = [ { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, - { name => 'gateways', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_gateway_output', message_multiple => 'All gateways are ok', skipped_code => { NO_VALUE() => 1 } } + { + name => 'gateways', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_gateway_output', cb_long_output => 'gateway_long_output', indent_long_output => ' ', message_multiple => 'All gateways are ok', + group => [ + { name => 'cpu', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } } + ] + } ]; $self->{maps_counters}->{global} = [ @@ -65,7 +79,7 @@ sub set_counters { } ]; - $self->{maps_counters}->{gateways} = [ + $self->{maps_counters}->{cpu} = [ { label => 'cpu-utilization', nlabel => 'gateway.cpu.utilization.percentage', set => { key_values => [ { name => 'prct_used' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } @@ -146,12 +160,17 @@ sub manage_selection { $self->{gateways}->{ $gw->{gw_id} } = { gatewayName => $gw->{gw_name}, clusterName => $gw->{cluster_name}, - siteName => $gw->{site_name} + siteName => $gw->{site_name}, + cpu => { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name} + } }; my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); if (defined($resource->{hourly_stat_last})) { - $self->{gateways}->{ $gw->{gw_id} }->{prct_used} = $resource->{hourly_stat_last}->{cpu_used_avg}; + $self->{gateways}->{ $gw->{gw_id} }->{cpu}->{prct_used} = $resource->{hourly_stat_last}->{cpu_used_avg}; } $self->{global}->{detected}++; diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm index 61909f0cc4..c536e97237 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm @@ -67,23 +67,37 @@ sub prefix_global_output { return 'Number of gateways '; } -sub prefix_gateway_output { +sub gateway_long_output { my ($self, %options) = @_; return sprintf( - "gateway '%s' [site: %s, cluster: %s] ", + "checking gateway '%s' [site: %s, cluster: %s]", $options{instance_value}->{gatewayName}, $options{instance_value}->{siteName}, $options{instance_value}->{clusterName} ); } +sub prefix_gateway_output { + my ($self, %options) = @_; + + return sprintf( + "gateway '%s' ", + $options{instance_value}->{gatewayName} + ); +} + sub set_counters { my ($self, %options) = @_; $self->{maps_counters_type} = [ { name => 'global', type => COUNTER_TYPE_GLOBAL, cb_prefix_output => 'prefix_global_output' }, - { name => 'gateways', type => COUNTER_TYPE_INSTANCE, cb_prefix_output => 'prefix_gateway_output', message_multiple => 'All gateways are ok', skipped_code => { NO_VALUE() => 1 } } + { + name => 'gateways', type => COUNTER_TYPE_MULTIPLE, cb_prefix_output => 'prefix_gateway_output', cb_long_output => 'gateway_long_output', indent_long_output => ' ', message_multiple => 'All gateways are ok', + group => [ + { name => 'disk', type => COUNTER_MULTIPLE_INSTANCE, skipped_code => { NO_VALUE() => 1 } } + ] + } ]; $self->{maps_counters}->{global} = [ @@ -99,7 +113,7 @@ sub set_counters { } ]; - $self->{maps_counters}->{gateways} = [ + $self->{maps_counters}->{disk} = [ { label => 'disk-usage', nlabel => 'gateway.disk.usage.bytes', set => { key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, @@ -199,7 +213,12 @@ sub manage_selection { $self->{gateways}->{ $gw->{gw_id} } = { gatewayName => $gw->{gw_name}, clusterName => $gw->{cluster_name}, - siteName => $gw->{site_name} + siteName => $gw->{site_name}, + disk => { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name} + } }; my $resource = $options{custom}->get_gateway_resource(gateway_id => $gw->{gw_id}); @@ -207,11 +226,11 @@ sub manage_selection { my $total = $resource->{system_stats}->{disk}->{total} * 1024 * 1024 * 1024; my $free = $resource->{system_stats}->{disk}->{free} * 1024 * 1024 * 1024; my $used = $resource->{system_stats}->{disk}->{used} * 1024 * 1024 * 1024; - $self->{gateways}->{ $gw->{gw_id} }->{total} = $total; - $self->{gateways}->{ $gw->{gw_id} }->{used} = $used; - $self->{gateways}->{ $gw->{gw_id} }->{free} = $free; - $self->{gateways}->{ $gw->{gw_id} }->{prct_used} = 100 - ($free * 100 / $total); - $self->{gateways}->{ $gw->{gw_id} }->{prct_free} = $free * 100 / $total; + $self->{gateways}->{ $gw->{gw_id} }->{disk}->{total} = $total; + $self->{gateways}->{ $gw->{gw_id} }->{disk}->{used} = $used; + $self->{gateways}->{ $gw->{gw_id} }->{disk}->{free} = $free; + $self->{gateways}->{ $gw->{gw_id} }->{disk}->{prct_used} = 100 - ($free * 100 / $total); + $self->{gateways}->{ $gw->{gw_id} }->{disk}->{prct_free} = $free * 100 / $total; } $self->{global}->{detected}++; diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm index 8c09f46b86..6aa013a4d9 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm @@ -149,10 +149,8 @@ sub prefix_gateway_output { my ($self, %options) = @_; return sprintf( - "gateway '%s' [site: %s, cluster: %s] ", - $options{instance_value}->{gatewayName}, - $options{instance_value}->{siteName}, - $options{instance_value}->{clusterName} + "gateway '%s' ", + $options{instance_value}->{gatewayName} ); } diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm index 9cdc442eee..69493faab7 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm @@ -82,10 +82,8 @@ sub prefix_gateway_output { my ($self, %options) = @_; return sprintf( - "gateway '%s' [site: %s, cluster: %s] ", - $options{instance_value}->{gatewayName}, - $options{instance_value}->{siteName}, - $options{instance_value}->{clusterName} + "gateway '%s' ", + $options{instance_value}->{gatewayName} ); } diff --git a/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm index 17f2cdf697..f8bad4741b 100644 --- a/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm @@ -96,10 +96,8 @@ sub prefix_gateway_output { my ($self, %options) = @_; return sprintf( - "gateway '%s' [site: %s, cluster: %s] ", - $options{instance_value}->{gatewayName}, - $options{instance_value}->{siteName}, - $options{instance_value}->{clusterName} + "gateway '%s' ", + $options{instance_value}->{gatewayName} ); } @@ -146,6 +144,19 @@ sub set_counters { closure_custom_threshold_check => \&catalog_status_threshold_ng } }, + { + label => 'running-version', + type => COUNTER_KIND_TEXT, + set => { + key_values => [ + { name => 'runningVersion' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + output_template => 'running version: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, { label => 'last-update-time', nlabel => 'gateway.update.time.last', set => { key_values => [ { name => 'lastUpdateTime' }, { name => 'lastUpdateTimeHuman' }, @@ -251,25 +262,26 @@ sub manage_selection { $self->{gateways}->{ $gw->{gw_id} } = { gatewayName => $gw->{gw_name}, clusterName => $gw->{cluster_name}, - siteName => $gw->{site_name}, + siteName => $gw->{site_name}, status => { - gatewayName => $gw->{gw_name}, - clusterName => $gw->{cluster_name}, - siteName => $gw->{site_name}, + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, operationalState => $gw->{gw_operational_state}, - desiredState => $gw->{gw_desired_state} + desiredState => $gw->{gw_desired_state}, + runningVersion => $gw->{gw_running_version} }, health => { gatewayName => $gw->{gw_name}, clusterName => $gw->{cluster_name}, - siteName => $gw->{site_name}, + siteName => $gw->{site_name}, healthColor => $gw->{gw_health_color} }, vrrp => { gatewayName => $gw->{gw_name}, clusterName => $gw->{cluster_name}, - siteName => $gw->{site_name}, - vrrpState => $gw->{gw_vrrp_state} + siteName => $gw->{site_name}, + vrrpState => $gw->{gw_vrrp_state} } }; @@ -358,6 +370,21 @@ You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName} Define the conditions to match for the status to be CRITICAL (default: '%{desiredState} ne %{operationalState}'). You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{desiredState}, %{operationalState} +=item B<--unknown-running-version> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{runningVersion} + +=item B<--warning-running-version> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{runningVersion} + +=item B<--critical-running-version> + +Define the conditions to match for the status to be CRITICAL. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{runningVersion} + =item B<--unknown-gateway-health> Define the conditions to match for the status to be UNKNOWN.