diff --git a/packaging/centreon-plugin-Applications-Exense-Step-Restapi/deb.json b/packaging/centreon-plugin-Applications-Exense-Step-Restapi/deb.json new file mode 100644 index 000000000..9757fe112 --- /dev/null +++ b/packaging/centreon-plugin-Applications-Exense-Step-Restapi/deb.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ + ] +} diff --git a/packaging/centreon-plugin-Applications-Exense-Step-Restapi/pkg.json b/packaging/centreon-plugin-Applications-Exense-Step-Restapi/pkg.json new file mode 100644 index 000000000..efc2f2ae7 --- /dev/null +++ b/packaging/centreon-plugin-Applications-Exense-Step-Restapi/pkg.json @@ -0,0 +1,9 @@ +{ + "pkg_name": "centreon-plugin-Applications-Exense-Step-Restapi", + "pkg_summary": "Centreon Plugin", + "plugin_name": "centreon_exense_step_restapi.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "apps/exense/step/restapi/" + ] +} diff --git a/packaging/centreon-plugin-Applications-Exense-Step-Restapi/rpm.json b/packaging/centreon-plugin-Applications-Exense-Step-Restapi/rpm.json new file mode 100644 index 000000000..e9dff7552 --- /dev/null +++ b/packaging/centreon-plugin-Applications-Exense-Step-Restapi/rpm.json @@ -0,0 +1,5 @@ +{ + "dependencies": [ + "perl(DateTime)" + ] +} diff --git a/src/apps/exense/step/restapi/custom/api.pm b/src/apps/exense/step/restapi/custom/api.pm new file mode 100644 index 000000000..ea42d7676 --- /dev/null +++ b/src/apps/exense/step/restapi/custom/api.pm @@ -0,0 +1,319 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::exense::step::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) = @_; + my $self = {}; + bless $self, $class; + + if (!defined($options{output})) { + print "Class Custom: Need to specify 'output' argument.\n"; + exit 3; + } + if (!defined($options{options})) { + $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); + $options{output}->option_exit(); + } + + if (!defined($options{noptions})) { + $options{options}->add_options(arguments => { + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port' }, + 'proto:s' => { name => 'proto' }, + 'api-username:s' => { name => 'api_username' }, + 'api-password:s' => { name => 'api_password' }, + 'token:s' => { name => 'token' }, + 'timeout:s' => { name => 'timeout' } + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl'); + $self->{cache_connect} = centreon::plugins::statefile->new(%options); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults {} + +sub check_options { + my ($self, %options) = @_; + + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : ''; + $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; + $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443; + $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->{token} = (defined($self->{option_results}->{token})) ? $self->{option_results}->{token} : ''; + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 30; + $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} : ''; + + if ($self->{hostname} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify hostname option.'); + $self->{output}->option_exit(); + } + if ($self->{token} ne '') { + return 0; + } + + if ($self->{api_username} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --api-username or --token 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(); + } + + $self->{cache_connect}->check_options(option_results => $self->{option_results}); + return 0; +} + +sub get_connection_infos { + my ($self, %options) = @_; + + return $self->{hostname} . '_' . $self->{http}->get_port(); +} + +sub get_hostname { + my ($self, %options) = @_; + + return $self->{hostname}; +} + +sub get_port { + my ($self, %options) = @_; + + return $self->{port}; +} + + +sub build_options_for_httplib { + my ($self, %options) = @_; + + $self->{option_results}->{hostname} = $self->{hostname}; + $self->{option_results}->{port} = $self->{port}; + $self->{option_results}->{proto} = $self->{proto}; +} + +sub settings { + my ($self, %options) = @_; + + $self->build_options_for_httplib(); + $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}}); +} + +sub clean_session_id { + my ($self, %options) = @_; + + my $datas = { updated => time() }; + $self->{cache_connect}->write(data => $datas); +} + +sub get_session_id { + my ($self, %options) = @_; + + my $has_cache_file = $self->{cache_connect}->read(statefile => 'exense_step_' . md5_hex($self->{option_results}->{hostname}) . '_' . md5_hex($self->{option_results}->{api_username})); + my $session_id = $self->{cache_connect}->get(name => 'session_id'); + my $md5_secret_cache = $self->{cache_connect}->get(name => 'md5_secret'); + my $md5_secret = md5_hex($self->{api_username} . $self->{api_password}); + + if ($has_cache_file == 0 || + !defined($session_id) || + (defined($md5_secret_cache) && $md5_secret_cache ne $md5_secret) + ) { + my $json_request = { username => $self->{api_username}, password => $self->{api_password} }; + my $encoded = centreon::plugins::misc::json_encode($json_request); + unless($encoded) { + $self->{output}->add_option_msg(short_msg => 'cannot encode json request'); + $self->{output}->option_exit(); + } + + my ($content) = $self->{http}->request( + method => 'POST', + url_path => '/rest/access/login', + query_form_post => $encoded, + warning_status => '', unknown_status => '', critical_status => '' + ); + + if ($self->{http}->get_code() != 200) { + $self->{output}->add_option_msg(short_msg => "Authentication error [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']"); + $self->{output}->option_exit(); + } + + my (@cookies) = $self->{http}->get_first_header(name => 'Set-Cookie'); + foreach my $cookie (@cookies) { + $session_id = $1 if ($cookie =~ /sessionid=(.+?);/); + } + + if (!defined($session_id)) { + $self->{output}->add_option_msg(short_msg => "Cannot get cookie"); + $self->{output}->option_exit(); + } + + my $datas = { + updated => time(), + session_id => $session_id, + md5_secret => $md5_secret + }; + $self->{cache_connect}->write(data => $datas); + } + + return $session_id; +} + +sub credentials { + my ($self, %options) = @_; + + my $creds = {}; + if ($self->{token} ne '') { + $creds = { + header => ['Authorization: Bearer ' . $self->{token}], + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + }; + } else { + my $session_id = $self->get_session_id(); + $creds = { + header => ['Cookie: sessionid=' . $session_id], + warning_status => '', + unknown_status => '', + critical_status => '' + }; + } + + return $creds; +} + +sub request { + my ($self, %options) = @_; + + my $endpoint = $options{endpoint}; + + $self->settings(); + my $creds = $self->credentials(); + + my $content = $self->{http}->request( + method => $options{method}, + url_path => $endpoint, + get_param => $options{get_param}, + query_form_post => $options{query_form_post}, + %$creds + ); + + # Maybe there is an issue with the token. So we retry. + if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { + $self->clean_session_id(); + $creds = $self->credentials(); + $creds->{unknown_status} = $self->{unknown_http_status}; + $creds->{warning_status} = $self->{warning_status}; + $creds->{critical_http_status} = $self->{critical_http_status}; + $content = $self->{http}->request( + method => $options{method}, + url_path => $endpoint, + get_param => $options{get_param}, + query_form_post => $options{query_form_post}, + %$creds + ); + } + + return if (defined($options{skip_decode})); + + my $decoded = centreon::plugins::misc::json_decode($content); + if (!defined($decoded)) { + $self->{output}->add_option_msg(short_msg => 'Error while retrieving data (add --debug option for detailed message)'); + $self->{output}->option_exit(); + } + + return $decoded; +} + +1; + +__END__ + +=head1 NAME + +Exense Step API + +=head1 SYNOPSIS + +Exense Step API + +=head1 REST API OPTIONS + +=over 8 + +=item B<--hostname> + +API hostname. + +=item B<--port> + +API port (default: 443) + +=item B<--proto> + +Specify https if needed (default: 'https') + +=item B<--token> + +Use token authentication. + +=item B<--api-username> + +Set API username + +=item B<--api-password> + +Set API password + +=item B<--timeout> + +Set HTTP timeout + +=back + +=head1 DESCRIPTION + +B. + +=cut diff --git a/src/apps/exense/step/restapi/mode/listplans.pm b/src/apps/exense/step/restapi/mode/listplans.pm new file mode 100644 index 000000000..e042c996a --- /dev/null +++ b/src/apps/exense/step/restapi/mode/listplans.pm @@ -0,0 +1,147 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::exense::step::restapi::mode::listplans; + +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 => { + 'tenant-name:s' => { name => 'tenant_name' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); + + if (!defined($self->{option_results}->{tenant_name}) || $self->{option_results}->{tenant_name} eq '') { + $self->{option_results}->{tenant_name} = '[All]'; + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $payload = $self->{option_results}->{tenant_name}; + $options{custom}->request(method => 'POST', endpoint => '/rest/tenants/current', query_form_post => $payload, skip_decode => 1); + + $payload = { + skip => 0, + limit => 4000000, + filters => [ + { + collectionFilter => { type => 'True', field => 'visible' } + } + ], + 'sort' => { + 'field' => 'attributes.name', + 'direction' => 'ASCENDING' + } + }; + $payload = centreon::plugins::misc::json_encode($payload); + unless($payload) { + $self->{output}->add_option_msg(short_msg => 'cannot encode json request'); + $self->{output}->option_exit(); + } + + my $plans = $options{custom}->request(method => 'POST', endpoint => '/rest/table/plans', query_form_post => $payload); + + my $results = []; + foreach my $plan (@{$plans->{data}}) { + # skip plans created by keyword single execution + next if ($plan->{visible} =~ /false|0/); + + push @$results, { + id => $plan->{id}, + name => $plan->{attributes}->{name} + }; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->output_add( + long_msg => sprintf( + '[id: %s][name: %s]', + $_->{id}, + $_->{name} + ) + ); + } + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List plans:' + ); + + $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 => ['id', 'name']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->add_disco_entry( + id => $_->{id}, + name => $_->{name} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List plans. + +=over 8 + +=item B<--tenant-name> + +Check plan of a tenant (default: '[All]'). + +=back + +=cut diff --git a/src/apps/exense/step/restapi/mode/listtenants.pm b/src/apps/exense/step/restapi/mode/listtenants.pm new file mode 100644 index 000000000..da1fd82e5 --- /dev/null +++ b/src/apps/exense/step/restapi/mode/listtenants.pm @@ -0,0 +1,111 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::exense::step::restapi::mode::listtenants; + +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 => {}); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $tenants = $options{custom}->request(method => 'GET', endpoint => '/rest/tenants'); + + my $results = []; + foreach my $tenant (@$tenants) { + push @$results, { + name => $tenant->{name}, + projectId => defined($tenant->{projectId}) ? $tenant->{projectId} : '', + global => $tenant->{global} =~ /true|1/i ? 1 : 0 + }; + } + + return $results; +} + +sub run { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->output_add( + long_msg => sprintf( + '[name: %s][projectId: %s][global: %s]', + $_->{name}, + $_->{projectId}, + $_->{global} + ) + ); + } + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List tenants:' + ); + + $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', 'projectId', 'global']); +} + +sub disco_show { + my ($self, %options) = @_; + + my $results = $self->manage_selection(%options); + foreach (@$results) { + $self->{output}->add_disco_entry(%$_); + } +} + +1; + +__END__ + +=head1 MODE + +List tenants. + +=over 8 + +=back + +=cut diff --git a/src/apps/exense/step/restapi/mode/plans.pm b/src/apps/exense/step/restapi/mode/plans.pm new file mode 100644 index 000000000..af077a018 --- /dev/null +++ b/src/apps/exense/step/restapi/mode/plans.pm @@ -0,0 +1,507 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::exense::step::restapi::mode::plans; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use DateTime; +use POSIX; +use JSON::XS; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use centreon::plugins::misc; + +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_exec_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} }, + instances => $self->{result_values}->{name}, + unit => $self->{instance_mode}->{option_results}->{unit}, + value => $self->{result_values}->{lastExecSeconds} >= 0 ? floor($self->{result_values}->{lastExecSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastExecSeconds}, + 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_exec_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => $self->{result_values}->{lastExecSeconds} >= 0 ? floor($self->{result_values}->{lastExecSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }) : $self->{result_values}->{lastExecSeconds}, + threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' }, + { label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' } + ] + ); +} + +sub custom_duration_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + nlabel => $self->{nlabel} . '.' . $unitdiv_long->{ $self->{instance_mode}->{option_results}->{unit} }, + instances => $self->{result_values}->{name}, + unit => $self->{instance_mode}->{option_results}->{unit}, + value => floor($self->{result_values}->{durationSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }), + 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_duration_threshold { + my ($self, %options) = @_; + + return $self->{perfdata}->threshold_check( + value => floor($self->{result_values}->{durationSeconds} / $unitdiv->{ $self->{instance_mode}->{option_results}->{unit} }), + threshold => [ + { label => 'critical-' . $self->{thlabel}, exit_litteral => 'critical' }, + { label => 'warning-'. $self->{thlabel}, exit_litteral => 'warning' }, + { label => 'unknown-'. $self->{thlabel}, exit_litteral => 'unknown' } + ] + ); +} + +sub custom_execution_status_output { + my ($self, %options) = @_; + + return sprintf( + "result: %s, status: %s", + $self->{result_values}->{result}, + $self->{result_values}->{status} + ); +} + +sub plan_long_output { + my ($self, %options) = @_; + + return sprintf( + "checking plan '%s'", + $options{instance_value}->{name} + ); +} + +sub prefix_plan_output { + my ($self, %options) = @_; + + my $plan_name = defined $options{instance_value}->{name} ? $options{instance_value}->{name} : 'unknown'; + + return sprintf("plan '%s' ", $plan_name); +} + +sub prefix_global_output { + my ($self, %options) = @_; + + return 'Number of plans '; +} + +sub prefix_execution_output { + my ($self, %options) = @_; + + return sprintf( + "execution '%s' [env: %s] [started: %s] ", + $options{instance_value}->{executionId}, + $options{instance_value}->{environment}, + $options{instance_value}->{started} + ); +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0, cb_prefix_output => 'prefix_global_output' }, + { name => 'plans', type => 3, cb_prefix_output => 'prefix_plan_output', cb_long_output => 'prefix_plan_output', indent_long_output => ' ', message_multiple => 'All plans are ok', + group => [ + { name => 'exec_detect', type => 0 }, + { name => 'failed', type => 0 }, + { name => 'timers', type => 0, skipped_code => { -10 => 1 } }, + { name => 'executions', type => 1, cb_prefix_output => 'prefix_execution_output', message_multiple => 'executions are ok', display_long => 1, sort_method => 'num', skipped_code => { -10 => 1 } } + ] + } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'plans-detected', display_ok => 0, nlabel => 'plans.detected.count', set => { + key_values => [ { name => 'detected' } ], + output_template => 'detected: %s', + perfdatas => [ + { template => '%s', min => 0 } + ] + } + } + ]; + + $self->{maps_counters}->{exec_detect} = [ + { label => 'plan-executions-detected', nlabel => 'plan.executions.detected.count', set => { + key_values => [ { name => 'detected' }, { name => 'name' } ], + output_template => 'number of plan executions detected: %s', + perfdatas => [ + { template => '%s', min => 0, label_extra_instance => 1, instance_use => 'name' } + ] + } + } + ]; + + $self->{maps_counters}->{failed} = [ + { label => 'plan-executions-failed-prct', nlabel => 'plan.executions.failed.percentage', set => { + key_values => [ { name => 'failedPrct' }, { name => 'name' } ], + output_template => 'number of failed executions: %.2f %%', + perfdatas => [ + { template => '%.2f', unit => '%', min => 0, max => 100, label_extra_instance => 1, instance_use => 'name' } + ] + } + } + ]; + + $self->{maps_counters}->{timers} = [ + { label => 'plan-execution-last', nlabel => 'plan.execution.last', set => { + key_values => [ { name => 'lastExecSeconds' }, { name => 'lastExecHuman' }, { name => 'name' } ], + output_template => 'last execution %s', + output_use => 'lastExecHuman', + closure_custom_perfdata => $self->can('custom_last_exec_perfdata'), + closure_custom_threshold_check => $self->can('custom_last_exec_threshold') + } + }, + { label => 'plan-running-duration', nlabel => 'plan.running.duration', set => { + key_values => [ { name => 'durationSeconds' }, { name => 'durationHuman' }, { name => 'name' } ], + output_template => 'running duration %s', + output_use => 'durationHuman', + closure_custom_perfdata => $self->can('custom_duration_perfdata'), + closure_custom_threshold_check => $self->can('custom_duration_threshold') + } + } + ]; + + $self->{maps_counters}->{executions} = [ + { + label => 'plan-execution-status', + type => 2, + set => { + key_values => [ + { name => 'status' }, {name => 'result' }, { name => 'planName' } + ], + closure_custom_output => $self->can('custom_execution_status_output'), + 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 => { + 'filter-plan-id:s' => { name => 'filter_plan_id' }, + 'filter-plan-name:s' => { name => 'filter_plan_name' }, + 'filter-environment:s' => { name => 'filter_environment' }, + 'since-timeperiod:s' => { name => 'since_timeperiod' }, + 'status-failed:s' => { name => 'status_failed' }, + 'only-last-execution' => { name => 'only_last_execution' }, + 'tenant-name:s' => { name => 'tenant_name' }, + 'timezone:s' => { name => 'timezone' }, + 'unit:s' => { name => 'unit', default => 's' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if ($self->{option_results}->{unit} eq '' || !defined($unitdiv->{$self->{option_results}->{unit}})) { + $self->{option_results}->{unit} = 's'; + } + + if (!defined($self->{option_results}->{since_timeperiod}) || $self->{option_results}->{since_timeperiod} eq '') { + $self->{option_results}->{since_timeperiod} = 86400; + } + + if (!defined($self->{option_results}->{tenant_name}) || $self->{option_results}->{tenant_name} eq '') { + $self->{option_results}->{tenant_name} = '[All]'; + } + + $self->{tz} = {}; + if (defined($self->{option_results}->{timezone}) && $self->{option_results}->{timezone} ne '') { + $self->{tz} = centreon::plugins::misc::set_timezone(name => $self->{option_results}->{timezone}); + } + + $self->{option_results}->{timezone} = 'UTC' if (!defined($self->{option_results}->{timezone}) || $self->{option_results}->{timezone} +eq ''); + + if (!defined($self->{option_results}->{status_failed}) || $self->{option_results}->{status_failed} eq '') { + $self->{option_results}->{status_failed} = '%{result} =~ /technical_error|failed|interrupted/i'; + } + + $self->{option_results}->{status_failed} =~ s/%\{(.*?)\}/\$values->{$1}/g; + $self->{option_results}->{status_failed} =~ s/%\((.*?)\)/\$values->{$1}/g; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $status_filter = {}; + if (defined($self->{option_results}->{filter_status}) && $self->{option_results}->{filter_status}[0] ne '') { + $status_filter->{statusFilter} = $self->{option_results}->{filter_status}; + } + + my $payload = $self->{option_results}->{tenant_name}; + $options{custom}->request(method => 'POST', endpoint => '/rest/tenants/current', query_form_post => $payload, skip_decode => 1); + + $payload = { + skip => 0, + limit => 4000000, + filters => [ + { + collectionFilter => { type => 'True', field => 'visible' } + } + ], + 'sort' => { + 'field' => 'attributes.name', + 'direction' => 'ASCENDING' + } + }; + eval { + $payload = encode_json($payload); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => 'cannot encode json request'); + $self->{output}->option_exit(); + } + + my $plans = $options{custom}->request(method => 'POST', endpoint => '/rest/table/plans', query_form_post => $payload); + + my $ctime = time(); + my $filterTime = ($ctime - $self->{option_results}->{since_timeperiod}) * 1000; + + $payload = { + skip => 0, + limit => 4000000, + filters => [ + { + collectionFilter => { type => 'Gte', field => 'startTime', value => $filterTime } + } + ], + 'sort' => { + 'field' => 'startTime', + 'direction' => 'DESCENDING' + } + }; + eval { + $payload = encode_json($payload); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => 'cannot encode json request'); + $self->{output}->option_exit(); + } + + my $executions = $options{custom}->request(method => 'POST', endpoint => '/rest/table/executions', query_form_post => $payload); + + $self->{global} = { detected => 0 }; + $self->{plans} = {}; + foreach my $plan (@{$plans->{data}}) { + # skip plans created by keyword single execution + next if (defined $plan->{visible} && $plan->{visible} =~ /false|0/); + next if (defined($self->{option_results}->{filter_plan_id}) && $self->{option_results}->{filter_plan_id} ne '' && + $plan->{id} !~ /$self->{option_results}->{filter_plan_id}/); + next if (defined($self->{option_results}->{filter_plan_name}) && $self->{option_results}->{filter_plan_name} ne '' && + $plan->{attributes}->{name} !~ /$self->{option_results}->{filter_plan_name}/); + + $self->{global}->{detected}++; + + $self->{plans}->{ $plan->{id} } = { + name => $plan->{attributes}->{name}, + exec_detect => { detected => 0, name => $plan->{attributes}->{name} }, + failed => { failedPrct => 0, name => $plan->{attributes}->{name} }, + timers => {}, + executions => {} + }; + + my ($last_exec, $older_running_exec); + my ($failed, $total) = (0, 0); + my $i = 0; + foreach my $plan_exec (@{$executions->{data}}) { + next if (!defined($plan_exec->{planId})); + next if ($plan_exec->{planId} ne $plan->{id}); + $plan_exec->{startTimeSec} = $plan_exec->{startTime} / 1000; + next if ($plan_exec->{startTimeSec} < ($ctime - $self->{option_results}->{since_timeperiod})); + next if (defined($self->{option_results}->{filter_environment}) && $self->{option_results}->{filter_environment} ne '' && + $plan_exec->{executionParameters}->{customParameters}->{env} !~ /$self->{option_results}->{filter_environment}/); + + # if the endTime is empty, we store this older running execution for later + if (!defined($plan_exec->{endTime}) || $plan_exec->{endTime} eq '') { + $older_running_exec = $plan_exec; + } + if (!defined($last_exec)) { + $last_exec = $plan_exec; + } + + $self->{plans}->{ $plan->{id} }->{exec_detect}->{detected}++; + $failed++ if ($self->{output}->test_eval(test => $self->{option_results}->{status_failed}, values => { result => lc($plan_exec->{result}), status => lc($plan_exec->{status}) })); + $total++; + + my $dt = DateTime->from_epoch(epoch => $plan_exec->{startTimeSec}, %{$self->{tz}}); + my $timeraised = sprintf( + '%02d-%02d-%02dT%02d:%02d:%02d (%s)', $dt->year, $dt->month, $dt->day, $dt->hour, $dt->minute, $dt->second, $self->{option_results}->{timezone} + ); + $self->{plans}->{ $plan->{id} }->{executions}->{$i} = { + executionId => $plan_exec->{id}, + planName => $plan->{attributes}->{name}, + environment => $plan_exec->{executionParameters}->{customParameters}->{env}, + started => $timeraised, + status => lc($plan_exec->{status}), + result => lc($plan_exec->{result}) + }; + $i++; + + last if (defined($self->{option_results}->{only_last_execution})); + } + + $self->{plans}->{ $plan->{id} }->{failed}->{failedPrct} = $total > 0 ? $failed * 100 / $total : 0; + + if (defined($last_exec)) { + $self->{plans}->{ $plan->{id} }->{timers} = { + name => $plan->{attributes}->{name}, + lastExecSeconds => defined($last_exec->{startTime}) ? $ctime - $last_exec->{startTimeSec} : -1, + lastExecHuman => defined($last_exec->{startTime}) ? centreon::plugins::misc::change_seconds(value => $ctime - $last_exec->{startTimeSec}) : 'never' + }; + } + + if (defined($older_running_exec)) { + my $duration = $ctime - $older_running_exec->{startTime}; + $self->{plans}->{ $plan->{name} }->{timers}->{durationSeconds} = $duration; + $self->{plans}->{ $plan->{name} }->{timers}->{durationHuman} = centreon::plugins::misc::change_seconds(value => $duration); + } + } +} + +1; + +__END__ + +=head1 MODE + +Check plans. + +=over 8 + +=item B<--tenant-name> + +Check plan of a tenant (default: '[All]'). + +=item B<--filter-plan-id> + +Filter plans by plan ID. + +=item B<--filter-plan-name> + +Filter plans by plan name. + +=item B<--filter-environment> + +Filter plan executions by environment name. + +=item B<--since-timeperiod> + +Time period to get plans executions information (in seconds. default: 86400). + +=item B<--only-last-execution> + +Check only last plan execution. + +=item B<--timezone> + +Define timezone for start/end plan execution time (default is 'UTC'). + +=item B<--status-failed> + +Expression to define status failed (default: '%{result} =~ /technical_error|failed|interrupted/i'). + +=item B<--unknown-plan-execution-status> + +Set unknown threshold for last plan execution status. +You can use the following variables: %{status}, %{planName} + +=item B<--warning-plan-execution-status> + +Set warning threshold for last plan execution status. +You can use the following variables: %{status}, %{planName} + +=item B<--critical-plan-execution-status> + +Set critical threshold for last plan execution status. +You can use the following variables: %{status}, %{planName} + +=item B<--warning-plans-detected> + +Thresholds. + +=item B<--critical-plans-detected> + +Thresholds. + +=item B<--warning-plan-executions-detected> + +Thresholds. + +=item B<--critical-plan-executions-detected> + +Thresholds. + +=item B<--warning-plan-executions-failed-prct> + +Thresholds. + +=item B<--critical-plan-executions-failed-prct> + +Thresholds. + +=item B<--warning-plan-execution-last> + +Thresholds. + +=item B<--critical-plan-execution-last> + +Thresholds. + +=item B<--warning-plan-running-duration> + +Thresholds. + +=item B<--critical-plan-running-duration> + +Thresholds. + + +=back + +=cut diff --git a/src/apps/exense/step/restapi/plugin.pm b/src/apps/exense/step/restapi/plugin.pm new file mode 100644 index 000000000..39195b45c --- /dev/null +++ b/src/apps/exense/step/restapi/plugin.pm @@ -0,0 +1,51 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +package apps::exense::step::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} = { + 'list-plans' => 'apps::exense::step::restapi::mode::listplans', + 'list-tenants' => 'apps::exense::step::restapi::mode::listtenants', + 'plans' => 'apps::exense::step::restapi::mode::plans' + }; + + $self->{custom_modes}->{api} = 'apps::exense::step::restapi::custom::api'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Exense Step using Rest API. + +=cut diff --git a/tests/apps/exense/step/restapi/list-plans.robot b/tests/apps/exense/step/restapi/list-plans.robot new file mode 100644 index 000000000..e8a29c720 --- /dev/null +++ b/tests/apps/exense/step/restapi/list-plans.robot @@ -0,0 +1,34 @@ +*** Settings *** + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${cmd} ${CENTREON_PLUGINS} +... --plugin=apps::exense::step::restapi::plugin +... --mode=list-plans +... --hostname=${HOSTNAME} +... --port=${APIPORT} +... --proto=http +... --timeout=15 +... --token=token + + +*** Test Cases *** +list-plans ${tc} + [Tags] apps + ${command} Catenate + ... ${cmd} + ... ${extraoptions} + + Ctn Run Command And Check Result As Strings ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} List plans: [id: 66d1904664e24c6ef10f74d2][name: plop2-test] [id: 669fc855498c2a0322a6a9c5][name: test-plan] [id: 66d180a764e24c6ef10e0155][name: test-plan_Copy] + ... 2 --tenant-name='[All]' List plans: [id: 66d1904664e24c6ef10f74d2][name: plop2-test] [id: 669fc855498c2a0322a6a9c5][name: test-plan] [id: 66d180a764e24c6ef10e0155][name: test-plan_Copy] diff --git a/tests/apps/exense/step/restapi/list-tenants.robot b/tests/apps/exense/step/restapi/list-tenants.robot new file mode 100644 index 000000000..6d4e9cd7c --- /dev/null +++ b/tests/apps/exense/step/restapi/list-tenants.robot @@ -0,0 +1,33 @@ +*** Settings *** + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${cmd} ${CENTREON_PLUGINS} +... --plugin=apps::exense::step::restapi::plugin +... --mode=list-tenants +... --hostname=${HOSTNAME} +... --port=${APIPORT} +... --proto=http +... --timeout=15 +... --token=token + + +*** Test Cases *** +list-tenants ${tc} + [Tags] apps + ${command} Catenate + ... ${cmd} + ... ${extraoptions} + + Ctn Run Command And Check Result As Strings ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} List tenants: [name: [All]][projectId: ][global: 1] [name: Common][projectId: 669fc509498c2a0322a6a5ad][global: 0] [name: test-project][projectId: 66d18fc464e24c6ef10f6f02][global: 0] diff --git a/tests/apps/exense/step/restapi/mockoon.json b/tests/apps/exense/step/restapi/mockoon.json new file mode 100644 index 000000000..ee6c2f7d3 --- /dev/null +++ b/tests/apps/exense/step/restapi/mockoon.json @@ -0,0 +1,289 @@ +{ + "uuid": "07dc5f45-6599-4e24-8ac5-2db84d687b64", + "lastMigration": 33, + "name": "List plans", + "endpointPrefix": "", + "latency": 0, + "port": 3000, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "f715b4db-b117-4da5-8620-0944ab2a4bc0", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "rest/access/login", + "responses": [ + { + "uuid": "0f4b3f64-13c5-4f7d-98f1-d8f8ddce11b4", + "body": "", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "Set-Cookie", + "value": "sessionid=azertyuiop" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "8fc53730-42d0-477a-b9fe-091f77a9124e", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "rest/tenants/current", + "responses": [ + { + "uuid": "cee1d9cb-26ac-45e6-8b84-fc439baa7203", + "body": "{}", + "latency": 0, + "statusCode": 204, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "2b5b4cf5-edd8-4b26-a014-bb2ff19def42", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "rest/table/plans", + "responses": [ + { + "uuid": "4fb9bc48-7d2e-4559-8642-2342c7b5007f", + "body": "{\r\n \"recordsTotal\": 11,\r\n \"recordsFiltered\": 3,\r\n \"data\": [\r\n {\r\n \"customFields\": {\r\n \"versionId\": \"66d1904664e24c6ef10f74d8\"\r\n },\r\n \"attributes\": {\r\n \"name\": \"plop2-test\",\r\n \"project\": \"66d18fc464e24c6ef10f6f02\"\r\n },\r\n \"root\": {\r\n \"_class\": \"TestCase\",\r\n \"customFields\": null,\r\n \"attributes\": {\r\n \"name\": \"plop2-test\"\r\n },\r\n \"dynamicName\": {\r\n \"dynamic\": false,\r\n \"value\": \"\",\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"useDynamicName\": false,\r\n \"description\": null,\r\n \"children\": [],\r\n \"customAttributes\": null,\r\n \"attachments\": null,\r\n \"skipNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"instrumentNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"continueParentNodeExecutionOnError\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"id\": \"66d1904664e24c6ef10f74d1\"\r\n },\r\n \"functions\": null,\r\n \"subPlans\": null,\r\n \"visible\": true,\r\n \"id\": \"66d1904664e24c6ef10f74d2\"\r\n },\r\n {\r\n \"customFields\": {\r\n \"versionId\": \"669fc885498c2a0322a6aa13\"\r\n },\r\n \"attributes\": {\r\n \"name\": \"test-plan\",\r\n \"project\": \"669fc509498c2a0322a6a5ad\"\r\n },\r\n \"root\": {\r\n \"_class\": \"TestCase\",\r\n \"customFields\": null,\r\n \"attributes\": {\r\n \"name\": \"test-plan\"\r\n },\r\n \"dynamicName\": {\r\n \"dynamic\": false,\r\n \"value\": \"\",\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"useDynamicName\": false,\r\n \"description\": null,\r\n \"children\": [\r\n {\r\n \"_class\": \"CallKeyword\",\r\n \"customFields\": null,\r\n \"attributes\": {\r\n \"name\": \"EBC_Put\"\r\n },\r\n \"dynamicName\": {\r\n \"dynamic\": false,\r\n \"value\": \"\",\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"useDynamicName\": false,\r\n \"description\": null,\r\n \"children\": [],\r\n \"customAttributes\": null,\r\n \"attachments\": null,\r\n \"skipNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"instrumentNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"continueParentNodeExecutionOnError\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"remote\": {\r\n \"dynamic\": false,\r\n \"value\": true,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"token\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"function\": {\r\n \"dynamic\": false,\r\n \"value\": \"{\\\"name\\\":{\\\"value\\\":\\\"EBC_Put\\\",\\\"dynamic\\\":false}}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"argument\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"resultMap\": {\r\n \"dynamic\": false,\r\n \"value\": null,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"id\": \"669fc899498c2a0322a6aa27\"\r\n },\r\n {\r\n \"_class\": \"CallKeyword\",\r\n \"customFields\": null,\r\n \"attributes\": {\r\n \"name\": \"EBC_Put\"\r\n },\r\n \"dynamicName\": {\r\n \"dynamic\": false,\r\n \"value\": \"\",\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"useDynamicName\": false,\r\n \"description\": null,\r\n \"children\": [],\r\n \"customAttributes\": null,\r\n \"attachments\": null,\r\n \"skipNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"instrumentNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"continueParentNodeExecutionOnError\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"remote\": {\r\n \"dynamic\": false,\r\n \"value\": true,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"token\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"function\": {\r\n \"dynamic\": false,\r\n \"value\": \"{\\\"name\\\":{\\\"value\\\":\\\"EBC_Put\\\",\\\"dynamic\\\":false}}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"argument\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"resultMap\": {\r\n \"dynamic\": false,\r\n \"value\": null,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"id\": \"669fc89f498c2a0322a6aa42\"\r\n }\r\n ],\r\n \"customAttributes\": null,\r\n \"attachments\": null,\r\n \"skipNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"instrumentNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"continueParentNodeExecutionOnError\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"id\": \"669fc855498c2a0322a6a9c4\"\r\n },\r\n \"functions\": null,\r\n \"subPlans\": null,\r\n \"visible\": true,\r\n \"id\": \"669fc855498c2a0322a6a9c5\"\r\n },\r\n {\r\n \"customFields\": {\r\n \"versionId\": \"66d180a764e24c6ef10e0159\"\r\n },\r\n \"attributes\": {\r\n \"name\": \"test-plan_Copy\",\r\n \"project\": \"669fc509498c2a0322a6a5ad\"\r\n },\r\n \"root\": {\r\n \"_class\": \"TestCase\",\r\n \"customFields\": null,\r\n \"attributes\": {\r\n \"name\": \"test-plan\"\r\n },\r\n \"dynamicName\": {\r\n \"dynamic\": false,\r\n \"value\": \"\",\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"useDynamicName\": false,\r\n \"description\": null,\r\n \"children\": [\r\n {\r\n \"_class\": \"CallKeyword\",\r\n \"customFields\": null,\r\n \"attributes\": {\r\n \"name\": \"EBC_Put\"\r\n },\r\n \"dynamicName\": {\r\n \"dynamic\": false,\r\n \"value\": \"\",\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"useDynamicName\": false,\r\n \"description\": null,\r\n \"children\": [],\r\n \"customAttributes\": null,\r\n \"attachments\": null,\r\n \"skipNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"instrumentNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"continueParentNodeExecutionOnError\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"remote\": {\r\n \"dynamic\": false,\r\n \"value\": true,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"token\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"function\": {\r\n \"dynamic\": false,\r\n \"value\": \"{\\\"name\\\":{\\\"value\\\":\\\"EBC_Put\\\",\\\"dynamic\\\":false}}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"argument\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"resultMap\": {\r\n \"dynamic\": false,\r\n \"value\": null,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"id\": \"66d180a764e24c6ef10e0157\"\r\n },\r\n {\r\n \"_class\": \"CallKeyword\",\r\n \"customFields\": null,\r\n \"attributes\": {\r\n \"name\": \"EBC_Put\"\r\n },\r\n \"dynamicName\": {\r\n \"dynamic\": false,\r\n \"value\": \"\",\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"useDynamicName\": false,\r\n \"description\": null,\r\n \"children\": [],\r\n \"customAttributes\": null,\r\n \"attachments\": null,\r\n \"skipNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"instrumentNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"continueParentNodeExecutionOnError\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"remote\": {\r\n \"dynamic\": false,\r\n \"value\": true,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"token\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"function\": {\r\n \"dynamic\": false,\r\n \"value\": \"{\\\"name\\\":{\\\"value\\\":\\\"EBC_Put\\\",\\\"dynamic\\\":false}}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"argument\": {\r\n \"dynamic\": false,\r\n \"value\": \"{}\",\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"resultMap\": {\r\n \"dynamic\": false,\r\n \"value\": null,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"id\": \"66d180a764e24c6ef10e0158\"\r\n }\r\n ],\r\n \"customAttributes\": null,\r\n \"attachments\": null,\r\n \"skipNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"instrumentNode\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": \"\",\r\n \"expressionType\": null\r\n },\r\n \"continueParentNodeExecutionOnError\": {\r\n \"dynamic\": false,\r\n \"value\": false,\r\n \"expression\": null,\r\n \"expressionType\": null\r\n },\r\n \"id\": \"66d180a764e24c6ef10e0156\"\r\n },\r\n \"functions\": null,\r\n \"subPlans\": null,\r\n \"visible\": true,\r\n \"id\": \"66d180a764e24c6ef10e0155\"\r\n }\r\n ]\r\n}\r\n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "bd3a354c-dd04-4687-a62b-ab55a9405808", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "rest/table/executions", + "responses": [ + { + "uuid": "a7273baa-242f-4dc9-9763-cda27e673764", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [], + "body": "{\n \"recordsTotal\": 11,\n \"recordsFiltered\": 10,\n \"data\": [\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"startTime\": 1725012300007,\n \"endTime\": 1725012300059,\n \"description\": \"test-plan\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": \"66d1866e64e24c6ef10e22ae\",\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d1866e64e24c6ef10e22af\"\n },\n \"executiontTaskParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\",\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"name\": null,\n \"executionsParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d1866e64e24c6ef10e22af\"\n },\n \"assertionPlan\": null,\n \"cronExpression\": \"0 0/5 * * * ?\",\n \"cronExclusions\": null,\n \"active\": true,\n \"id\": \"66d1866e64e24c6ef10e22ae\"\n },\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d1994c64e24c6ef1111cdb\",\n \"name\": \"test-plan\",\n \"executionID\": \"66d1994c64e24c6ef1111cdb\",\n \"artefactID\": \"669fc855498c2a0322a6a9c4\",\n \"executionTime\": 1725012300046,\n \"duration\": 8,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"669fc855498c2a0322a6a9c4\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d1994c64e24c6ef1111d1b\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d1994c64e24c6ef1111cdb\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 2\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 2,\n \"count\": 2,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d1994c64e24c6ef1111d14\"\n },\n \"id\": \"66d1994c64e24c6ef1111cdb\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"startTime\": 1725012000006,\n \"endTime\": 1725012000080,\n \"description\": \"test-plan\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": \"66d1866e64e24c6ef10e22ae\",\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d1866e64e24c6ef10e22af\"\n },\n \"executiontTaskParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\",\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"name\": null,\n \"executionsParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d1866e64e24c6ef10e22af\"\n },\n \"assertionPlan\": null,\n \"cronExpression\": \"0 0/5 * * * ?\",\n \"cronExclusions\": null,\n \"active\": true,\n \"id\": \"66d1866e64e24c6ef10e22ae\"\n },\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d1982064e24c6ef1111bbb\",\n \"name\": \"test-plan\",\n \"executionID\": \"66d1982064e24c6ef1111bbb\",\n \"artefactID\": \"669fc855498c2a0322a6a9c4\",\n \"executionTime\": 1725012000062,\n \"duration\": 9,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"669fc855498c2a0322a6a9c4\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d1982064e24c6ef1111bfb\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d1982064e24c6ef1111bbb\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 2\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 2,\n \"count\": 2,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d1982064e24c6ef1111bf4\"\n },\n \"id\": \"66d1982064e24c6ef1111bbb\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"startTime\": 1725011910722,\n \"endTime\": 1725011910844,\n \"description\": \"test-plan\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": null,\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d197c664e24c6ef1111a59\"\n },\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d197c664e24c6ef1111a5c\",\n \"name\": \"test-plan\",\n \"executionID\": \"66d197c664e24c6ef1111a5c\",\n \"artefactID\": \"669fc855498c2a0322a6a9c4\",\n \"executionTime\": 1725011910818,\n \"duration\": 15,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"669fc855498c2a0322a6a9c4\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d197c664e24c6ef1111aac\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d197c664e24c6ef1111a5c\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 2\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 2,\n \"count\": 2,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d197c664e24c6ef1111aa4\"\n },\n \"id\": \"66d197c664e24c6ef1111a5c\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"startTime\": 1725011910391,\n \"endTime\": 1725011910495,\n \"description\": \"test-plan\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": null,\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d197c664e24c6ef11119a5\"\n },\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d197c664e24c6ef11119a8\",\n \"name\": \"test-plan\",\n \"executionID\": \"66d197c664e24c6ef11119a8\",\n \"artefactID\": \"669fc855498c2a0322a6a9c4\",\n \"executionTime\": 1725011910475,\n \"duration\": 11,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"669fc855498c2a0322a6a9c4\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d197c664e24c6ef11119e6\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d197c664e24c6ef11119a8\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 2\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 2,\n \"count\": 2,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d197c664e24c6ef11119e1\"\n },\n \"id\": \"66d197c664e24c6ef11119a8\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"startTime\": 1725011910013,\n \"endTime\": 1725011910158,\n \"description\": \"test-plan\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": null,\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d197c664e24c6ef11118f7\"\n },\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d197c664e24c6ef11118fa\",\n \"name\": \"test-plan\",\n \"executionID\": \"66d197c664e24c6ef11118fa\",\n \"artefactID\": \"669fc855498c2a0322a6a9c4\",\n \"executionTime\": 1725011910129,\n \"duration\": 19,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"669fc855498c2a0322a6a9c4\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d197c664e24c6ef1111946\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d197c664e24c6ef11118fa\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 2\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 2,\n \"count\": 2,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d197c664e24c6ef1111941\"\n },\n \"id\": \"66d197c664e24c6ef11118fa\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"startTime\": 1725011909109,\n \"endTime\": 1725011909165,\n \"description\": \"test-plan\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"669fc855498c2a0322a6a9c5\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": null,\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"669fc855498c2a0322a6a9c5\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"test-plan\",\n \"id\": \"66d197c564e24c6ef111183e\"\n },\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d197c564e24c6ef1111841\",\n \"name\": \"test-plan\",\n \"executionID\": \"66d197c564e24c6ef1111841\",\n \"artefactID\": \"669fc855498c2a0322a6a9c4\",\n \"executionTime\": 1725011909149,\n \"duration\": 10,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"test-plan\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"669fc855498c2a0322a6a9c4\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d197c564e24c6ef1111873\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d197c564e24c6ef1111841\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 2\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 2,\n \"count\": 2,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d197c564e24c6ef111186e\"\n },\n \"id\": \"66d197c564e24c6ef1111841\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"startTime\": 1725011899876,\n \"endTime\": 1725011899919,\n \"description\": \"plop2-test\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"66d1904664e24c6ef10f74d2\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"66d1904664e24c6ef10f74d2\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": null,\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"66d1904664e24c6ef10f74d2\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"plop2-test\",\n \"id\": \"66d197bb64e24c6ef111177b\"\n },\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d197bb64e24c6ef111177e\",\n \"name\": \"plop2-test\",\n \"executionID\": \"66d197bb64e24c6ef111177e\",\n \"artefactID\": \"66d1904664e24c6ef10f74d1\",\n \"executionTime\": 1725011899912,\n \"duration\": 2,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"plop2-test\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"66d1904664e24c6ef10f74d1\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d197bb64e24c6ef11117ac\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d197bb64e24c6ef111177e\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 0\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 0,\n \"count\": 0,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d197bb64e24c6ef11117a7\"\n },\n \"id\": \"66d197bb64e24c6ef111177e\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"startTime\": 1725011899293,\n \"endTime\": 1725011899345,\n \"description\": \"plop2-test\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"66d1904664e24c6ef10f74d2\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"66d1904664e24c6ef10f74d2\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": null,\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"66d1904664e24c6ef10f74d2\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"plop2-test\",\n \"id\": \"66d197bb64e24c6ef11116e4\"\n },\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d197bb64e24c6ef11116e7\",\n \"name\": \"plop2-test\",\n \"executionID\": \"66d197bb64e24c6ef11116e7\",\n \"artefactID\": \"66d1904664e24c6ef10f74d1\",\n \"executionTime\": 1725011899336,\n \"duration\": 2,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"plop2-test\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"66d1904664e24c6ef10f74d1\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d197bb64e24c6ef1111715\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d197bb64e24c6ef11116e7\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 0\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 0,\n \"count\": 0,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d197bb64e24c6ef1111710\"\n },\n \"id\": \"66d197bb64e24c6ef11116e7\"\n },\n {\n \"customFields\": {\n \"hasTimeSeries\": true\n },\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"startTime\": 1725011898218,\n \"endTime\": 1725011898267,\n \"description\": \"plop2-test\",\n \"executionType\": null,\n \"status\": \"ENDED\",\n \"result\": \"PASSED\",\n \"lifecycleErrors\": null,\n \"planId\": \"66d1904664e24c6ef10f74d2\",\n \"importResult\": {\n \"successful\": true,\n \"planId\": \"66d1904664e24c6ef10f74d2\",\n \"errors\": null\n },\n \"reportExports\": [\n {\n \"status\": \"SUCCESSFUL\",\n \"error\": null,\n \"url\": null\n }\n ],\n \"executionTaskID\": null,\n \"parameters\": [],\n \"executionParameters\": {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"66d18fc464e24c6ef10f6f02\"\n },\n \"customParameters\": {\n \"env\": \"TEST\"\n },\n \"userID\": \"admin\",\n \"artefactFilter\": null,\n \"mode\": \"RUN\",\n \"plan\": null,\n \"repositoryObject\": {\n \"repositoryID\": \"local\",\n \"repositoryParameters\": {\n \"planid\": \"66d1904664e24c6ef10f74d2\"\n }\n },\n \"isolatedExecution\": false,\n \"exports\": [],\n \"description\": \"plop2-test\",\n \"id\": \"66d197ba64e24c6ef1111642\"\n },\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.TestCaseReportNode\",\n \"customFields\": null,\n \"parentID\": \"66d197ba64e24c6ef1111645\",\n \"name\": \"plop2-test\",\n \"executionID\": \"66d197ba64e24c6ef1111645\",\n \"artefactID\": \"66d1904664e24c6ef10f74d1\",\n \"executionTime\": 1725011898259,\n \"duration\": 2,\n \"attachments\": [],\n \"status\": \"PASSED\",\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"TestCase\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"plop2-test\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"id\": \"66d1904664e24c6ef10f74d1\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d197ba64e24c6ef1111673\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": \"statusDistributionForFunctionCalls\",\n \"executionId\": \"66d197ba64e24c6ef1111645\",\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 0\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 0,\n \"count\": 0,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d197ba64e24c6ef111166e\"\n },\n \"id\": \"66d197ba64e24c6ef1111645\"\n },\n {\n \"customFields\": null,\n \"attributes\": {\n \"project\": \"669fc509498c2a0322a6a5ad\"\n },\n \"startTime\": 1725002700000,\n \"endTime\": null,\n \"description\": null,\n \"executionType\": null,\n \"status\": null,\n \"result\": null,\n \"lifecycleErrors\": null,\n \"planId\": null,\n \"importResult\": null,\n \"reportExports\": null,\n \"executionTaskID\": null,\n \"parameters\": null,\n \"executionParameters\": null,\n \"executiontTaskParameters\": null,\n \"rootReportNode\": {\n \"_class\": \"step.artefacts.reports.FailureReportNode\",\n \"customFields\": null,\n \"parentID\": null,\n \"name\": null,\n \"executionID\": null,\n \"artefactID\": null,\n \"executionTime\": 0,\n \"duration\": null,\n \"attachments\": [],\n \"status\": null,\n \"error\": null,\n \"customAttributes\": null,\n \"resolvedArtefact\": {\n \"_class\": \"Failure\",\n \"customFields\": null,\n \"attributes\": {\n \"name\": \"Failure\"\n },\n \"dynamicName\": {\n \"dynamic\": false,\n \"value\": \"\",\n \"expression\": \"\",\n \"expressionType\": null\n },\n \"useDynamicName\": false,\n \"description\": null,\n \"children\": [],\n \"customAttributes\": null,\n \"attachments\": null,\n \"skipNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"instrumentNode\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"continueParentNodeExecutionOnError\": {\n \"dynamic\": false,\n \"value\": false,\n \"expression\": null,\n \"expressionType\": null\n },\n \"message\": null,\n \"stackTrace\": null,\n \"id\": \"66d19a0d64e24c6ef1111e4e\"\n },\n \"orphan\": false,\n \"contributingError\": null,\n \"id\": \"66d19a0d64e24c6ef1111e4d\"\n },\n \"executionSummary\": {\n \"customFields\": null,\n \"viewId\": null,\n \"executionId\": null,\n \"distribution\": {\n \"NORUN\": {\n \"status\": \"NORUN\",\n \"count\": 0\n },\n \"IMPORT_ERROR\": {\n \"status\": \"IMPORT_ERROR\",\n \"count\": 0\n },\n \"TECHNICAL_ERROR\": {\n \"status\": \"TECHNICAL_ERROR\",\n \"count\": 0\n },\n \"INTERRUPTED\": {\n \"status\": \"INTERRUPTED\",\n \"count\": 0\n },\n \"PASSED\": {\n \"status\": \"PASSED\",\n \"count\": 0\n },\n \"RUNNING\": {\n \"status\": \"RUNNING\",\n \"count\": 0\n },\n \"FAILED\": {\n \"status\": \"FAILED\",\n \"count\": 0\n },\n \"VETOED\": {\n \"status\": \"VETOED\",\n \"count\": 0\n },\n \"SKIPPED\": {\n \"status\": \"SKIPPED\",\n \"count\": 0\n }\n },\n \"countForecast\": 0,\n \"count\": 0,\n \"label\": \"Keyword calls: \",\n \"id\": \"66d19a0d64e24c6ef1111e4f\"\n },\n \"id\": \"66d1767864e24c6ef1081bd5\"\n }\n ]\n}\n" + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "cd6f3935-a44d-425b-91c4-96b7ae374a89", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "rest/tenants", + "responses": [ + { + "uuid": "e720fcad-13eb-4f12-b22a-e9e7d50fcf7a", + "body": "[{\"name\":\"[All]\",\"global\":true,\"projectId\":null},{\"name\":\"Common\",\"global\":false,\"projectId\":\"669fc509498c2a0322a6a5ad\"},{\"name\":\"test-project\",\"global\":false,\"projectId\":\"66d18fc464e24c6ef10f6f02\"}]\n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "f715b4db-b117-4da5-8620-0944ab2a4bc0" + }, + { + "type": "route", + "uuid": "8fc53730-42d0-477a-b9fe-091f77a9124e" + }, + { + "type": "route", + "uuid": "2b5b4cf5-edd8-4b26-a014-bb2ff19def42" + }, + { + "type": "route", + "uuid": "bd3a354c-dd04-4687-a62b-ab55a9405808" + }, + { + "type": "route", + "uuid": "cd6f3935-a44d-425b-91c4-96b7ae374a89" + } + ], + "proxyMode": false, + "proxyHost": "", + "proxyRemovePrefix": false, + "tlsOptions": { + "enabled": false, + "type": "CERT", + "pfxPath": "", + "certPath": "", + "keyPath": "", + "caPath": "", + "passphrase": "" + }, + "cors": true, + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Access-Control-Allow-Methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + } + ], + "proxyReqHeaders": [ + { + "key": "", + "value": "" + } + ], + "proxyResHeaders": [ + { + "key": "", + "value": "" + } + ], + "data": [], + "callbacks": [] +} \ No newline at end of file diff --git a/tests/apps/exense/step/restapi/plans.robot b/tests/apps/exense/step/restapi/plans.robot new file mode 100644 index 000000000..9401bcd3c --- /dev/null +++ b/tests/apps/exense/step/restapi/plans.robot @@ -0,0 +1,51 @@ +*** Settings *** + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}mockoon.json + +${cmd} ${CENTREON_PLUGINS} +... --plugin=apps::exense::step::restapi::plugin +... --hostname=${HOSTNAME} +... --mode=plans +... --port=${APIPORT} +... --proto=http +... --timeout=15 +... --token=token +... --since-timeperiod=3153600000 + + +*** Test Cases *** +plans ${tc} + [Tags] apps + ${command} Catenate + ... ${cmd} + ... ${extraoptions} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} OK: All plans are ok \\\| 'plans.detected.count'=3;;;0; 'test-plan#plan.executions.detected.count'=6;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;;0;100 'test-plan#plan.execution.last.seconds'=17822466s;;;0; 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.executions.detected.count'=3;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.execution.last.seconds'=17822867s;;;0; + ... 2 --tenant-name='[All]' --filter-plan-name='test-plan' OK: All plans are ok \\\| 'plans.detected.count'=2;;;0; 'test-plan#plan.executions.detected.count'=6;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;;0;100 'test-plan#plan.execution.last.seconds'=17822466s;;;0; 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 + ... 3 --critical-plan-execution-status='\\\%{status} eq "ended"' --filter-plan-name='test-plan' CRITICAL: plan 'test-plan' execution '66d1994c64e24c6ef1111cdb' \\\\[env:\\\\s*(\\\\w+)\\\\]\\\\s*\\\\[started:\\\\s*([\\\\d-]+T[\\\\d:]+)\\\\s*\\\\(UTC\\\\)\\\\] result: passed, status: ended + ... 4 --unknown-plan-execution-status='\\\%{status} eq "ended"' --filter-plan-name='test-plan' UNKNOWN: plan 'test-plan' execution '66d1994c64e24c6ef1111cdb' \\\\[env:\\\\s*(\\\\w+)\\\\]\\\\s*\\\\[started:\\\\s*([\\\\d-]+T[\\\\d:]+)\\\\s*\\\\(UTC\\\\)\\\\] result: passed, status: ended + ... 5 --warning-plan-execution-status='\\\%{status} eq "ended"' --filter-plan-name='test-plan' WARNING: plan 'test-plan' execution '66d1994c64e24c6ef1111cdb' \\\\[env:\\\\s*(\\\\w+)\\\\]\\\\s*\\\\[started:\\\\s*([\\\\d-]+T[\\\\d:]+)\\\\s*\\\\(UTC\\\\)\\\\] result: passed, status: ended + ... 6 --status-failed='\\\%{result} =~ /technical_error|failed|interrupted/i' OK: All plans are ok \\\| 'plans.detected.count'=3;;;0; 'test-plan#plan.executions.detected.count'=6;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;;0;100 'test-plan#plan.execution.last.seconds'=17822468s;;;0; 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.executions.detected.count'=3;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.execution.last.seconds'=17822869s;;;0; + ... 7 --only-last-execution OK: All plans are ok \\\| 'plans.detected.count'=3;;;0; 'test-plan#plan.executions.detected.count'=1;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;;0;100 'test-plan#plan.execution.last.seconds'=17822469s;;;0; 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.executions.detected.count'=1;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.execution.last.seconds'=17822870s;;;0; + ... 8 --since-timeperiod OK: All plans are ok \\\| 'plans.detected.count'=3;;;0; 'test-plan#plan.executions.detected.count'=0;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;;0;100 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.executions.detected.count'=0;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 + ... 9 --warning-plans-detected=1 WARNING: Number of plans detected: 3 \\\| 'plans.detected.count'=3;0:1;;0; 'test-plan#plan.executions.detected.count'=0;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;;0;100 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.executions.detected.count'=0;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 + ... 10 --critical-plans-detected=2 CRITICAL: Number of plans detected: 3 \\\| 'plans.detected.count'=3;;0:2;0; 'test-plan#plan.executions.detected.count'=0;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;;0;100 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.executions.detected.count'=0;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 + ... 11 --warning-plan-executions-detected=1:1 --filter-plan-name='plop2-test' WARNING: plan 'plop2-test' number of plan executions detected: 3 \\\| 'plans.detected.count'=1;;;0; 'plop2-test#plan.executions.detected.count'=3;1:1;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.execution.last.seconds'=17824109s;;;0; + ... 12 --critical-plan-executions-detected=2:2 --filter-plan-name='plop2-test' CRITICAL: plan 'plop2-test' number of plan executions detected: 3 \\\| 'plans.detected.count'=1;;;0; 'plop2-test#plan.executions.detected.count'=3;;2:2;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.execution.last.seconds'=17824110s;;;0; + ... 13 --warning-plan-executions-failed-prct=1:1 --filter-plan-name='plop2-test' WARNING: plan 'plop2-test' number of failed executions: 0.00 % \\\| 'plans.detected.count'=1;;;0; 'plop2-test#plan.executions.detected.count'=0;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;1:1;;0;100 + ... 14 --critical-plan-executions-failed-prct=2:2 --filter-plan-name='test-plan' CRITICAL: plan 'test-plan' number of failed executions: 0.00 % - plan 'test-plan_Copy' number of failed executions: 0.00 % \\\| 'plans.detected.count'=2;;;0; 'test-plan#plan.executions.detected.count'=0;;;0; 'test-plan#plan.executions.failed.percentage'=0.00%;;2:2;0;100 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;2:2;0;100 + ... 15 --warning-plan-execution-last=1:1 --filter-plan-name='plop2-test' WARNING: plan 'plop2-test' last execution (\\\\d+M)?\\\\s?(\\\\d+w)?\\\\s?(\\\\d+d)?\\\\s?(\\\\d+h)?\\\\s?(\\\\d+m)?\\\\s?(\\\\d+s)? \\\| 'plans.detected.count'=1;;;0; 'plop2-test#plan.executions.detected.count'=3;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.execution.last.seconds'=17824618s;1:1;;0; + ... 16 --critical-plan-execution-last=2:2 --filter-plan-name='plop2-test' CRITICAL: plan 'plop2-test' last execution (\\\\d+M)?\\\\s?(\\\\d+w)?\\\\s?(\\\\d+d)?\\\\s?(\\\\d+h)?\\\\s?(\\\\d+m)?\\\\s?(\\\\d+s)? \\\| 'plans.detected.count'=1;;;0; 'plop2-test#plan.executions.detected.count'=3;;;0; 'plop2-test#plan.executions.failed.percentage'=0.00%;;;0;100 'plop2-test#plan.execution.last.seconds'=17824619s;;2:2;0; + ... 17 --warning-plan-running-duration=0 --filter-plan-name='test-plan_Copy' OK: plan 'test-plan_Copy' number of plan executions detected: 0 - number of failed executions: 0.00 % | 'plans.detected.count'=1;;;0; 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 + ... 18 --critical-plan-running-duration='0' --filter-plan-name='test-plan_Copy' OK: plan 'test-plan_Copy' number of plan executions detected: 0 - number of failed executions: 0.00 % | 'plans.detected.count'=1;;;0; 'test-plan_Copy#plan.executions.detected.count'=0;;;0; 'test-plan_Copy#plan.executions.failed.percentage'=0.00%;;;0;100 diff --git a/tests/resources/spellcheck/stopwords.txt b/tests/resources/spellcheck/stopwords.txt index c6f51cd4f..8c90b812e 100644 --- a/tests/resources/spellcheck/stopwords.txt +++ b/tests/resources/spellcheck/stopwords.txt @@ -73,6 +73,7 @@ Ekara env ESX eth +Exense ext4 fanspeed FCCapacity