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)" + ] +} 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..a3db9c399b --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/custom/api.pm @@ -0,0 +1,487 @@ +# +# 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}, + site_name => $site->{location}, + 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}; + $gw->{gw_last_poll_update} = $gateway->{last_poll_updated}; + $gw->{gw_running_version} = $gateway->{running_version}; + 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') { + $response = { + 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}, + wanmon_avg_values_last => $data->{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/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/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/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/mode/gatewaycontainers.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm new file mode 100644 index 0000000000..aec870e3e7 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaycontainers.pm @@ -0,0 +1,371 @@ +# +# 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' ", + $options{instance_value}->{gatewayName} + ); +} + +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/mode/gatewaycpu.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm new file mode 100644 index 0000000000..dd6d1f27a7 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaycpu.pm @@ -0,0 +1,244 @@ +# +# 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 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' ", + $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_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} = [ + { 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}->{cpu} = [ + { 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}, + 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} }->{cpu}->{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..c536e97237 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaydisk.pm @@ -0,0 +1,320 @@ +# +# 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 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' ", + $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_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} = [ + { 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}->{disk} = [ + { 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}, + 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}); + 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} }->{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}++; + } +} + +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/gatewayinterfaces.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm new file mode 100644 index 0000000000..6aa013a4d9 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewayinterfaces.pm @@ -0,0 +1,596 @@ +# +# 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' ", + $options{instance_value}->{gatewayName} + ); +} + +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/mode/gatewaymemory.pm b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm new file mode 100644 index 0000000000..69493faab7 --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaymemory.pm @@ -0,0 +1,367 @@ +# +# 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::gatewaymemory; + +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' ", + $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_MULTIPLE, cb_prefix_output => 'prefix_gateway_output', cb_long_output => 'gateway_long_output', indent_long_output => ' ', message_multiple => 'All gateways are ok', + group => [ + { 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 => '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}->{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 => '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 => '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}->{swap} = [ + { label => 'swap-usage', nlabel => 'gateway.swap.usage.bytes', + set => { + key_values => [ + { name => 'used' }, { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + 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 + ); + } + } + } + ]; +} + +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}->{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 + }; + } + + $self->{global}->{detected}++; + } +} + +1; + +__END__ + +=head1 MODE + +Check gateway memory. + +=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-memory-usage> + +Threshold in bytes. + +=item B<--critical-memory-usage> + +Threshold in bytes. + +=item B<--warning-memory-usage-free> + +Threshold in bytes. + +=item B<--critical-memory-usage-free> + +Threshold in bytes. + +=item B<--warning-memory-usage-prct> + +Threshold in percentage. + +=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 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..f8bad4741b --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/mode/gatewaystatus.pm @@ -0,0 +1,428 @@ +# +# 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::gatewaystatus; + +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_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) = @_; + + return sprintf( + "operational state: %s [desired state: %s]", + $self->{result_values}->{operationalState}, + $self->{result_values}->{desiredState} + ); +} + +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' ", + $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_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, 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 => '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}->{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 => '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' }, + { 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') + } + } + ]; + + $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}->{vrrp} = [ + { + label => 'gateway-vrrp-status', + type => COUNTER_KIND_TEXT, + critical_default => '%{vrrpState} =~ /fault/i', + set => { + key_values => [ + { name => 'vrrpState' }, + { name => 'gatewayName' }, { name => 'clusterName' }, { name => 'siteName' } + ], + output_template => 'VRRP state: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + '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' }, + '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 }; + $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 => { + gatewayName => $gw->{gw_name}, + clusterName => $gw->{cluster_name}, + siteName => $gw->{site_name}, + operationalState => $gw->{gw_operational_state}, + desiredState => $gw->{gw_desired_state}, + runningVersion => $gw->{gw_running_version} + }, + 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} + } + }; + + 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}++; + } +} + +1; + +__END__ + +=head1 MODE + +Check gateway status. + +=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<--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. + +=item B<--critical-gateways-detected> + +Threshold. + +=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-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. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{healthColor} + +=item B<--warning-gateway-health> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{healthColor} + +=item B<--critical-gateway-health> + +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<--unknown-gateway-vrrp-status> + +Define the conditions to match for the status to be UNKNOWN. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{vrrpState} + +=item B<--warning-gateway-vrrp-status> + +Define the conditions to match for the status to be WARNING. +You can use the following variables: %{siteName}, %{clusterName}, %{gatewayName}, %{vrrpState} + +=item B<--critical-gateway-vrrp-status> + +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 diff --git a/src/cloud/zscaler/ztb/restapi/plugin.pm b/src/cloud/zscaler/ztb/restapi/plugin.pm new file mode 100644 index 0000000000..b15c99565e --- /dev/null +++ b/src/cloud/zscaler/ztb/restapi/plugin.pm @@ -0,0 +1,57 @@ +# +# 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', + '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'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Zscaler Zero Trust Branch using API. + +=cut