diff --git a/centreon-plugins/apps/vtom/restapi/custom/api.pm b/centreon-plugins/apps/vtom/restapi/custom/api.pm index 93e8f82f2..4e7f36536 100644 --- a/centreon-plugins/apps/vtom/restapi/custom/api.pm +++ b/centreon-plugins/apps/vtom/restapi/custom/api.pm @@ -23,7 +23,9 @@ package apps::vtom::restapi::custom::api; use strict; use warnings; use centreon::plugins::http; +use centreon::plugins::statefile; use JSON::XS; +use Digest::MD5 qw(md5_hex); sub new { my ($class, %options) = @_; @@ -38,21 +40,28 @@ sub new { $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); $options{output}->option_exit(); } - + if (!defined($options{noptions})) { - $options{options}->add_options(arguments => { - 'hostname:s@' => { name => 'hostname' }, - 'port:s@' => { name => 'port' }, - 'proto:s@' => { name => 'proto' }, - 'username:s@' => { name => 'username' }, - 'password:s@' => { name => 'password' }, - 'timeout:s@' => { name => 'timeout' } + $options{options}->add_options(arguments => { + 'api-username:s' => { name => 'api_username' }, + 'api-password:s' => { name => 'api_password' }, + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port' }, + 'proto:s' => { name => 'proto' }, + 'timeout:s' => { name => 'timeout' }, + 'unknown-http-status:s' => { name => 'unknown_http_status' }, + 'warning-http-status:s' => { name => 'warning_http_status' }, + 'critical-http-status:s' => { name => 'critical_http_status' }, + 'token:s' => { name => 'token' }, + 'cache-use' => { name => 'cache_use' } }); } $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); $self->{output} = $options{output}; $self->{http} = centreon::plugins::http->new(%options); + $self->{cache_token} = centreon::plugins::statefile->new(%options); + $self->{cache} = centreon::plugins::statefile->new(%options); return $self; } @@ -68,108 +77,237 @@ sub set_defaults {} sub check_options { my ($self, %options) = @_; - $self->{hostname} = (defined($self->{option_results}->{hostname})) ? shift(@{$self->{option_results}->{hostname}}) : undef; - $self->{port} = (defined($self->{option_results}->{port})) ? shift(@{$self->{option_results}->{port}}) : 30080; - $self->{proto} = (defined($self->{option_results}->{proto})) ? shift(@{$self->{option_results}->{proto}}) : 'http'; - $self->{username} = (defined($self->{option_results}->{username})) ? shift(@{$self->{option_results}->{username}}) : ''; - $self->{password} = (defined($self->{option_results}->{password})) ? shift(@{$self->{option_results}->{password}}) : ''; - $self->{timeout} = (defined($self->{option_results}->{timeout})) ? shift(@{$self->{option_results}->{timeout}}) : 10; - - if (!defined($self->{hostname})) { - $self->{output}->add_option_msg(short_msg => "Need to specify hostname option."); + $self->{option_results}->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 30002; + $self->{option_results}->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; + $self->{option_results}->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 30; + $self->{api_username} = (defined($self->{option_results}->{api_username})) ? $self->{option_results}->{api_username} : ''; + $self->{api_password} = (defined($self->{option_results}->{api_password})) ? $self->{option_results}->{api_password} : ''; + $self->{unknown_http_status} = (defined($self->{option_results}->{unknown_http_status})) ? $self->{option_results}->{unknown_http_status} : '%{http_code} < 200 or %{http_code} >= 300'; + $self->{warning_http_status} = (defined($self->{option_results}->{warning_http_status})) ? $self->{option_results}->{warning_http_status} : ''; + $self->{critical_http_status} = (defined($self->{option_results}->{critical_http_status})) ? $self->{option_results}->{critical_http_status} : ''; + $self->{token} = $self->{option_results}->{token}; + + if (!defined($self->{option_results}->{hostname}) || $self->{option_results}->{hostname} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify --hostname option.'); $self->{output}->option_exit(); } - if (!defined($self->{hostname}) || - scalar(@{$self->{option_results}->{hostname}}) == 0) { - return 0; + $self->{cache_token}->check_options(option_results => $self->{option_results}); + $self->{cache}->check_options(option_results => $self->{option_results}); + + return 0 if (defined($self->{token}) && $self->{token} ne ''); + + if ($self->{api_username} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify --api-username option.'); + $self->{output}->option_exit(); + } + if ($self->{api_password} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify --api-password option.'); + $self->{output}->option_exit(); } - return 1; -} -sub build_options_for_httplib { - my ($self, %options) = @_; - - $self->{option_results}->{hostname} = $self->{hostname}; - $self->{option_results}->{timeout} = $self->{timeout}; - $self->{option_results}->{port} = $self->{port}; - $self->{option_results}->{proto} = $self->{proto}; - $self->{option_results}->{credentials} = 1; - $self->{option_results}->{basic} = 1; - $self->{option_results}->{username} = $self->{username}; - $self->{option_results}->{password} = $self->{password}; + return 0; } sub settings { my ($self, %options) = @_; - $self->build_options_for_httplib(); + return if (defined($self->{settings_done})); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->add_header(key => 'Content-Type', value => 'application/json'); $self->{http}->set_options(%{$self->{option_results}}); + $self->{settings_done} = 1; } -sub cache_environment { +sub get_connection_info { my ($self, %options) = @_; - - my $has_cache_file = $options{statefile}->read(statefile => 'cache_vtom_env_' . $self->{hostname} . '_' . $self->{port}); - my $timestamp_cache = $options{statefile}->get(name => 'last_timestamp'); - my $environments = $options{statefile}->get(name => 'environments'); - if ($has_cache_file == 0 || !defined($timestamp_cache) || ((time() - $timestamp_cache) > (($options{reload_cache_time}) * 60))) { - $environments = {}; - my $datas = { last_timestamp => time(), environments => $environments }; - my $result = $self->get(path => '/api/environment/list'); - if (defined($result->{result}->{rows})) { - foreach (@{$result->{result}->{rows}}) { - $environments->{$_->{id}} = $_->{name}; - } - } - $options{statefile}->write(data => $datas); - } - - return $environments; + + return $self->{option_results}->{hostname} . ':' . $self->{option_results}->{port}; } -sub cache_application { +sub get_token { my ($self, %options) = @_; - - my $has_cache_file = $options{statefile}->read(statefile => 'cache_vtom_app_' . $self->{hostname} . '_' . $self->{port}); - my $timestamp_cache = $options{statefile}->get(name => 'last_timestamp'); - my $applications = $options{statefile}->get(name => 'applications'); - if ($has_cache_file == 0 || !defined($timestamp_cache) || ((time() - $timestamp_cache) > (($options{reload_cache_time}) * 60))) { - $applications = {}; - my $datas = { last_timestamp => time(), applications => $applications }; - my $result = $self->get(path => '/api/application/list'); - if (defined($result->{result}->{rows})) { - foreach (@{$result->{result}->{rows}}) { - $applications->{$_->{id}} = { name => $_->{name}, envSId => $_->{envSId} }; - } + + my $has_cache_file = $self->{cache}->read(statefile => 'vtom_' . md5_hex($self->get_connection_info() . '_' . $self->{api_username})); + my $token = $self->{cache}->get(name => 'token'); + my $expires_on = $self->{cache}->get(name => 'expires_on'); + my $md5_secret_cache = $self->{cache}->get(name => 'md5_secret'); + my $md5_secret = md5_hex($self->{api_username} . $self->{api_password}); + + if ($has_cache_file == 0 || + !defined($token) || + (time() > $expires_on) || + (defined($md5_secret_cache) && $md5_secret_cache ne $md5_secret) + ) { + my $json_request = { + grant_type => 'password', + username => $self->{api_username}, + password => $self->{api_password}, + tokenLifetime => 7200, + tokenRefresh => \0 + }; + my $encoded; + eval { + $encoded = encode_json($json_request); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => 'cannot encode json request'); + $self->{output}->option_exit(); } - $options{statefile}->write(data => $datas); + + $self->settings(); + my $content = $self->{http}->request( + method => 'POST', + url_path => '/vtom/public/auth/1.0/authorize', + query_form_post => $encoded, + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + + my $decoded; + eval { + $decoded = JSON::XS->new->utf8->decode($content); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); + $self->{output}->option_exit(); + } + + $token = $decoded->{access_token}; + my $datas = { + updated => time(), + token => $token, + md5_secret => $md5_secret, + expires_on => time() + $decoded->{expires_in} + }; + $self->{cache}->write(data => $datas); } - - return $applications; + + return $token; } -sub get { +sub clean_token { + my ($self, %options) = @_; + + my $datas = { updated => time() }; + $self->{cache}->write(data => $datas); +} + +sub credentials { + my ($self, %options) = @_; + + my $creds = {}; + if (defined($self->{token}) && $self->{token} ne '') { + $creds = { + header => ['X-API-KEY: ' . $self->{token}], + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + }; + } else { + my $token = $self->get_token(); + $creds = { + header => ['X-API-KEY: ' . $token], + unknown_status => '', + warning_status => '', + critical_status => '' + }; + } + + return $creds; +} + +sub request_api { my ($self, %options) = @_; $self->settings(); + my $creds = $self->credentials(); + my ($content) = $self->{http}->request( + url_path => $options{endpoint}, + get_param => $options{get_param}, + %$creds + ); - my $response = $self->{http}->request(url_path => $options{path}, - critical_status => '', warning_status => ''); - my $content; + # Maybe token is invalid. so we retry + if (defined($self->{token}) && $self->{token} ne '' && $self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { + $self->clean_token(); + $creds = $self->credentials(); + $content = $self->{http}->request( + url_path => $options{endpoint}, + get_param => $options{get_param}, + %$creds, + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + } + + if (!defined($content) || $content eq '') { + $self->{output}->add_option_msg(short_msg => "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']"); + $self->{output}->option_exit(); + } + + my $decoded; eval { - $content = JSON::XS->new->utf8->decode($response); + $decoded = JSON::XS->new->allow_nonref(1)->utf8->decode($content); }; if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); + $self->{output}->add_option_msg(short_msg => "Cannot decode response (add --debug option to display returned content)"); $self->{output}->option_exit(); } - if (defined($content->{errmsg})) { - $self->{output}->add_option_msg(short_msg => "Cannot get data: " . $content->{errmsg}); + + return $decoded; +} + +sub write_cache_file { + my ($self, %options) = @_; + + $self->{cache}->read(statefile => 'cache_vtom_' . md5_hex($self->get_connection_info()) . '_' . $options{statefile}); + $self->{cache}->write(data => { + update_time => time(), + response => $options{response} + }); +} + +sub get_cache_file_response { + my ($self, %options) = @_; + + $self->{cache}->read(statefile => 'cache_vtom_' . md5_hex($self->get_connection_info()) . '_' . $options{statefile}); + my $response = $self->{cache}->get(name => 'response'); + if (!defined($response)) { + $self->{output}->add_option_msg(short_msg => 'Cache file missing'); $self->{output}->option_exit(); } - - return $content; + return $response; +} + +sub call_jobs { + my ($self, %options) = @_; + + return $self->request_api( + endpoint => '/vtom/public/monitoring/1.0/jobs/status' + ); +} + +sub cache_jobs { + my ($self, %options) = @_; + + my $datas = $self->call_jobs(); + $self->write_cache_file( + statefile => 'jobsStatus', + response => $datas + ); + + return $datas; +} + +sub get_jobs { + my ($self, %options) = @_; + + return $self->get_cache_file_response(statefile => 'jobsStatus') + if (defined($self->{option_results}->{cache_use})); + return $self->call_jobs(); } 1; @@ -178,39 +316,41 @@ __END__ =head1 NAME -VTOM REST API - -=head1 SYNOPSIS - -VTOM Rest API custom mode +VTOM Rest API =head1 REST API OPTIONS +VTOM Rest API + =over 8 =item B<--hostname> -VTOM hostname. +Set hostname. =item B<--port> -Port used (Default: 30080) +Port used (Default: 30002) =item B<--proto> -Specify https if needed (Default: 'http') +Specify https if needed (Default: 'https') -=item B<--username> +=item B<--api-username> -VTOM username. +API username. -=item B<--password> +=item B<--api-password> -VTOM password. +API password. + +=item B<--token> + +Use token authentication directly. =item B<--timeout> -Set HTTP timeout +Set timeout in seconds (Default: 30). =back diff --git a/centreon-plugins/apps/vtom/restapi/mode/cache.pm b/centreon-plugins/apps/vtom/restapi/mode/cache.pm new file mode 100644 index 000000000..f2fcbabdc --- /dev/null +++ b/centreon-plugins/apps/vtom/restapi/mode/cache.pm @@ -0,0 +1,60 @@ +# +# Copyright 2022 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::vtom::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_jobs(); + $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/centreon-plugins/apps/vtom/restapi/mode/jobs.pm b/centreon-plugins/apps/vtom/restapi/mode/jobs.pm new file mode 100644 index 000000000..a29945912 --- /dev/null +++ b/centreon-plugins/apps/vtom/restapi/mode/jobs.pm @@ -0,0 +1,388 @@ +# +# Copyright 2022 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::vtom::restapi::mode::jobs; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::misc; +use centreon::plugins::statefile; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use Digest::MD5; +use DateTime; + +sub custom_status_output { + my ($self, %options) = @_; + + my $msg = 'status: ' . $self->{result_values}->{status}; + if ($self->{result_values}->{message} ne '') { + $msg .= ' [message: ' . $self->{result_values}->{message} . ']'; + } + + return $msg; +} + +sub custom_long_output { + my ($self, %options) = @_; + + return 'started since: ' . centreon::plugins::misc::change_seconds(value => $self->{result_values}->{elapsed}); +} + +sub custom_long_calc { + my ($self, %options) = @_; + + $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; + $self->{result_values}->{name} = $options{new_datas}->{$self->{instance} . '_name'}; + $self->{result_values}->{environment} = $options{new_datas}->{$self->{instance} . '_environment'}; + $self->{result_values}->{application} = $options{new_datas}->{$self->{instance} . '_application'}; + $self->{result_values}->{elapsed} = $options{new_datas}->{$self->{instance} . '_elapsed'}; + + return -11 if ($self->{result_values}->{status} !~ /Running/i); + + return 0; +} + +sub custom_success_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel}, + unit => '%', + instances => [$self->{result_values}->{environment}, $self->{result_values}->{application}, $self->{result_values}->{name}], + value => $self->{result_values}->{success}, + 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 prefix_global_output { + my ($self, %options) = @_; + + return 'Number of jobs '; +} + +sub prefix_job_output { + my ($self, %options) = @_; + + return sprintf( + "job '%s/%s/%s' ", + $options{instance_value}->{environment}, + $options{instance_value}->{application}, + $options{instance_value}->{name} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0, cb_prefix_output => 'prefix_global_output', }, + { name => 'jobs', type => 1, cb_prefix_output => 'prefix_job_output', message_multiple => 'All jobs are ok', , skipped_code => { -10 => 1, -11 => 1 } } + ]; + + $self->{maps_counters}->{jobs} = [ + { + label => 'status', + type => 2, + critical_default => '%{status} =~ /Error/i', + set => { + key_values => [ + { name => 'status' }, { name => 'name' }, { name => 'environment' }, + { name => 'application' }, { name => 'exit_code' }, { name => 'message' } + ], + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'long', type => 2, set => { + key_values => [ + { name => 'status' }, { name => 'name' }, { name => 'environment' }, + { name => 'application' }, { name => 'elapsed' } + ], + closure_custom_calc => $self->can('custom_long_calc'), + closure_custom_output => $self->can('custom_long_output'), + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'success-prct', nlabel => 'job.success.percentage', set => { + key_values => [ + { name => 'success' }, { name => 'name' }, + { name => 'environment' }, { name => 'application' } + ], + output_template => 'success: %.2f %%', + closure_custom_perfdata => $self->can('custom_success_perfdata') + } + } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'running', nlabel => 'jobs.running.count', set => { + key_values => [ { name => 'running' }, { name => 'total' } ], + output_template => 'running: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'errors', nlabel => 'jobs.errors.count', set => { + key_values => [ { name => 'error' }, { name => 'total' } ], + output_template => 'errors: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'waiting', nlabel => 'jobs.waiting.count', set => { + key_values => [ { name => 'waiting' }, { name => 'total' } ], + output_template => 'waiting: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'finished', nlabel => 'jobs.finished.count', set => { + key_values => [ { name => 'finished' }, { name => 'total' } ], + output_template => 'finished: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'notscheduled', nlabel => 'jobs.notscheduled.count', set => { + key_values => [ { name => 'notscheduled' }, { name => 'total' } ], + output_template => 'not scheduled: %s', + perfdatas => [ + { template => '%s', min => 0, max => 'total' } + ] + } + }, + { label => 'descheduled', nlabel => 'jobs.descheduled.count', set => { + key_values => [ { name => 'descheduled' }, { name => 'total' } ], + output_template => 'descheduled: %s', + perfdatas => [ + { template => '%s', min => 0, max => '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 => { + 'filter-application:s' => { name => 'filter_application' }, + 'filter-environment:s' => { name => 'filter_environment' }, + 'filter-name:s' => { name => 'filter_name' }, + 'timezone:s' => { name => 'timezone' } + }); + + $self->{cache_status} = centreon::plugins::statefile->new(%options); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->{cache_status}->check_options(%options); +} + +sub get_success { + my ($self, %options) = @_; + + if (!defined($options{history}->{ $options{id} })) { + $options{history}->{ $options{id} } = { lastEndDateTime => '', status => [] }; + } + + if ($options{job}->{status} =~ /finished|errors/i) { + if ($options{history}->{ $options{id} }->{lastEndDateTime} ne $options{job}->{endDateTime}) { + push @{$options{history}->{ $options{id} }->{status}}, $options{job}->{status}; + $options{history}->{ $options{id} }->{lastEndDateTime} = $options{job}->{endDateTime}; + } + } + + return undef if (scalar(@{$options{history}->{ $options{id} }->{status}}) <= 0); + shift @{$options{history}->{ $options{id} }->{status}} + if (scalar(@{$options{history}->{ $options{id} }->{status}}) > 10); + my $success = 0; + foreach (@{$options{history}->{ $options{id} }->{status}}) { + $success++ if (/finished/i); + } + + $success = $success * 100 / scalar(@{$options{history}->{ $options{id} }->{status}}); + return $success; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $jobs = $options{custom}->get_jobs(); + + $self->{cache_status}->read( + statefile => 'vtom_' . Digest::MD5::md5_hex( + $options{custom}->get_connection_info() . '_' . + (defined($self->{option_results}->{filter_counters}) ? $self->{option_results}->{filter_counters} : '') . '_' . + (defined($self->{option_results}->{filter_application}) ? $self->{option_results}->{filter_application} : '') . '_' . + (defined($self->{option_results}->{filter_environment}) ? $self->{option_results}->{filter_environment} : '') . '_' . + (defined($self->{option_results}->{filter_name}) ? $self->{option_results}->{filter_name} : '') + ) + ); + my $history = $self->{cache_status}->get(name => 'history'); + $history = {} if (!defined($history)); + + my $current_time = time(); + $self->{global} = { total => 0, running => 0, waiting => 0, finished => 0, error => 0, notscheduled => 0, descheduled => 0 }; + $self->{jobs} = {}; + foreach my $job (@$jobs) { + my $id = $job->{environment} . '/' . $job->{application} . '/' . $job->{name}; + + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $job->{name} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $job->{name} . "': no matching filter.", debug => 1); + next; + } + if (defined($self->{option_results}->{filter_application}) && $self->{option_results}->{filter_application} ne '' && + $job->{application} !~ /$self->{option_results}->{filter_application}/) { + $self->{output}->output_add(long_msg => "skipping '" . $job->{name} . "': no matching filter.", debug => 1); + next; + } + if (defined($self->{option_results}->{filter_environment}) && $self->{option_results}->{filter_environment} ne '' && + $job->{environment} !~ /$self->{option_results}->{filter_environment}/) { + $self->{output}->output_add(long_msg => "skipping '" . $job->{name} . "': no matching filter.", debug => 1); + next; + } + + my $elapsed; + # 2022-01-18 01:08:33 + if (defined($job->{beginDateTime}) && $job->{beginDateTime} =~ /^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$/) { + my $tz = {}; + if (defined($self->{option_results}->{timezone}) && $self->{option_results}->{timezone} ne '') { + $tz = centreon::plugins::misc::set_timezone(name => $self->{option_results}->{timezone}); + } + my $dt = DateTime->new( + year => $1, + month => $2, + day => $3, + hour => $4, + minute => $5, + second => $6, + %$tz + ); + $elapsed = $current_time - $dt->epoch(); + } + + my $message = defined($job->{message}) ? $job->{message} : ''; + $message =~ s/\|/-/msg; + + my $success = $self->get_success( + id => $id, + job => $job, + history => $history + ); + + $self->{global}->{total}++; + $self->{global}->{ lc($job->{status}) }++; + $self->{jobs}->{$id} = { + name => $job->{name}, + application => $job->{application}, + environment => $job->{environment}, + status => lc($job->{status}), + message => $message, + exit_code => defined($job->{returnCode}) ? $job->{returnCode} : '-', + elapsed => $elapsed, + success => $success + }; + } + + $self->{cache_status}->write(data => $history); +} + +1; + +__END__ + +=head1 MODE + +Check jobs. + +=over 8 + +=item B<--filter-counters> + +Only display some counters (regexp can be used). +Example: --filter-counters='total-error' + +=item B<--filter-environment> + +Filter environment name (cannot be a regexp). + +=item B<--filter-application> + +Filter application name (cannot be a regexp). + +=item B<--filter-name> + +Filter name (can be a regexp). + +=item B<--timezone> + +Set date timezone. +Can use format: 'Europe/London' or '+0100'. + +=item B<--warning-status> + +Set warning threshold for status (Default: -) +Can used special variables like: %{name}, %{status}, %{exit_code}, %{message}, %{environment}, %{application} + +=item B<--critical-status> + +Set critical threshold for status (Default: '%{exit_code} =~ /Error/i'). +Can used special variables like: %{name}, %{status}, %{exit_code}, %{message}, %{environment}, %{application} + +=item B<--warning-long> + +Set warning threshold for long jobs (Default: none) +Can used special variables like: %{name}, %{status}, %{elapsed}, %{application} + +=item B<--critical-long> + +Set critical threshold for long jobs (Default: none). +Can used special variables like: %{name}, %{status}, %{elapsed}, %{application} + +=item B<--warning-*> B<--critical-*> + +Thresholds. +Can be: 'running', 'errors', 'waiting', +'finished', 'notscheduled', 'descheduled', +'success-prct'. + +=back + +=cut diff --git a/centreon-plugins/apps/vtom/restapi/mode/jobstatus.pm b/centreon-plugins/apps/vtom/restapi/mode/jobstatus.pm deleted file mode 100644 index 4909970db..000000000 --- a/centreon-plugins/apps/vtom/restapi/mode/jobstatus.pm +++ /dev/null @@ -1,338 +0,0 @@ -# -# Copyright 2022 Centreon (http://www.centreon.com/) -# -# Centreon is a full-fledged industry-strength solution that meets -# the needs in IT infrastructure and application monitoring for -# service performance. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -package apps::vtom::restapi::mode::jobstatus; - -use base qw(centreon::plugins::templates::counter); - -use strict; -use warnings; -use centreon::plugins::misc; -use centreon::plugins::statefile; -use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); - -sub custom_status_output { - my ($self, %options) = @_; - my $msg = 'status : ' . $self->{result_values}->{status}; - if ($self->{result_values}->{information} ne '') { - $msg .= ' [information: ' . $self->{result_values}->{information} . ']'; - } - - return $msg; -} - -sub custom_status_calc { - my ($self, %options) = @_; - - $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; - $self->{result_values}->{name} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{environment} = $options{new_datas}->{$self->{instance} . '_environment'}; - $self->{result_values}->{application} = $options{new_datas}->{$self->{instance} . '_application'}; - $self->{result_values}->{exit_code} = $options{new_datas}->{$self->{instance} . '_exit_code'}; - $self->{result_values}->{family} = $options{new_datas}->{$self->{instance} . '_family'}; - $self->{result_values}->{information} = $options{new_datas}->{$self->{instance} . '_information'}; - - return 0; -} - -sub custom_long_output { - my ($self, %options) = @_; - my $msg = 'started since : ' . centreon::plugins::misc::change_seconds(value => $self->{result_values}->{elapsed}); - - return $msg; -} - -sub custom_long_calc { - my ($self, %options) = @_; - - $self->{result_values}->{status} = $options{new_datas}->{$self->{instance} . '_status'}; - $self->{result_values}->{name} = $options{new_datas}->{$self->{instance} . '_name'}; - $self->{result_values}->{environment} = $options{new_datas}->{$self->{instance} . '_environment'}; - $self->{result_values}->{application} = $options{new_datas}->{$self->{instance} . '_application'}; - $self->{result_values}->{elapsed} = $options{new_datas}->{$self->{instance} . '_elapsed'}; - $self->{result_values}->{family} = $options{new_datas}->{$self->{instance} . '_family'}; - - return -11 if ($self->{result_values}->{status} !~ /Running/i); - - return 0; -} - -sub prefix_global_output { - my ($self, %options) = @_; - - return "Total Job "; -} - -sub prefix_job_output { - my ($self, %options) = @_; - - return "job '" . $options{instance_value}->{environment} . '/' . $options{instance_value}->{application} . '/' . $options{instance_value}->{name} . "' "; -} - -sub set_counters { - my ($self, %options) = @_; - - $self->{maps_counters_type} = [ - { name => 'global', type => 0, cb_prefix_output => 'prefix_global_output', }, - { name => 'job', type => 1, cb_prefix_output => 'prefix_job_output', message_multiple => 'All jobs are ok', , skipped_code => { -11 => 1 } }, - ]; - - $self->{maps_counters}->{job} = [ - { - label => 'status', - type => 2, - critical_default => '%{status} =~ /Error/i', - set => { - key_values => [ { name => 'status' }, { name => 'name' }, { name => 'environment' }, - { name => 'application' }, { name => 'exit_code' }, { name => 'family' }, { name => 'information' } ], - closure_custom_calc => $self->can('custom_status_calc'), - closure_custom_output => $self->can('custom_status_output'), - closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => \&catalog_status_threshold_ng, - } - }, - { label => 'long', type => 2, set => { - key_values => [ { name => 'status' }, { name => 'name' }, { name => 'environment' }, - { name => 'application' }, { name => 'elapsed' }, { name => 'family' } ], - closure_custom_calc => $self->can('custom_long_calc'), - closure_custom_output => $self->can('custom_long_output'), - closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => \&catalog_status_threshold_ng, - } - }, - ]; - - $self->{maps_counters}->{global} = [ - { label => 'total-error', nlabel => 'jobs.errors.total.count', set => { - key_values => [ { name => 'error' }, { name => 'total' } ], - output_template => 'error : %s', - perfdatas => [ - { label => 'total_error', template => '%s', min => 0, max => 'total' } - ] - } - }, - { label => 'total-running', nlabel => 'jobs.running.total.count', set => { - key_values => [ { name => 'running' }, { name => 'total' } ], - output_template => 'running : %s', - perfdatas => [ - { label => 'total_running', template => '%s', min => 0, max => 'total' } - ] - } - }, - { label => 'total-unplanned', nlabel => 'jobs.unplanned.total.count', set => { - key_values => [ { name => 'unplanned' }, { name => 'total' } ], - output_template => 'unplanned : %s', - perfdatas => [ - { label => 'total_unplanned', template => '%s', min => 0, max => 'total' } - ] - } - }, - { label => 'total-finished', nlabel => 'jobs.finished.total.count', set => { - key_values => [ { name => 'finished' }, { name => 'total' } ], - output_template => 'finished : %s', - perfdatas => [ - { label => 'total_finished', template => '%s', min => 0, max => 'total' } - ] - } - }, - { label => 'total-coming', nlabel => 'jobs.coming.total.count', set => { - key_values => [ { name => 'coming' }, { name => 'total' } ], - output_template => 'coming : %s', - perfdatas => [ - { label => 'total_coming', template => '%s', min => 0, max => 'total' }, - ] - } - } - ]; -} - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options); - bless $self, $class; - - $options{options}->add_options(arguments => { - "filter-application:s" => { name => 'filter_application' }, - "filter-environment:s" => { name => 'filter_environment' }, - "filter-name:s" => { name => 'filter_name' }, - "filter-family:s" => { name => 'filter_family' }, - "reload-cache-time:s" => { name => 'reload_cache_time', default => 180 }, - }); - $self->{statefile_cache_app} = centreon::plugins::statefile->new(%options); - $self->{statefile_cache_env} = centreon::plugins::statefile->new(%options); - - return $self; -} - -sub check_options { - my ($self, %options) = @_; - $self->SUPER::check_options(%options); - - $self->{statefile_cache_app}->check_options(%options); - $self->{statefile_cache_env}->check_options(%options); -} - -my %mapping_job_status = ( - R => 'Running', - U => 'Unplanned', - F => 'Finished', - W => 'Coming', - E => 'Error', -); - -sub manage_selection { - my ($self, %options) = @_; - - my $environments = $options{custom}->cache_environment(statefile => $self->{statefile_cache_env}, - reload_cache_time => $self->{option_results}->{reload_cache_time}); - my $applications = $options{custom}->cache_application(statefile => $self->{statefile_cache_app}, - reload_cache_time => $self->{option_results}->{reload_cache_time}); - - $self->{job} = {}; - $self->{global} = { total => 0, running => 0, unplanned => 0, finished => 0, coming => 0, error => 0 }; - my $path = '/api/job/getAll'; - if (defined($self->{option_results}->{filter_application}) && $self->{option_results}->{filter_application} ne '') { - $path = '/api/job/list?applicationName=' . $self->{option_results}->{filter_application}; - } - if (defined($self->{option_results}->{filter_environment}) && $self->{option_results}->{filter_environment} ne '') { - $path = '/api/job/list?environmentName=' . $self->{option_results}->{filter_environment}; - } - my $result = $options{custom}->get(path => $path); - my $entries = defined($result->{result}) && ref($result->{result}) eq 'ARRAY' ? - $result->{result} : (defined($result->{result}->{rows}) ? - $result->{result}->{rows} : []); - - my $current_time = time(); - foreach my $entry (@{$entries}) { - my $application_sid = defined($entry->{applicationSId}) ? $entry->{applicationSId} : - (defined($entry->{appSId}) ? $entry->{appSId} : undef); - my $application = defined($application_sid) && defined($applications->{$application_sid}) ? - $applications->{$application_sid}->{name} : 'unknown'; - my $environment = defined($application_sid) && defined($applications->{$application_sid}) && defined($environments->{$applications->{$application_sid}->{envSId}}) ? - $environments->{$applications->{$application_sid}->{envSId}} : 'unknown'; - my $display = $environment . '/' . $application . '/' . $entry->{name}; - - if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && - $display !~ /$self->{option_results}->{filter_name}/) { - $self->{output}->output_add(long_msg => "skipping '" . $display . "': no matching filter.", debug => 1); - next; - } - my $family = defined($entry->{family}) ? $entry->{family} : '-'; - if (defined($self->{option_results}->{filter_family}) && $self->{option_results}->{filter_family} ne '' && - $family !~ /$self->{option_results}->{filter_family}/) { - $self->{output}->output_add(long_msg => "skipping '" . $family . "': no matching filter.", debug => 1); - next; - } - - - my $information = defined($entry->{information}) ? $entry->{information} : ''; - $information =~ s/\|/-/msg; - - $self->{global}->{total} += 1; - $self->{global}->{lc($mapping_job_status{$entry->{status}})} += 1; - $self->{job}->{$entry->{id}} = { - name => $entry->{name}, - status => $mapping_job_status{$entry->{status}}, information => $information, - exit_code => defined($entry->{retcode}) ? $entry->{retcode} : '-', - family => $family, application => $application, environment => $environment, - elapsed => defined($entry->{timeBegin}) ? ( $current_time - $entry->{timeBegin}) : undef, - }; - } - - if (scalar(keys %{$self->{job}}) <= 0) { - $self->{output}->add_option_msg(short_msg => "No job found."); - $self->{output}->option_exit(); - } -} - -1; - -__END__ - -=head1 MODE - -Check job status. - -=over 8 - -=item B<--filter-environment> - -Filter environment name (cannot be a regexp). - -=item B<--filter-application> - -Filter application name (cannot be a regexp). - -=item B<--filter-name> - -Filter name (can be a regexp). - -=item B<--filter-family> - -Filter family (can be a regexp). - -=item B<--filter-counters> - -Only display some counters (regexp can be used). -Example: --filter-counters='^total-error$' - -=item B<--warning-*> - -Threshold warning. -Can be: 'total-error', 'total-running', 'total-unplanned', -'total-finished', 'total-coming'. - -=item B<--critical-*> - -Threshold critical. -Can be: 'total-error', 'total-running', 'total-unplanned', -'total-finished', 'total-coming'. - -=item B<--warning-status> - -Set warning threshold for status (Default: -) -Can used special variables like: %{name}, %{status}, -%{exit_code}, %{family}, %{information}, %{environment}, %{application} - -=item B<--critical-status> - -Set critical threshold for status (Default: '%{exit_code} =~ /Error/i'). -Can used special variables like: %{name}, %{status}, -%{exit_code}, %{family}, %{information}, %{environment}, %{application} - -=item B<--warning-long> - -Set warning threshold for long jobs (Default: none) -Can used special variables like: %{name}, %{status}, %{elapsed}, -%{family}, %{environment}, %{application} - -=item B<--critical-long> - -Set critical threshold for long jobs (Default: none). -Can used special variables like: %{name}, %{status}, %{elapsed}, -%{family}, %{environment}, %{application} - -=item B<--reload-cache-time> - -Time in seconds before reloading cache file (default: 180). - -=back - -=cut diff --git a/centreon-plugins/apps/vtom/restapi/mode/listjobs.pm b/centreon-plugins/apps/vtom/restapi/mode/listjobs.pm new file mode 100644 index 000000000..b7852cc54 --- /dev/null +++ b/centreon-plugins/apps/vtom/restapi/mode/listjobs.pm @@ -0,0 +1,111 @@ +# +# Copyright 2022 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::vtom::restapi::mode::listjobs; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => {}); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $jobs = $options{custom}->call_jobs(); + my $results = []; + foreach my $job (@$jobs) { + push @$results, $job; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->output_add( + long_msg => sprintf( + '[name: %s][application: %s][environment: %s][status: %s]', + $_->{name}, + $_->{application}, + $_->{environment}, + $_->{status} + ) + ); + } + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List jobs:' + ); + + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'application', 'environment', 'status']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->add_disco_entry( + name => $_->{name}, + application => $_->{application}, + environment => $_->{environment}, + status => $_->{status} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List jobs. + +=over 8 + +=back + +=cut diff --git a/centreon-plugins/apps/vtom/restapi/plugin.pm b/centreon-plugins/apps/vtom/restapi/plugin.pm index e04950c14..65207021b 100644 --- a/centreon-plugins/apps/vtom/restapi/plugin.pm +++ b/centreon-plugins/apps/vtom/restapi/plugin.pm @@ -29,12 +29,13 @@ sub new { my $self = $class->SUPER::new(package => __PACKAGE__, %options); bless $self, $class; - $self->{version} = '1.0'; $self->{modes} = { - 'job-status' => 'apps::vtom::restapi::mode::jobstatus' + 'cache' => 'apps::vtom::restapi::mode::cache', + 'jobs' => 'apps::vtom::restapi::mode::jobs', + 'list-jobs' => 'apps::vtom::restapi::mode::listjobs' }; - $self->{custom_modes}{api} = 'apps::vtom::restapi::custom::api'; + $self->{custom_modes}->{api} = 'apps::vtom::restapi::custom::api'; return $self; } diff --git a/centreon-plugins/hardware/devices/hikvision/nvr/isapi/mode/time.pm b/centreon-plugins/hardware/devices/hikvision/nvr/isapi/mode/time.pm index 600035258..32664dd8d 100644 --- a/centreon-plugins/hardware/devices/hikvision/nvr/isapi/mode/time.pm +++ b/centreon-plugins/hardware/devices/hikvision/nvr/isapi/mode/time.pm @@ -96,7 +96,7 @@ sub get_target_time { $self->{output}->option_exit(); } - my $tz = $7; + my $tz = { time_zone => $7 }; if (defined($self->{option_results}->{timezone}) && $self->{option_results}->{timezone} ne '') { $tz = centreon::plugins::misc::set_timezone(name => $self->{option_results}->{timezone}); } @@ -108,7 +108,7 @@ sub get_target_time { hour => $4, minute => $5, second => $6, - time_zone => $7 + %$tz ); return ($dt->epoch(), $result->{localTime});