Quanta refresh apps::monitoring::quanta::restapi (#5715)

Co-authored-by: Sylvain Cresto <scresto@centreon.com>

Refs: CTOR-1779
This commit is contained in:
Thibault S 2025-09-02 10:54:16 +02:00 committed by GitHub
parent e206ddfaac
commit 8c41fce2d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 2209 additions and 189 deletions

View File

@ -1,5 +1,5 @@
# #
# Copyright 2024 Centreon (http://www.centreon.com/) # Copyright 2025 Centreon (http://www.centreon.com/)
# #
# Centreon is a full-fledged industry-strength solution that meets # Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for # the needs in IT infrastructure and application monitoring for
@ -23,8 +23,10 @@ package apps::monitoring::quanta::restapi::custom::api;
use strict; use strict;
use warnings; use warnings;
use centreon::plugins::http; use centreon::plugins::http;
use centreon::plugins::statefile;
use DateTime; use DateTime;
use JSON::XS; use JSON::XS;
use Digest::MD5 qw(md5_hex);
sub new { sub new {
my ($class, %options) = @_; my ($class, %options) = @_;
@ -42,18 +44,21 @@ sub new {
if (!defined($options{noptions})) { if (!defined($options{noptions})) {
$options{options}->add_options(arguments => { $options{options}->add_options(arguments => {
'hostname:s' => { name => 'hostname' }, 'api-token:s' => { name => 'api_token' },
'url-path:s' => { name => 'url_path' }, 'api-path:s' => { name => 'api_path' },
'port:s' => { name => 'port' }, 'url-path:s' => { redirect => 'api_path' },
'proto:s' => { name => 'proto' }, 'hostname:s' => { name => 'hostname' },
'api-token:s' => { name => 'api_token' }, 'port:s' => { name => 'port' },
'timeout:s' => { name => 'timeout' } 'proto:s' => { name => 'proto' },
'timeout:s' => { name => 'timeout' },
'reload-cache-time:s' => { name => 'reload_cache_time' },
}); });
} }
$options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1);
$self->{output} = $options{output}; $self->{output} = $options{output};
$self->{http} = centreon::plugins::http->new(%options); $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl');
$self->{cache_objects} = centreon::plugins::statefile->new(%options);
return $self; return $self;
} }
@ -72,15 +77,19 @@ sub check_options {
$self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : 'app.quanta.io'; $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : 'app.quanta.io';
$self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443; $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443;
$self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https';
$self->{url_path} = (defined($self->{option_results}->{url_path})) ? $self->{option_results}->{url_path} : '/api'; $self->{api_path} = (defined($self->{option_results}->{api_path})) ? $self->{option_results}->{api_path} : '/api/v1';
$self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10; $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10;
$self->{api_token} = (defined($self->{option_results}->{api_token})) ? $self->{option_results}->{api_token} : ''; $self->{api_token} = (defined($self->{option_results}->{api_token})) ? $self->{option_results}->{api_token} : '';
$self->{reload_cache_time} = (defined($self->{option_results}->{reload_cache_time})) ? $self->{option_results}->{reload_cache_time} : 86400;
$self->{force_cache_reload} = (defined($self->{option_results}->{force_cache_reload})) ? $self->{option_results}->{force_cache_reload} : undef;
if (!defined($self->{api_token}) || $self->{api_token} eq '') { if (!defined($self->{api_token}) || $self->{api_token} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --api-token option."); $self->{output}->add_option_msg(short_msg => "Need to specify --api-token option.");
$self->{output}->option_exit(); $self->{output}->option_exit();
} }
$self->{cache_objects}->check_options(option_results => $self->{option_results});
return 0; return 0;
} }
@ -94,6 +103,7 @@ sub build_options_for_httplib {
$self->{option_results}->{url_path} = $self->{url_path}; $self->{option_results}->{url_path} = $self->{url_path};
$self->{option_results}->{warning_status} = ''; $self->{option_results}->{warning_status} = '';
$self->{option_results}->{critical_status} = ''; $self->{option_results}->{critical_status} = '';
$self->{option_results}->{unknown_status} = '';
} }
sub settings { sub settings {
@ -101,39 +111,113 @@ sub settings {
$self->build_options_for_httplib(); $self->build_options_for_httplib();
$self->{http}->add_header(key => 'Accept', value => 'application/json'); $self->{http}->add_header(key => 'Accept', value => 'application/json');
$self->{http}->add_header(key => 'Content-Type', value => 'application/json');
$self->{http}->add_header(key => 'Authorization', value => 'Token ' . $self->{api_token});
$self->{http}->set_options(%{$self->{option_results}}); $self->{http}->set_options(%{$self->{option_results}});
} }
sub get_api_token { sub get_data_export_api {
my ($self, %options) = @_;
return $self->{api_token};
}
sub request_api {
my ($self, %options) = @_; my ($self, %options) = @_;
$self->settings; $self->settings();
$self->{output}->output_add(long_msg => "Query URL: '" . $self->{proto} . "://" . $self->{hostname} .
$self->{url_path} . $options{url_path} . "'", debug => 1);
my $content = $self->{http}->request(url_path => $self->{url_path} . $options{url_path}); my ($json, $response, $encoded_form_post);
my $decoded;
eval { eval {
$decoded = JSON::XS->new->utf8->decode($content); $encoded_form_post = JSON::XS->new->utf8->encode($options{data});
};
my $endpoint = defined($options{is_rum}) ? '/rum_data_export' : '/data_export';
$response = $self->{http}->request(
method => 'POST',
url_path => $self->{api_path} . $endpoint,
query_form_post => $encoded_form_post
);
if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) {
$self->{output}->add_option_msg(short_msg => "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']");
$self->{output}->output_add(long_msg => $response, debug => 1);
$self->{output}->option_exit();
}
eval {
$json = JSON::XS->new->utf8->decode($response);
}; };
if ($@) { if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); $self->{output}->add_option_msg(short_msg => "Cannot decode JSON response: $response");
$self->{output}->option_exit();
}
if (defined($decoded->{error})) {
$self->{output}->add_option_msg(short_msg => "API returned error '" . $decoded->{error} . "'");
$self->{output}->option_exit(); $self->{output}->option_exit();
};
return $json;
}
sub get_configuration_api {
my ($self, %options) = @_;
$self->settings();
my ($json, $response);
my $get_param = [];
if (defined($options{get_param})) {
push @$get_param, $options{get_param};
} }
return $decoded; $response = $self->{http}->request(
method => 'GET',
url_path => $self->{api_path} . $options{endpoint},
get_param => $get_param
);
if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) {
$self->{output}->add_option_msg(short_msg => "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']");
$self->{output}->output_add(long_msg => $response, debug => 1);
$self->{output}->option_exit();
}
eval {
$json = JSON::XS->new->utf8->decode($response);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode JSON response: $response");
$self->{output}->option_exit();
};
return $json;
}
# Available calls depending on type:
# journeys: /sites/{site_id}/user_journeys to list all journeys for a site
# journey: /sites/{site_id}/user_journeys/{journey_id} unused
# interactions: /sites/{site_id}/user_journeys/{journey_id}/interactions to list all interactions for a journey
# interaction: /sites/{site_id}/user_journeys/{journey_id}/interactions/{interaction_id} to get details of a specific interaction
sub list_objects {
my ($self, %options) = @_;
my $endpoint = '/sites/' . $options{site_id};
if ($options{type} =~ /journey|interaction/) {
$endpoint .= '/user_journeys/';
if ($options{type} eq 'journey') {
$endpoint .= $options{journey_id};
}
if ($options{type} =~ /interaction/) {
$endpoint .= $options{journey_id} . '/interactions';
if ($options{type} eq 'interaction') {
$endpoint .= '/' . $options{interaction_id};
}
}
}
# Results are cached to avoid too many API calls
my $has_cache_file = $self->{cache_objects}->read(statefile => 'quanta_cache_' . md5_hex($options{site_id}) . md5_hex($endpoint));
my $response = $self->{cache_objects}->get(name => 'response');
my $freshness = defined($self->{cache_objects}->get(name => 'update_time')) ? time() - $self->{cache_objects}->get(name => 'update_time') : undef;
if ( $has_cache_file == 0 || !defined($response) || (defined($freshness)) && ($freshness > $self->{reload_cache_time}) ) {
$response = $self->get_configuration_api(endpoint => $endpoint);
}
$self->{cache_objects}->write(data => {
update_time => time(),
response => $response
});
return $response;
} }
1; 1;
@ -142,15 +226,15 @@ __END__
=head1 NAME =head1 NAME
Quanta Rest API Quanta by Centreon Rest API
=head1 SYNOPSIS =head1 SYNOPSIS
Quanta Rest API custom mode Quanta by Centreon Rest API custom mode
=head1 REST API OPTIONS =head1 REST API OPTIONS
Quanta Rest API Quanta by Centreon Rest API
=over 8 =over 8
@ -166,9 +250,9 @@ API port (default: 443)
Specify https if needed (default: 'https') Specify https if needed (default: 'https')
=item B<--url-path> =item B<--api-path>
API URL path (default: '/api') API URL path (default: '/api/v1')
=item B<--api-token> =item B<--api-token>

View File

@ -0,0 +1,116 @@
#
# 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::monitoring::quanta::restapi::mode::listuserjourneys;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'site-id:s' => { name => 'site_id', default => '' },
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if ($self->{option_results}->{site_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --site-id option.");
$self->{output}->option_exit();
}
$self->{site_id} = $self->{option_results}->{site_id};
}
sub manage_selection {
my ($self, %options) = @_;
$self->{results} = $options{custom}->list_objects(
type => 'journeys',
site_id => $self->{site_id}
);
}
sub run {
my ($self, %options) = @_;
$self->manage_selection(%options);
foreach my $user_journey (@{$self->{results}->{user_journeys}}){
$self->{output}->output_add(
long_msg => sprintf("[name: %s][id: %s]",
$user_journey->{name},
$user_journey->{id},
)
);
}
$self->{output}->output_add(
severity => 'OK',
short_msg => 'User journeys:'
);
$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', 'id']);
}
sub disco_show {
my ($self, %options) = @_;
$self->manage_selection(%options);
foreach my $user_journey (@{$self->{results}->{user_journeys}}){
$self->{output}->add_disco_entry(
id => $user_journey->{id},
name => $user_journey->{name}
);
}
}
1;
__END__
=head1 MODE
List Quanta by Centreon user journeys for a given site.
=over 8
=item B<--site-id>
Set ID of the site (mandatory option).
=back
=cut

View File

@ -0,0 +1,274 @@
#
# 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::monitoring::quanta::restapi::mode::rum;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'rum', type => 1, message_multiple => 'All RUM counters are OK', cb_prefix_output => 'prefix_output', skipped_code => { -10 => 1 } }
];
$self->{maps_counters}->{rum} = [
{ label => 'sessions', nlabel => 'sessions.count', set => {
key_values => [ { name => 'sessions' }, { name => 'display' } ],
output_template => 'sessions: %.d',
perfdatas => [
{ value => 'sessions', template => '%.d',
min => 0, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'page-views', nlabel => 'pageviews.count', set => {
key_values => [ { name => 'page_views' }, { name => 'display' } ],
output_template => 'page views: %.d',
perfdatas => [
{ value => 'page_views', template => '%.d',
min => 0, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'bounce-rate', nlabel => 'bounce.rate.percentage', set => {
key_values => [ { name => 'bounces' }, { name => 'display' } ],
output_template => 'bounce rate: %.d%%',
perfdatas => [
{ value => 'bounces', template => '%.d',
min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'ttfb', nlabel => 'ttfb.milliseconds', set => {
key_values => [ { name => 'backend_time' }, { name => 'display' } ],
output_template => 'ttfb: %.3fms',
perfdatas => [
{ value => 'backend_time', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'onload', nlabel => 'onload.time.milliseconds', set => {
key_values => [ { name => 'frontend_time' }, { name => 'display' } ],
output_template => 'onload time: %.2fms',
perfdatas => [
{ value => 'frontend_time', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'interaction-next-paint', nlabel => 'nextpaint.interaction.time.milliseconds', set => {
key_values => [ { name => 'interaction_to_next_paint' }, { name => 'display' } ],
output_template => 'interaction to next paint: %.2fms',
perfdatas => [
{ value => 'interaction_to_next_paint', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'speed-index', nlabel => 'speedindex.time.milliseconds', set => {
key_values => [ { name => 'speed_index' }, { name => 'display' } ],
output_template => 'speed index: %.2fms',
perfdatas => [
{ value => 'speed_index', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
];
}
sub prefix_output {
my ($self, %options) = @_;
return $self->{perspective} . ' ' . $options{instance_value}->{display} . ': ';
}
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 => {
"limit-results:s" => { name => 'limit_results', default => '10' },
"perspective:s" => { name => 'perspective', default => 'all' },
"timeframe:s" => { name => 'timeframe', default => '1800' },
"site-id:s" => { name => 'site_id', default => '' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
$self->{$_} = $self->{option_results}->{$_} foreach qw/limit_results perspective site_id timeframe/;
if ($self->{site_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --site-id option.");
$self->{output}->option_exit();
}
if ($self->{perspective} !~ m/all|url|browser|country|city|os/) {
$self->{output}->add_option_msg(short_msg => 'Unknown perspective set in "--perspective" option.');
$self->{output}->option_exit();
}
}
sub manage_selection {
my ($self, %options) = @_;
my $rum_metrics = [
{ name => 'sessions' },
{ name => 'page_views' },
{ name => 'bounces' },
{ name => 'speed_index', is_time => 1 },
{ name => 'frontend_time', is_time => 1 },
{ name => 'backend_time', is_time => 1 },
{ name => 'interaction_to_next_paint', is_time => 1 }
];
my $rum_payload;
my $rum_aggregations = [ 'mean' ];
$rum_payload->{namespace} = 'rum';
$rum_payload->{index} = $self->{perspective};
# numifying is required with rum API for INT types
$rum_payload->{tenant_id} = int $self->{site_id};
$rum_payload->{limit} = int $self->{limit_results};
$rum_payload->{point_period} = int $self->{timeframe};
$rum_payload->{range} = int $self->{timeframe};
foreach my $metric (@$rum_metrics) {
foreach (@$rum_aggregations) {
push @{$rum_payload->{metrics_filter}->{$metric->{name}}->{aggregations}}, $_;
}
}
my $results = $options{custom}->get_data_export_api(data => $rum_payload, is_rum => 1);
foreach my $metric (@$rum_metrics) {
my $dimension = $self->{perspective};
foreach my $result (@{$results->{results}}) {
if (scalar(keys %{$result->{dimensions}}) > 0) {
foreach (sort keys %{$result->{dimensions}}) {
$dimension = $result->{dimensions}->{$_} if $result->{dimensions}->{$_};
}
}
$self->{rum}->{$dimension}->{display} = $dimension ne 'all' ? $dimension : 'pages';
if (defined($metric->{is_time})) {
$self->{rum}->{$dimension}->{$metric->{name}} = $result->{total}->{$metric->{name}}->{mean};
} else {
$self->{rum}->{$dimension}->{$metric->{name}} = $result->{total}->{$metric->{name}}->{count} if (defined($result->{total}->{$metric->{name}}->{count}));
}
}
}
}
1;
__END__
=head1 MODE
Check Quanta by Centreon RUM metrics for a given site.
=over 8
=item B<--site-id>
Set ID of the site (mandatory option).
=item B<--timeframe>
Set timeframe in seconds (default: 1800).
=item B<--perspective>
Set the perspective in which the data will be applied.
Can be: 'all', 'url', 'browser', 'country', 'city', 'os' (default: 'all').
=item B<--limit-results>
To be used with --perspective. Limit the number of results to be fetched (number of different URLs, browsers, etc...).
(default: 10).
=item B<--warning-sessions>
Warning threshold for sessions.
=item B<--critical-sessions>
Critical threshold for sessions.
=item B<--warning-page-views>
Warning threshold for page views.
=item B<--critical-page-views>
Critical threshold for page views.
=item B<--warning-bounce-rate>
Warning threshold for bounce rate.
=item B<--critical-bounce-rate>
Critical threshold for bounce rate.
=item B<--warning-ttfb>
Warning threshold for time to first byte (in ms).
=item B<--critical-ttfb>
Critical threshold for time to first byte (in ms).
=item B<--warning-onload>
Warning threshold for C<onload> time (in ms).
=item B<--critical-onload>
Critical threshold for C<onload> time (in ms).
=item B<--warning-interaction-next-paint>
Warning threshold for time to interaction next paint (in ms).
=item B<--critical-interaction-next-paint>
Critical threshold for time to interaction next paint (in ms).
=item B<--warning-speed-index>
Warning threshold for speed index.
=item B<--critical-speed-index>
Critical threshold for speed index.
=back
=cut

View File

@ -0,0 +1,185 @@
#
# 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::monitoring::quanta::restapi::mode::siteoverview;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'sites', type => 1, message_multiple => 'All sites are OK', cb_prefix_output => 'prefix_output' }
];
$self->{maps_counters}->{sites} = [
{ label => 'performance-score', nlabel => 'performance.score', set => {
key_values => [ { name => 'performance_score' }, { name => 'display' } ],
output_template => 'performance score: %d',
perfdatas => [
{ value => 'performance_score', template => '%d',
min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'digital-sobriety-score', nlabel => 'digitalsobriety.score', set => {
key_values => [ { name => 'digital_sobriety_score' }, { name => 'display' } ],
output_template => 'digital sobriety score: %d',
perfdatas => [
{ value => 'digital_sobriety_score', template => '%d',
min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'eco-design-score', nlabel => 'ecodesign.score', set => {
key_values => [ { name => 'eco_design_score' }, { name => 'display' } ],
output_template => 'eco design score: %d',
perfdatas => [
{ value => 'eco_design_score', template => '%d',
min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'carbon-footprint', nlabel => 'perclick.carbon.footprint.gramm', set => {
key_values => [ { name => 'carbon_footprint_per_click' }, { name => 'display' } ],
output_template => 'carbon footprint per click: %.2fg',
perfdatas => [
{ value => 'carbon_footprint_per_click', template => '%.2f',
min => 0, unit => 'g', label_extra_instance => 1, instance_use => 'display' },
],
}
}
];
}
sub prefix_output {
my ($self, %options) = @_;
return "Site '" . $options{instance_value}->{display} . "' ";
}
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 => {
"site-id:s" => { name => 'site_id', default => '' },
"timeframe:s" => { name => 'timeframe', default => '3600' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
$self->{$_} = $self->{option_results}->{$_} foreach qw/site_id timeframe/;
if ($self->{site_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --site-id option.");
$self->{output}->option_exit();
}
}
sub manage_selection {
my ($self, %options) = @_;
my $site_metrics = [
'performance_score',
'digital_sobriety_score',
'eco_design_score',
'carbon_footprint_per_click'
];
my ($site_payload, $resources_payload);
$site_payload->{type} = 'site';
$site_payload->{id} = $self->{site_id};
foreach (@$site_metrics) {
push @{$site_payload->{metrics}}, { name => $_};
}
push @{$resources_payload->{resources}}, $site_payload;
$resources_payload->{range} = $self->{timeframe};
my $results = $options{custom}->get_data_export_api(data => $resources_payload);
foreach my $site (@{$results->{resources}}) {
$self->{sites}->{$site->{id}}->{display} = $site->{name};
foreach my $metric (@{$site->{metrics}}) {
$self->{sites}->{$site->{id}}->{$metric->{name}} = $metric->{values}[0]->{average};
}
}
}
1;
__END__
=head1 MODE
Check Quanta by Centreon overview performance metrics for a given site.
=over 8
=item B<--site-id>
Set ID of the site (mandatory option).
=item B<--timeframe>
Set timeframe in seconds (default: 3600).
=item B<--warning-performance-score>
Warning threshold for performance score.
=item B<--critical-performance-score>
Critical threshold for performance score.
=item B<--warning-digital-sobriety-score>
Warning threshold for digital sobriety score.
=item B<--critical-digital-sobriety-score>
Critical threshold for digital sobriety score.
=item B<--warning-eco-design-score>
Warning threshold for C<eco design> score.
=item B<--critical-eco-design-score>
Critical threshold for C<eco design> score.
=item B<--warning-carbon-footprint>
Warning threshold for carbon footprint.
=item B<--critical-carbon-footprint>
Critical threshold for carbon footprint.
=back
=cut

View File

@ -0,0 +1,243 @@
#
# 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::monitoring::quanta::restapi::mode::userjourneyincidents;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub prefix_output {
my ($self, %options) = @_;
return "Incident for interaction '" . $options{instance_value}->{interaction_name} . "' ";
}
sub prefix_output_global {
my ($self, %options) = @_;
return 'Incidents ';
}
sub custom_duration_output {
my ($self, %options) = @_;
if ($self->{result_values}->{status} =~ 'Open') {
return sprintf(
'start time: %s, duration: %s',
$self->{result_values}->{start_time},
centreon::plugins::misc::change_seconds(value => $self->{result_values}->{duration})
);
} else {
return sprintf(
'start time: %s, end time: %s, duration: %s',
$self->{result_values}->{start_time},
$self->{result_values}->{end_time},
centreon::plugins::misc::change_seconds(value => $self->{result_values}->{duration})
);
}
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_output_global' },
{ name => 'incidents', type => 1, message_multiple => 'No ongoing incident', cb_prefix_output => 'prefix_output' }
];
$self->{maps_counters}->{global} = [
{ label => 'incidents-total', nlabel => 'quanta.incidents.total.count', set => {
key_values => [ { name => 'total' } ],
output_template => 'total: %s',
perfdatas => [ { template => '%d', min => 0 } ]
}
}
];
$self->{maps_counters}->{incidents} = [
{ label => 'incident-status',
type => 2,
warning_default => '',
critical_default => '%{status} =~ "open"',
set => {
key_values => [ { name => 'status' }, { name => 'display' }, { name => 'interaction_name' } ],
output_template => 'status: %s',
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'incident-type',
type => 2,
warning_default => '',
critical_default => '',
set => {
key_values => [ { name => 'type' }, { name => 'display' }, { name => 'interaction_name' } ],
output_template => 'type: %s',
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'incident-duration', set => {
key_values => [ { name => 'duration' }, { name => 'start_time' }, { name => 'end_time' }, { name => 'status'} ],
closure_custom_output => $self->can('custom_duration_output'),
closure_custom_perfdata => sub { return 0; }
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
"ignore-closed" => { name => 'ignore_closed' },
"journey-id:s" => { name => 'journey_id', default => '' },
"site-id:s" => { name => 'site_id', default => '' },
"timeframe:s" => { name => 'timeframe', default => '300' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
$self->{$_} = $self->{option_results}->{$_} foreach qw/journey_id site_id timeframe/;
if ($self->{site_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --site-id option.");
$self->{output}->option_exit();
}
if ($self->{journey_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --journey-id option.");
$self->{output}->option_exit();
}
}
sub manage_selection {
my ($self, %options) = @_;
my $results = $options{custom}->get_configuration_api(
endpoint => '/sites/' . $self->{site_id} . '/user_journeys/' . $self->{journey_id} . '/incidents',
get_param => 'range=' . $self->{timeframe}
);
$self->{global}->{total} = 0;
foreach my $incident (@{$results->{incidents}}) {
my $kind = $incident->{kind} =~ s/_/ /gr;
my $end_time = $incident->{end_clock} ? $incident->{end_clock} : time();
next if defined($self->{option_results}->{ignore_closed}) && $incident->{end_clock};
my $interaction = $options{custom}->list_objects(type => 'interaction', site_id => $self->{site_id}, journey_id => $self->{journey_id}, interaction_id => $incident->{interaction_id});
$self->{incidents}->{$incident->{id}} = {
display => $incident->{id},
type => $kind,
interaction_name => $interaction->{interaction}->{name},
start_time => POSIX::strftime('%d-%m-%Y %H:%M:%S %Z', localtime($incident->{start_clock})),
end_time => POSIX::strftime('%d-%m-%Y %H:%M:%S %Z', localtime($end_time)),
duration => $end_time - $incident->{start_clock}
};
$self->{incidents}->{$incident->{id}}->{status} = $incident->{end_clock} ? 'closed' : 'open';
$self->{global}->{total}++;
}
}
1;
__END__
=head1 MODE
Check Quanta by Centreon incidents for a given user journey.
=over 8
=item B<--site-id>
Set ID of the site (mandatory option).
=item B<--journey-id>
Set ID of the user journey (mandatory option).
=item B<--timeframe>
Set timeframe in seconds (default: 300).
=item B<--ignore-closed>
Ignore closed incidents.
=item B<--warning-incidents-total>
Warning threshold for incidents total.
=item B<--critical-incidents-total>
Critical threshold for incidents total.
=item B<--warning-incident-status>
Define the conditions to match for the status to be B<WARNING>.
You can use the following variables: C<%{status}>.
Example: C<--warning-incident-status='%{status} =~ /open/i'>
=item B<--critical-incident-status>
Define the conditions to match for the status to be B<CRITICAL>.
You can use the following variables: C<%{status}>.
Default: C<--critical-incident-status='%{status} =~ /open/i'>
=item B<--warning-incident-type>
Define the conditions to match for the incident type to be B<WARNING>.
You can use the following variables: C<%{type}>.
Example: C<--warning-incident-type='%{type} =~ /error/i'>
=item B<--critical-incident-type>
Define the conditions to match for the incident type to be B<CRITICAL>.
You can use the following variables: C<%{type}>.
Example: C<--critical-incident-type='%{type} =~ /error/i'>
=item B<--warning-incident-duration>
Warning threshold for incident duration (in seconds).
=item B<--critical-incident-duration>
Critical threshold for incident duration (in seconds).
=back
=cut

View File

@ -0,0 +1,304 @@
#
# 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::monitoring::quanta::restapi::mode::userjourneystatistics;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'metrics', type => 1, message_multiple => 'User journey is OK', cb_prefix_output => 'prefix_output', skipped_code => { -10 => 1 } }
];
$self->{maps_counters}->{metrics} = [
{ label => 'journey-performance-score', nlabel => 'journey.performance.score', set => {
key_values => [ { name => 'avg_lh_performance_score' }, { name => 'display' } ],
output_template => 'journey performance score: %d',
perfdatas => [
{ value => 'avg_lh_performance_score', template => '%d',
min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'journey-hero-time', nlabel => 'journey.herotime.milliseconds', set => {
key_values => [ { name => 'total_hero_time' }, { name => 'display' } ],
output_template => 'journey hero time: %.2fms',
perfdatas => [
{ value => 'total_hero_time', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'journey-speed-index', nlabel => 'journey.speedindex.time.milliseconds', set => {
key_values => [ { name => 'total_speed_index' }, { name => 'display' } ],
output_template => 'journey speed index: %.2fms',
perfdatas => [
{ value => 'total_speed_index', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'journey-ttfb', nlabel => 'journey.ttfb.milliseconds', set => {
key_values => [ { name => 'total_net_request_ttfb' }, { name => 'display' } ],
output_template => 'journey ttfb: %.2fms',
perfdatas => [
{ value => 'total_net_request_ttfb', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'interaction-performance-score', nlabel => 'interaction.performance.score', set => {
key_values => [ { name => 'lh_performance_score' }, { name => 'display' } ],
output_template => 'performance score: %d',
perfdatas => [
{ value => 'lh_performance_score', template => '%d',
min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'interaction-hero-time', nlabel => 'herotime.milliseconds', set => {
key_values => [ { name => 'hero_time' }, { name => 'display' } ],
output_template => 'hero time: %.2fms',
perfdatas => [
{ value => 'hero_time', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'interaction-speed-index', nlabel => 'speedindex.time.milliseconds', set => {
key_values => [ { name => 'speed_index' }, { name => 'display' } ],
output_template => 'speed index: %.2fms',
perfdatas => [
{ value => 'speed_index', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'interaction-ttfb', nlabel => 'ttfb.milliseconds', set => {
key_values => [ { name => 'net_request_ttfb' }, { name => 'display' } ],
output_template => 'ttfb: %.2fms',
perfdatas => [
{ value => 'net_request_ttfb', template => '%.2f',
min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' },
],
}
}
];
}
sub prefix_output {
my ($self, %options) = @_;
return $options{instance_value}->{type} . ' "' . $options{instance_value}->{name} . '" ';
}
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 => {
"show-interactions" => { name => 'add_interactions' },
"journey-id:s" => { name => 'journey_id', default => '' },
"site-id:s" => { name => 'site_id', default => '' },
"timeframe:s" => { name => 'timeframe', default => '300' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
$self->{$_} = $self->{option_results}->{$_} foreach qw/journey_id site_id timeframe/;
if ($self->{journey_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --journey-id option.");
$self->{output}->option_exit();
}
if ($self->{site_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --site-id option.");
$self->{output}->option_exit();
}
}
sub manage_selection {
my ($self, %options) = @_;
my $journey_metrics = [
'avg_lh_performance_score',
'total_hero_time',
'total_net_request_ttfb',
'total_speed_index'
];
if (defined($self->{option_results}->{add_interactions})) {
my $interaction_metrics = [
'lh_performance_score',
'speed_index',
'hero_time',
'net_request_ttfb'
];
my $interactions_list = $options{custom}->list_objects(type => 'interactions', site_id => $self->{site_id}, journey_id => $self->{journey_id});
foreach my $interaction (@{$interactions_list->{interactions}}) {
my $interaction_payload;
$interaction_payload->{type} = 'interaction';
$interaction_payload->{id} = $interaction->{id};
foreach (@$interaction_metrics) {
push @{$interaction_payload->{metrics}}, { name => $_};
}
push @{$self->{resources_payload}->{resources}}, $interaction_payload;
};
}
my $journey_payload;
$journey_payload->{type} = 'journey';
$journey_payload->{id} = $self->{journey_id};
foreach (@$journey_metrics) {
push @{$journey_payload->{metrics}}, { name => $_ };
}
push @{$self->{resources_payload}->{resources}}, $journey_payload;
$self->{resources_payload}->{range} = $self->{timeframe};
my $results = $options{custom}->get_data_export_api(data => $self->{resources_payload});
foreach my $result (@{$results->{resources}}) {
$self->{metrics}->{$result->{id}}->{display} = $result->{type} . "_" . $result->{name};
$self->{metrics}->{$result->{id}}->{name} = $result->{name};
$self->{metrics}->{$result->{id}}->{type} = $result->{type};
foreach my $metric (@{$result->{metrics}}) {
my $timestamp = 0;
foreach my $timeserie (@{$metric->{values}}) {
if ($timeserie->{timestamp} > $timestamp) {
$self->{metrics}->{$result->{id}}->{$metric->{name}} = $timeserie->{average};
$timestamp = $timeserie->{timestamp};
}
}
}
}
}
1;
__END__
=head1 MODE
Check Quanta by Centreon statistics for a user journey.
=over 8
=item B<--site-id>
Set ID of the site (mandatory option).
=item B<--journey-id>
Set ID of the user journey (mandatory option).
=item B<--show-interactions>
Also monitor interactions (scenario's steps) of a user journey.
=item B<--timeframe>
Set timeframe in seconds (default: 300).
=item B<--warning-journey-performance-score>
Warning threshold for journey performance score.
=item B<--critical-journey-performance-score>
Critical threshold for journey performance score.
=item B<--warning-journey-hero-time>
Warning threshold for journey hero time (in ms).
=item B<--critical-journey-hero-time>
Critical threshold for journey hero time (in ms).
=item B<--warning-journey-speed-index>
Warning threshold for journey speed index (in ms).
=item B<--critical-journey-speed-index>
Critical threshold for journey speed index (in ms).
=item B<--warning-journey-ttfb>
Warning threshold for journey time to first byte (in ms).
=item B<--critical-journey-ttfb>
Critical threshold for journey time to first byte (in ms).
=back
=head2 Interaction related metrics
The following parameters take effect only if --show-interactions is set
=over 8
=item B<--warning-interaction-performance-score>
Warning threshold for interaction performance score.
=item B<--critical-interaction-performance-score>
Critical threshold for interaction performance score.
=item B<--warning-interaction-hero-time>
Warning threshold for interaction hero time (in ms).
=item B<--critical-interaction-hero-time>
Critical threshold for interaction hero time (in ms).
=item B<--warning-interaction-speed-index>
Warning threshold for interaction speed index (in ms).
=item B<--critical-interaction-speed-index>
Critical threshold for interaction speed index (in ms).
=item B<--warning-interaction-ttfb>
Warning threshold for interaction time to first byte (in ms).
=item B<--critical-interaction-ttfb>
Critical threshold for time to first byte (in ms).
=back
=cut

View File

@ -1,147 +0,0 @@
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package apps::monitoring::quanta::restapi::mode::webscenariosavailability;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 1, cb_prefix_output => 'prefix_output' }
];
$self->{maps_counters}->{global} = [
{ label => 'total-response-time', nlabel => 'total.response.time.seconds', set => {
key_values => [ { name => 'response_time' }, { name => 'display' } ],
output_template => 'Total Response Time: %.3fs',
perfdatas => [
{ value => 'response_time', template => '%.3f',
min => 0, unit => 's', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'availability', nlabel => 'availability.percentage', set => {
key_values => [ { name => 'availability' }, { name => 'display' } ],
output_template => 'Availability: %.2f%%',
perfdatas => [
{ value => 'availability', template => '%s',
min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'display' },
],
}
},
{ label => 'step-response-time', nlabel => 'step.response.time.seconds', set => {
key_values => [ { name => 'avg_step_response_time' }, { name => 'display' } ],
output_template => 'Step Average Response Time: %.3fs',
perfdatas => [
{ value => 'avg_step_response_time', template => '%.3f',
min => 0, unit => 's', label_extra_instance => 1, instance_use => 'display' },
],
}
},
];
}
sub prefix_output {
my ($self, %options) = @_;
return "Scenario '" . $options{instance_value}->{display} . "' ";
}
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 => {
"scenario-id:s" => { name => 'scenario_id' },
"timeframe:s" => { name => 'timeframe', default => 900 },
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if (!defined($self->{option_results}->{scenario_id}) || $self->{option_results}->{scenario_id} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify --scenario-id option.");
$self->{output}->option_exit();
}
}
sub manage_selection {
my ($self, %options) = @_;
$self->{global} = {};
my $from = DateTime->now->subtract(seconds => $self->{option_results}->{timeframe});
my $to = DateTime->now;
my $from_epoch = $from->epoch;
my $to_epoch = $to->epoch;
my $url = '/partners/report/' . $options{custom}->get_api_token .
'?scenario=' . $self->{option_results}->{scenario_id} .
'&from=' . $from_epoch . '&to=' . $to_epoch;
my $results = $options{custom}->request_api(url_path => $url);
$self->{global}->{$results->{site}->{id}}->{display} = $results->{site}->{scenario};
$self->{global}->{$results->{site}->{id}}->{availability} = $results->{site}->{availability} * 100;
$self->{global}->{$results->{site}->{id}}->{avg_step_response_time} = $results->{site}->{avg_step_response_time};
foreach my $response (@{$results->{site}->{scenario_response_times}}) {
$self->{global}->{$results->{site}->{id}}->{response_time} += $response->{value};
}
$self->{global}->{$results->{site}->{id}}->{response_time} /= scalar(@{$results->{site}->{scenario_response_times}})
if (scalar(@{$results->{site}->{scenario_response_times}}) > 0);
}
1;
__END__
=head1 MODE
Check web scenario availability metrics.
(Data are delayed by a minimum of 3 hours)
=over 8
=item B<--scenario-id>
Set ID of the scenario (mandatory option).
=item B<--timeframe>
Set timeframe in seconds (default: 900).
=item B<--warning-*> B<--critical-*>
Can be: 'total-response-time', 'availability',
'step-response-time'.
=back
=cut

View File

@ -1,5 +1,5 @@
# #
# Copyright 2024 Centreon (http://www.centreon.com/) # Copyright 2025 Centreon (http://www.centreon.com/)
# #
# Centreon is a full-fledged industry-strength solution that meets # Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for # the needs in IT infrastructure and application monitoring for
@ -30,11 +30,15 @@ sub new {
bless $self, $class; bless $self, $class;
$self->{version} = '0.1'; $self->{version} = '0.1';
%{$self->{modes}} = ( $self->{modes} = {
'web-scenarios-availability' => 'apps::monitoring::quanta::restapi::mode::webscenariosavailability', 'list-user-journeys' => 'apps::monitoring::quanta::restapi::mode::listuserjourneys',
); 'rum' => 'apps::monitoring::quanta::restapi::mode::rum',
'site-overview' => 'apps::monitoring::quanta::restapi::mode::siteoverview',
'user-journey-incidents' => 'apps::monitoring::quanta::restapi::mode::userjourneyincidents',
'user-journey-statistics' => 'apps::monitoring::quanta::restapi::mode::userjourneystatistics'
};
$self->{custom_modes}{api} = 'apps::monitoring::quanta::restapi::custom::api'; $self->{custom_modes}->{api} = 'apps::monitoring::quanta::restapi::custom::api';
return $self; return $self;
} }
@ -44,6 +48,6 @@ __END__
=head1 PLUGIN DESCRIPTION =head1 PLUGIN DESCRIPTION
Check Quanta.io application probes results. Check Quanta application probes results.
=cut =cut

View File

@ -0,0 +1,33 @@
*** Settings ***
Documentation Quanta
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${CMD} ${CENTREON_PLUGINS}
... --plugin=apps::monitoring::quanta::restapi::plugin
... --hostname=${HOSTNAME}
... --api-token=PaSsWoRd
... --site-id=10
... --proto=http
... --port=${APIPORT}
*** Test Cases ***
ListUserJourneys ${tc}
[Tags] quanta api
${command} Catenate
... ${CMD}
... --mode=list-user-journeys
... ${extra_options}
Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp}
Examples: tc extraoptions expected_regexp --
... 1 ${EMPTY} ^User journeys:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,54 @@
*** Settings ***
Documentation Quanta
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${CMD} ${CENTREON_PLUGINS}
... --plugin=apps::monitoring::quanta::restapi::plugin
... --hostname=${HOSTNAME}
... --api-token=PaSsWoRd
... --site-id=10
... --proto=http
... --port=${APIPORT}
*** Test Cases ***
Rum ${tc}
[Tags] quanta api
${command} Catenate
... ${CMD}
... --mode=rum
... ${extra_options}
Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp}
Examples: tc extraoptions expected_regexp --
... 1 ${EMPTY} OK: all pages: sessions: 34, page views: 62, bounce rate: 43%, ttfb: 1617.714ms, onload time: 5494.82ms, interaction to next paint: 46.70ms, speed index: 2536.67ms \\\\| 'pages#sessions.count'=34;;;0; 'pages#pageviews.count'=62;;;0; 'pages#bounce.rate.percentage'=43%;;;0;100 'pages#ttfb.milliseconds'=1617.71ms;;;0; 'pages#onload.time.milliseconds'=5494.82ms;;;0; 'pages#nextpaint.interaction.time.milliseconds'=46.70ms;;;0; 'pages#speedindex.time.milliseconds'=2536.67ms;;;0;
... 2 --perspective=all OK: all pages: sessions: 34, page views: 62, bounce rate: 43%, ttfb: 1617.714ms, onload time: 5494.82ms, interaction to next paint: 46.70ms, speed index: 2536.67ms \\\\| 'pages#sessions.count'=34;;;0; 'pages#pageviews.count'=62;;;0; 'pages#bounce.rate.percentage'=43%;;;0;100 'pages#ttfb.milliseconds'=1617.71ms;;;0; 'pages#onload.time.milliseconds'=5494.82ms;;;0; 'pages#nextpaint.interaction.time.milliseconds'=46.70ms;;;0; 'pages#speedindex.time.milliseconds'=2536.67ms;;;0;
... 3 --perspective=all --critical-page-views=:10 ^CRITICAL: all pages.+$
... 4 --perspective=all --warning-page-views=:10 ^WARNING: all pages.+$
... 5 --perspective=url OK: All RUM counters are OK \\\\| '/#pageviews.count'=6;;;0; '/#ttfb.milliseconds'=2308.00ms;;;0; '/#onload.time.milliseconds'=5970.00ms;;;0; '/#nextpaint.interaction.time.milliseconds'=58.67ms;;;0; '/#speedindex.time.milliseconds'=2710.00ms;;;0; '/ariege/departement/#pageviews.count'=2;;;0; '/ariege/departement/#ttfb.milliseconds'=1380.00ms;;;0; '/ariege/departement/#onload.time.milliseconds'=2198.00ms;;;0; '/ariege/departement/#nextpaint.interaction.time.milliseconds'=50.67ms;;;0; '/ariege/departement/#speedindex.time.milliseconds'=1521.00ms;;;0; '/fr/#pageviews.count'=8;;;0; '/fr/#ttfb.milliseconds'=1741.67ms;;;0; '/fr/#onload.time.milliseconds'=5020.38ms;;;0; '/fr/#nextpaint.interaction.time.milliseconds'=74.00ms;;;0; '/fr/#speedindex.time.milliseconds'=3838.71ms;;;0; '/fr/ariege/#pageviews.count'=1;;;0; '/fr/ariege/#ttfb.milliseconds'=1491.00ms;;;0; '/fr/ariege/#onload.time.milliseconds'=2713.00ms;;;0; '/fr/ariege/#speedindex.time.milliseconds'=1697.00ms;;;0; '/fr/contrazy/#pageviews.count'=2;;;0; '/fr/contrazy/#ttfb.milliseconds'=3469.50ms;;;0; '/fr/contrazy/#onload.time.milliseconds'=9203.50ms;;;0; '/fr/contrazy/#speedindex.time.milliseconds'=4092.50ms;;;0; '/fr/merigon/#pageviews.count'=2;;;0; '/fr/merigon/#ttfb.milliseconds'=1174.50ms;;;0; '/fr/merigon/#onload.time.milliseconds'=5819.50ms;;;0; '/fr/merigon/#nextpaint.interaction.time.milliseconds'=29.33ms;;;0; '/fr/merigon/#speedindex.time.milliseconds'=6318.00ms;;;0; '/fr/stgirons/#pageviews.count'=3;;;0; '/fr/stgirons/#ttfb.milliseconds'=543.50ms;;;0; '/fr/stgirons/#onload.time.milliseconds'=2847.67ms;;;0; '/fr/stgirons/#nextpaint.interaction.time.milliseconds'=112.00ms;;;0; '/fr/stgirons/#speedindex.time.milliseconds'=1062.50ms;;;0; '/guzet/#pageviews.count'=3;;;0; '/guzet/#ttfb.milliseconds'=1264.00ms;;;0; '/guzet/#onload.time.milliseconds'=1916.00ms;;;0; '/guzet/#nextpaint.interaction.time.milliseconds'=64.00ms;;;0; '/guzet/#speedindex.time.milliseconds'=1445.00ms;;;0; '/massat/#pageviews.count'=1;;;0; '/massat/#ttfb.milliseconds'=2484.00ms;;;0; '/massat/#onload.time.milliseconds'=13258.00ms;;;0; '/montardit/#pageviews.count'=3;;;0; '/montardit/#ttfb.milliseconds'=1171.33ms;;;0; '/montardit/#onload.time.milliseconds'=4867.33ms;;;0; '/montardit/#nextpaint.interaction.time.milliseconds'=52.00ms;;;0; '/montardit/#speedindex.time.milliseconds'=1357.67ms;;;0;
... 6 --perspective=url --critical-interaction-next-paint=:10 ^CRITICAL: url.+$
... 7 --perspective=url --warning-interaction-next-paint=:10 ^WARNING: url.+$
... 8 --perspective=browser OK: All RUM counters are OK \\\\| 'Chrome#sessions.count'=16;;;0; 'Chrome#pageviews.count'=22;;;0; 'Chrome#bounce.rate.percentage'=20%;;;0;100 'Chrome#ttfb.milliseconds'=1554.31ms;;;0; 'Chrome#onload.time.milliseconds'=5495.91ms;;;0; 'Chrome#nextpaint.interaction.time.milliseconds'=80.38ms;;;0; 'Chrome#speedindex.time.milliseconds'=2706.62ms;;;0; 'Chrome Mobile#sessions.count'=1;;;0; 'Chrome Mobile#pageviews.count'=1;;;0; 'Chrome Mobile#bounce.rate.percentage'=1%;;;0;100 'Chrome Mobile#ttfb.milliseconds'=5021.00ms;;;0; 'Chrome Mobile#onload.time.milliseconds'=7429.00ms;;;0; 'Chrome Mobile#speedindex.time.milliseconds'=5260.00ms;;;0; 'Edge#sessions.count'=8;;;0; 'Edge#pageviews.count'=18;;;0; 'Edge#bounce.rate.percentage'=12%;;;0;100 'Edge#ttfb.milliseconds'=1330.06ms;;;0; 'Edge#onload.time.milliseconds'=4063.17ms;;;0; 'Edge#nextpaint.interaction.time.milliseconds'=41.87ms;;;0; 'Edge#speedindex.time.milliseconds'=1592.81ms;;;0; 'Firefox#sessions.count'=3;;;0; 'Firefox#pageviews.count'=15;;;0; 'Firefox#bounce.rate.percentage'=3%;;;0;100 'Firefox#ttfb.milliseconds'=1233.40ms;;;0; 'Firefox#onload.time.milliseconds'=6396.27ms;;;0; 'Firefox#nextpaint.interaction.time.milliseconds'=20.73ms;;;0; 'Firefox#speedindex.time.milliseconds'=2624.56ms;;;0; 'HeadlessChrome#sessions.count'=1;;;0; 'HeadlessChrome#pageviews.count'=1;;;0; 'HeadlessChrome#bounce.rate.percentage'=1%;;;0;100 'HeadlessChrome#ttfb.milliseconds'=6226.00ms;;;0; 'HeadlessChrome#onload.time.milliseconds'=8563.00ms;;;0; 'HeadlessChrome#speedindex.time.milliseconds'=6764.00ms;;;0; 'Mobile Safari#sessions.count'=1;;;0; 'Mobile Safari#pageviews.count'=1;;;0; 'Mobile Safari#bounce.rate.percentage'=1%;;;0;100 'Mobile Safari#ttfb.milliseconds'=1183.00ms;;;0; 'Mobile Safari#onload.time.milliseconds'=2572.00ms;;;0; 'Mobile Safari#speedindex.time.milliseconds'=1446.00ms;;;0; 'Opera Mobile#sessions.count'=1;;;0; 'Opera Mobile#pageviews.count'=1;;;0; 'Opera Mobile#bounce.rate.percentage'=1%;;;0;100 'Opera Mobile#ttfb.milliseconds'=3092.00ms;;;0; 'Opera Mobile#onload.time.milliseconds'=11991.00ms;;;0; 'Opera Mobile#speedindex.time.milliseconds'=6095.00ms;;;0;
... 9 --perspective=browser --critical-ttfb=:10 ^CRITICAL: browser.+$
... 10 --perspective=browser --warning-ttfb=:10 ^WARNING: browser.+$
... 11 --perspective=country OK: country country: sessions: 34, page views: 62, bounce rate: 43%, ttfb: 1617.714ms, onload time: 5494.82ms, interaction to next paint: 46.70ms, speed index: 2536.67ms \\\\| 'country#sessions.count'=34;;;0; 'country#pageviews.count'=62;;;0; 'country#bounce.rate.percentage'=43%;;;0;100 'country#ttfb.milliseconds'=1617.71ms;;;0; 'country#onload.time.milliseconds'=5494.82ms;;;0; 'country#nextpaint.interaction.time.milliseconds'=46.70ms;;;0; 'country#speedindex.time.milliseconds'=2536.67ms;;;0;
... 12 --perspective=country --critical-bounce-rate=:10 ^CRITICAL: country.+$
... 13 --perspective=country --warning-bounce-rate=:10 ^WARNING: country.+$
... 14 --perspective=city OK: All RUM counters are OK \\\\| 'EE#pageviews.count'=7;;;0; 'EE#ttfb.milliseconds'=1128.71ms;;;0; 'EE#onload.time.milliseconds'=1716.00ms;;;0; 'EE#nextpaint.interaction.time.milliseconds'=32.00ms;;;0; 'EE#speedindex.time.milliseconds'=1300.86ms;;;0; 'FR#sessions.count'=1;;;0; 'FR#pageviews.count'=1;;;0; 'FR#bounce.rate.percentage'=1%;;;0;100 'FR#ttfb.milliseconds'=1183.00ms;;;0; 'FR#onload.time.milliseconds'=2572.00ms;;;0; 'FR#speedindex.time.milliseconds'=1446.00ms;;;0; 'IN#sessions.count'=1;;;0; 'IN#pageviews.count'=3;;;0; 'IN#bounce.rate.percentage'=2%;;;0;100 'IN#ttfb.milliseconds'=1660.00ms;;;0; 'IN#onload.time.milliseconds'=3668.67ms;;;0; 'IN#nextpaint.interaction.time.milliseconds'=36.00ms;;;0; 'IN#speedindex.time.milliseconds'=2294.67ms;;;0; 'MA#sessions.count'=1;;;0; 'MA#pageviews.count'=1;;;0; 'MA#bounce.rate.percentage'=1%;;;0;100 'MA#ttfb.milliseconds'=3092.00ms;;;0; 'MA#onload.time.milliseconds'=11991.00ms;;;0; 'MA#speedindex.time.milliseconds'=6095.00ms;;;0; 'NO#sessions.count'=1;;;0; 'NO#pageviews.count'=3;;;0; 'NO#bounce.rate.percentage'=2%;;;0;100 'NO#ttfb.milliseconds'=1171.33ms;;;0; 'NO#onload.time.milliseconds'=4867.33ms;;;0; 'NO#nextpaint.interaction.time.milliseconds'=52.00ms;;;0; 'NO#speedindex.time.milliseconds'=1357.67ms;;;0; 'SN#sessions.count'=1;;;0; 'SN#pageviews.count'=1;;;0; 'SN#bounce.rate.percentage'=2%;;;0;100 'SN#ttfb.milliseconds'=2693.00ms;;;0; 'SN#onload.time.milliseconds'=23566.00ms;;;0; 'SN#nextpaint.interaction.time.milliseconds'=64.00ms;;;0; 'SN#speedindex.time.milliseconds'=17320.00ms;;;0; 'US#sessions.count'=4;;;0; 'US#pageviews.count'=6;;;0; 'US#bounce.rate.percentage'=5%;;;0;100 'US#ttfb.milliseconds'=2587.75ms;;;0; 'US#onload.time.milliseconds'=4897.67ms;;;0; 'US#speedindex.time.milliseconds'=3131.00ms;;;0;
... 15 --perspective=city --critical-speed-index=:10 ^CRITICAL: city.+$
... 16 --perspective=city --warning-speed-index=:10 ^WARNING: city.+$
... 17 --perspective=os OK: All RUM counters are OK \\\\| 'Android#sessions.count'=1;;;0; 'Android#pageviews.count'=1;;;0; 'Android#bounce.rate.percentage'=1%;;;0;100 'Android#ttfb.milliseconds'=3092.00ms;;;0; 'Android#onload.time.milliseconds'=11991.00ms;;;0; 'Android#speedindex.time.milliseconds'=6095.00ms;;;0; 'Linux#sessions.count'=1;;;0; 'Linux#pageviews.count'=1;;;0; 'Linux#bounce.rate.percentage'=1%;;;0;100 'Linux#ttfb.milliseconds'=6226.00ms;;;0; 'Linux#onload.time.milliseconds'=8563.00ms;;;0; 'Linux#speedindex.time.milliseconds'=6764.00ms;;;0; 'Mac OS X#sessions.count'=1;;;0; 'Mac OS X#pageviews.count'=3;;;0; 'Mac OS X#bounce.rate.percentage'=2%;;;0;100 'Mac OS X#ttfb.milliseconds'=1264.00ms;;;0; 'Mac OS X#onload.time.milliseconds'=1916.00ms;;;0; 'Mac OS X#speedindex.time.milliseconds'=1445.00ms;;;0; 'Windows#sessions.count'=21;;;0; 'Windows#pageviews.count'=49;;;0; 'Windows#bounce.rate.percentage'=27%;;;0;100 'Windows#ttfb.milliseconds'=1371.89ms;;;0; 'Windows#onload.time.milliseconds'=5404.73ms;;;0; 'Windows#nextpaint.interaction.time.milliseconds'=45.41ms;;;0; 'Windows#speedindex.time.milliseconds'=2345.58ms;;;0; 'iOS#sessions.count'=1;;;0; 'iOS#pageviews.count'=1;;;0; 'iOS#bounce.rate.percentage'=1%;;;0;100 'iOS#ttfb.milliseconds'=1183.00ms;;;0; 'iOS#onload.time.milliseconds'=2572.00ms;;;0; 'iOS#speedindex.time.milliseconds'=1446.00ms;;;0;
... 18 --perspective=os --critical-onload=:10 ^CRITICAL: os.+$
... 19 --perspective=os --warning-onload=:10 ^WARNING: os.+$
... 20 --perspective=os --critical-sessions=:10 ^CRITICAL: os.+$
... 21 --perspective=os --warning-sessions=:10 ^WARNING: os.+$

View File

@ -0,0 +1,34 @@
*** Settings ***
Documentation Quanta
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${CMD} ${CENTREON_PLUGINS}
... --plugin=apps::monitoring::quanta::restapi::plugin
... --hostname=${HOSTNAME}
... --api-token=PaSsWoRd
... --site-id=10
... --proto=http
... --port=${APIPORT}
*** Test Cases ***
SiteOverview ${tc}
[Tags] quanta api
${command} Catenate
... ${CMD}
... --mode=site-overview
... ${extra_options}
Ctn Run Command And Check Result As Strings ${command} ${expected_result}
Examples: tc extraoptions expected_result --
... 1 ${EMPTY} OK: Site 'www.ariege.com' performance score: 72, digital sobriety score: 56, eco design score: 62, carbon footprint per click: 1.28g | 'www.ariege.com#performance.score'=72;;;0;100 'www.ariege.com#digitalsobriety.score'=56;;;0;100 'www.ariege.com#ecodesign.score'=62;;;0;100 'www.ariege.com#perclick.carbon.footprint.gramm'=1.28g;;;0;

View File

@ -0,0 +1,39 @@
*** Settings ***
Documentation Quanta
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${CMD} ${CENTREON_PLUGINS}
... --plugin=apps::monitoring::quanta::restapi::plugin
... --hostname=${HOSTNAME}
... --api-token=PaSsWoRd
... --site-id=10
... --proto=http
... --port=${APIPORT}
*** Test Cases ***
UserJourneyIncidents ${tc}
[Tags] quanta api
${command} Catenate
... ${CMD}
... --mode=user-journey-incidents
... --journey-id=3666
... ${extra_options}
Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp}
Examples: tc extraoptions expected_regexp --
... 1 ${EMPTY} CRITICAL: Incident for interaction 'Decline cookies' status: open \\\\| 'quanta.incidents.total.count'=32;;;0;
... 2 --ignore-closed CRITICAL: Incident for interaction 'Decline cookies' status: open \\\\| 'quanta.incidents.total.count'=1;;;0;
... 3 --critical-incident-status='' --warning-incident-status='\\\%{status} =~ /open/i' WARNING: Incident for interaction 'Decline cookies' status: open \\\\| 'quanta.incidents.total.count'=32;;;0;
... 4 --critical-incident-status='' --warning-incident-type='\\\%{type} =~ /timeout/i' ^WARNING: Incident for interaction.+$
... 5 --critical-incident-status='' --warning-incident-duration=:10 ^WARNING: Incident for interaction.+$

View File

@ -0,0 +1,43 @@
*** Settings ***
Documentation Quanta
Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource
Suite Setup Start Mockoon ${MOCKOON_JSON}
Suite Teardown Stop Mockoon
Test Timeout 120s
*** Variables ***
${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json
${HOSTNAME} 127.0.0.1
${APIPORT} 3000
${CMD} ${CENTREON_PLUGINS}
... --plugin=apps::monitoring::quanta::restapi::plugin
... --hostname=${HOSTNAME}
... --api-token=PaSsWoRd
... --site-id=10
... --proto=http
... --port=${APIPORT}
*** Test Cases ***
UserJourneyStatistics ${tc}
[Tags] quanta api
${command} Catenate
... ${CMD}
... --mode=user-journey-statistics
... --journey-id=3666
... ${extra_options}
Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp}
Examples: tc extraoptions expected_regexp --
... 1 ${EMPTY} OK: journey "Basic user journey" journey performance score: 76, journey hero time: 35933.10ms, journey speed index: 13754.20ms, journey ttfb: 33.56ms \\\\| 'journey_Basic user journey#journey.performance.score'=76;;;0;100 'journey_Basic user journey#journey.herotime.milliseconds'=35933.10ms;;;0; 'journey_Basic user journey#journey.speedindex.time.milliseconds'=13754.20ms;;;0; 'journey_Basic user journey#journey.ttfb.milliseconds'=33.56ms;;;0;
... 2 --warning-journey-hero-time=:10 ^WARNING: journey "Basic user journey" journey hero time.+$
... 3 --warning-journey-speed-index=:10 ^WARNING: journey "Basic user journey" journey speed index.+$
... 4 --critical-journey-ttfb=:10 ^CRITICAL: journey "Basic user journey" journey ttfb.+$
... 5 --critical-journey-performance-score=:10 ^CRITICAL: journey "Basic user journey" journey performance score.+$
... 6 --show-interactions --critical-interaction-ttfb=:10 ^CRITICAL: interaction "Home".+$
... 7 --show-interactions --warning-interaction-speed-index=:10 ^WARNING: interaction "Home".+$
... 8 --show-interactions --critical-interaction-hero-time=:10 ^CRITICAL: interaction "Home".+$
... 9 --show-interactions=1 --warning-interaction-performance-score=11: ^WARNING: interaction "Home".+$