From 72d46d1cb56499bd76e65df830ebfe85a8dba626 Mon Sep 17 00:00:00 2001 From: omercier <32134301+omercier@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:22:38 +0200 Subject: [PATCH] fix(http-collection): merge community PR + add tests (#5044) Co-authored-by: garnier-quentin Co-authored-by: qgarnier Co-authored-by: Colin Gagnaire Co-authored-by: pkippes Co-authored-by: tuntoja <58987095+tuntoja@users.noreply.github.com> Co-authored-by: May <110405507+paul-oureib@users.noreply.github.com> --- .../deb.json | 2 + src/apps/protocols/http/mode/collection.pm | 186 ++++++++++++---- ...on-centreon-web-check-auth.collection.json | 61 +++++ ...-web-check-broken-commands.collection.json | 160 +++++++++++++ ...treon-web-check-down-hosts.collection.json | 132 +++++++++++ .../http/collection-centreon-web.mockoon.json | 210 ++++++++++++++++++ .../http/collection-centreon-web.robot | 34 +++ 7 files changed, 736 insertions(+), 49 deletions(-) create mode 100644 tests/robot/apps/protocols/http/collection-centreon-web-check-auth.collection.json create mode 100644 tests/robot/apps/protocols/http/collection-centreon-web-check-broken-commands.collection.json create mode 100644 tests/robot/apps/protocols/http/collection-centreon-web-check-down-hosts.collection.json create mode 100644 tests/robot/apps/protocols/http/collection-centreon-web.mockoon.json create mode 100644 tests/robot/apps/protocols/http/collection-centreon-web.robot diff --git a/packaging/centreon-plugin-Applications-Protocol-Http/deb.json b/packaging/centreon-plugin-Applications-Protocol-Http/deb.json index e79bebd9d..1fee8f83c 100644 --- a/packaging/centreon-plugin-Applications-Protocol-Http/deb.json +++ b/packaging/centreon-plugin-Applications-Protocol-Http/deb.json @@ -1,7 +1,9 @@ { "dependencies": [ + "libjson-maybexs-perl", "libjson-perl", "libxml-xpath-perl", + "libxml-libxml-perl", "libxml-libxml-simple-perl" ] } diff --git a/src/apps/protocols/http/mode/collection.pm b/src/apps/protocols/http/mode/collection.pm index 1ddddb697..df87efd84 100644 --- a/src/apps/protocols/http/mode/collection.pm +++ b/src/apps/protocols/http/mode/collection.pm @@ -87,7 +87,7 @@ sub custom_select_output { if (defined($format)) { return sprintf( - $format->{printf_msg}, @{$format->{printf_var}} + $format->{printf_msg}, @{$format->{printf_var}} ); } @@ -123,7 +123,7 @@ 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 => { 'config:s' => { name => 'config' }, 'filter-selection:s%' => { name => 'filter_selection' }, @@ -192,7 +192,7 @@ sub get_map_value { my ($self, %options) = @_; return undef if ( - !defined($self->{config}->{mapping}) || + !defined($self->{config}->{mapping}) || !defined($self->{config}->{mapping}->{ $options{map} }) ); return '' if (!defined($self->{config}->{mapping}->{ $options{map} }->{ $options{value} })); @@ -254,8 +254,9 @@ sub get_payload { sub call_http { my ($self, %options) = @_; - if (!defined($options{rq}->{hostname}) || $options{rq}->{hostname} eq '') { - $self->{output}->add_option_msg(short_msg => "hostname attribute is missing [http > requests > $options{rq}->{name}]"); + if ((!defined($options{rq}->{full_url}) || $options{rq}->{full_url} eq '') && + (!defined($options{rq}->{hostname}) || $options{rq}->{hostname} eq '')) { + $self->{output}->add_option_msg(short_msg => "hostname or full_url attribute is missing [http > requests > $options{rq}->{name}]"); $self->{output}->option_exit(); } if (!defined($options{rq}->{rtype}) || $options{rq}->{rtype} !~ /^(?:txt|json|xml)$/) { @@ -298,11 +299,19 @@ sub call_http { $http = centreon::plugins::http->new(noptions => 1, output => $self->{output}); } + my $full_url; + my $hostname = $self->substitute_string(value => $options{rq}->{hostname}); + if (defined($options{rq}->{full_url}) && $options{rq}->{full_url} ne '') { + $full_url = $self->substitute_string(value => $options{rq}->{full_url}); + $hostname = ''; + } + my $timing0 = [gettimeofday]; my ($content) = $http->request( - backend => $self->substitute_string(value => $options{rq}->{backend}), + http_backend => $self->substitute_string(value => $options{rq}->{backend}), method => $self->substitute_string(value => $options{rq}->{method}), - hostname => $self->substitute_string(value => $options{rq}->{hostname}), + full_url => $full_url, + hostname => $hostname, proto => $self->substitute_string(value => $options{rq}->{proto}), port => $self->substitute_string(value => $options{rq}->{port}), url_path => $self->substitute_string(value => $options{rq}->{endpoint}), @@ -321,26 +330,6 @@ sub call_http { $self->add_builtin(name => 'httpCode.' . $options{rq}->{name}, value => $http->get_code()); $self->add_builtin(name => 'httpMessage.' . $options{rq}->{name}, value => $http->get_message()); - if ($options{rq}->{rtype} eq 'json') { - eval { - $content = JSON::XS->new->utf8->decode($content); - }; - } elsif ($options{rq}->{rtype} eq 'xml') { - eval { - $SIG{__WARN__} = sub {}; - $content = XMLin($content, ForceArray => $options{rq}->{force_array}, KeyAttr => []); - }; - } - if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode response (add --debug option to display returned content)"); - $self->{output}->output_add(long_msg => "$@", debug => 1); - $self->{output}->option_exit(); - } - - my $encoded = JSON::XS->new->utf8->pretty->encode($content); - $self->{output}->output_add(long_msg => '======> returned JSON structure:', debug => 1); - $self->{output}->output_add(long_msg => "$encoded", debug => 1); - return ($http->get_header(), $content, $http); } @@ -441,8 +430,31 @@ sub parse_structure { $options{conf}->{path} = $self->substitute_string(value => $options{conf}->{path}); + my $content; + if ($options{rtype} eq 'json') { + eval { + $content = JSON::XS->new->utf8->decode($options{content}); + }; + } elsif ($options{rtype} eq 'xml') { + eval { + $SIG{__WARN__} = sub {}; + $content = XMLin($options{content}, ForceArray => $options{force_array}, KeyAttr => []); + }; + } + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode response (add --debug option to display returned content)"); + $self->{output}->output_add(long_msg => "$@", debug => 1); + $self->{output}->option_exit(); + } + + if ($self->{output}->is_debug()) { + my $encoded = JSON::XS->new->allow_nonref(1)->utf8->pretty->encode($content); + $self->{output}->output_add(long_msg => '======> returned JSON structure:', debug => 1); + $self->{output}->output_add(long_msg => "$encoded", debug => 1); + } + my $jpath = JSON::Path->new($options{conf}->{path}); - my @values = $jpath->values($options{content}); + my @values = $jpath->values($content); my $local = {}; my $i = 0; @@ -534,26 +546,50 @@ sub collect_http_tables { ($headers, $content, $http) = $self->call_http(rq => $options{requests}->[$i], http => $options{http}); $self->set_builtin(); - next if (!defined($options{requests}->[$i]->{parse})); - - my $local; - foreach my $conf (@{$options{requests}->[$i]->{parse}}) { - if ($options{requests}->[$i]->{rtype} eq 'txt') { - $local = $self->parse_txt(name => $options{requests}->[$i]->{name}, headers => $headers, content => $content, conf => $conf); - } else { - $local = $self->parse_structure(name => $options{requests}->[$i]->{name}, content => $content, conf => $conf); - } - } - - if (defined($options{requests}->[$i]->{scenario_stopped}) && $options{requests}->[$i]->{scenario_stopped} && - $self->check_filter2(filter => $options{requests}->[$i]->{scenario_stopped}, values => $self->{expand})) { + if (defined($options{requests}->[$i]->{scenario_stopped_first}) && $options{requests}->[$i]->{scenario_stopped_first} && + $self->check_filter2(filter => $options{requests}->[$i]->{scenario_stopped_first}, values => $self->{expand})) { $self->{scenario_stopped} = 1; if (defined($options{requests}->[$i]->{scenario_retry}) && $options{requests}->[$i]->{scenario_retry} =~ /^true|1$/i) { $self->{scenario_loop}++; $self->{scenario_retry} = 1; } } else { - $self->save_local_http_cache(local_http_cache => $local_http_cache, local => $local); + my $local = {}; + if (defined($options{requests}->[$i]->{parse})) { + foreach my $conf (@{$options{requests}->[$i]->{parse}}) { + my $lentries = {}; + if ($options{requests}->[$i]->{rtype} eq 'txt') { + $lentries = $self->parse_txt(name => $options{requests}->[$i]->{name}, headers => $headers, content => $content, conf => $conf); + } else { + $lentries = $self->parse_structure( + name => $options{requests}->[$i]->{name}, + content => $content, + conf => $conf, + rtype => $options{requests}->[$i]->{rtype}, + force_array => $options{requests}->[$i]->{force_array} + ); + } + + $local = { %$local, %$lentries }; + } + } + + $self->set_functions( + section => "http > requests > $options{requests}->[$i]->{name}", + functions => $options{requests}->[$i]->{functions}, + default => 1 + ); + + if (defined($options{requests}->[$i]->{scenario_stopped}) && $options{requests}->[$i]->{scenario_stopped} && + $self->check_filter2(filter => $options{requests}->[$i]->{scenario_stopped}, values => $self->{expand})) { + $self->{scenario_stopped} = 1; + if (defined($options{requests}->[$i]->{scenario_retry}) && $options{requests}->[$i]->{scenario_retry} =~ /^true|1$/i) { + $self->{scenario_loop}++; + $self->{scenario_retry} = 1; + } + } else { + $self->save_local_http_cache(local_http_cache => $local_http_cache, local => $local); + } } } @@ -625,6 +661,16 @@ sub use_local_http_cache { } } + my $builtin = $local_http_cache->get(name => 'builtin'); + foreach my $name (keys %$builtin) { + $self->add_builtin(name => $name, value => $builtin->{$name}); + } + + my $local_vars = $local_http_cache->get(name => 'local_vars'); + foreach my $name (keys %$local_vars) { + $self->set_local_variable(name => $name, value => $local_vars->{$name}); + } + return 1; } @@ -632,12 +678,20 @@ sub save_local_http_cache { my ($self, %options) = @_; if (defined($options{local_http_cache})) { + my $expand = {}; + foreach my $name (keys %{$self->{expand}}) { + next if ($name =~ /^(builtin|constants)\./); + $expand->{$name} = $self->{expand}->{$name}; + } + $options{local_http_cache}->write( data => { http_collected => { tables => $options{local}, epoch => time() - } + }, + builtin => $self->{builtin}, + local_vars => $expand } ); } @@ -737,6 +791,17 @@ sub display_variables { } } } + + foreach my $name (keys %{$self->{expand}}) { + $self->{output}->output_add( + long_msg => sprintf( + ' %s = %s', + $name, + $self->{expand}->{$name} + ), + debug => 1 + ); + } } sub collect_http { @@ -765,6 +830,13 @@ sub collect_http { $self->collect_http_sampling(); + # can use local_var set for selection/selection_loop + $self->{local_vars} = {}; + foreach my $name (keys %{$self->{expand}}) { + next if ($name =~ /^(builtin|constants)\./); + $self->{local_vars}->{$name} = $self->{expand}->{$name}; + } + if ($self->{output}->is_debug()) { $self->display_variables(); } @@ -790,6 +862,14 @@ sub get_local_variable { } +sub set_local_variables { + my ($self, %options) = @_; + + foreach (keys %{$self->{local_vars}}) { + $self->set_local_variable(name => $_, value => $self->{local_vars}->{$_}); + } +} + sub set_local_variable { my ($self, %options) = @_; @@ -934,7 +1014,7 @@ sub parse_http_tables { ($code, $msg_error, $end, $table_label) = $self->parse_forward( chars => $options{chars}, start => $options{start}, - allowed => '[a-zA-Z0-9_]', + allowed => '[a-zA-Z0-9_\-]', stop => '[).]' ); if ($code) { @@ -999,7 +1079,7 @@ sub parse_http_tables { ($code, $msg_error, $end, $label) = $self->parse_forward( chars => $options{chars}, start => $end + 2, - allowed => '[a-zA-Z0-9_]', + allowed => '[a-zA-Z0-9_\-]', stop => '[)]' ); if ($code) { @@ -1039,7 +1119,7 @@ sub parse_special_variable { my ($code, $msg_error, $end, $label) = $self->parse_forward( chars => $options{chars}, start => $start + 2, - allowed => '[a-zA-Z0-9\._]', + allowed => '[a-zA-Z0-9\._\-]', stop => '[)]' ); if ($code) { @@ -1630,6 +1710,7 @@ sub exec_func_capture { $self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save attribute"); $self->{output}->option_exit(); } + $self->set_special_variable_value(value => $value, %$save); } } @@ -1707,7 +1788,7 @@ sub prepare_variables { return undef if (!defined($options{value})); - while ($options{value} =~ /%\(([a-zA-Z0-9\.]+?)\)/g) { + while ($options{value} =~ /%\(([a-zA-Z0-9\.\-]+?)\)/g) { next if ($1 =~ /^http\./); $options{value} =~ s/%\(($1)\)/\$expand->{'$1'}/g; } @@ -1721,7 +1802,7 @@ sub check_filter { return 0 if (!defined($options{filter}) || $options{filter} eq ''); our $expand = $options{values}; - $options{filter} =~ s/%\(([a-zA-Z0-9\._:]+?)\)/\$expand->{'$1'}/g; + $options{filter} =~ s/%\(([a-zA-Z0-9\._:\-]+?)\)/\$expand->{'$1'}/g; my $result = $self->{safe}->reval("$options{filter}"); if ($@) { $self->{output}->add_option_msg(short_msg => 'Unsafe code evaluation: ' . $@); @@ -1737,7 +1818,7 @@ sub check_filter2 { return 0 if (!defined($options{filter}) || $options{filter} eq ''); our $expand = $options{values}; - $options{filter} =~ s/%\(([a-zA-Z0-9\._:]+?)\)/\$expand->{'$1'}/g; + $options{filter} =~ s/%\(([a-zA-Z0-9\._:\-]+?)\)/\$expand->{'$1'}/g; my $result = $self->{safe}->reval("$options{filter}"); if ($@) { $self->{output}->add_option_msg(short_msg => 'Unsafe code evaluation: ' . $@); @@ -1820,6 +1901,7 @@ sub add_selection { my $config = {}; $self->{expand} = $self->set_constants(); $self->set_builtin(); + $self->set_local_variables(); $self->{expand}->{name} = $_->{name} if (defined($_->{name})); $self->set_functions(section => "selection > $i > functions", functions => $_->{functions}, position => 'before_expand'); $self->set_expand_table(section => "selection > $i > expand_table", expand => $_->{expand_table}); @@ -1857,20 +1939,26 @@ sub add_selection_loop { next if (!defined($_->{source}) || $_->{source} eq ''); $self->{current_section} = '[selection_loop > ' . $i . ' > source]'; + my $result = $self->parse_special_variable(chars => [split //, $_->{source}], start => 0); if ($result->{type} != 2) { $self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed"); $self->{output}->option_exit(); } + next if (!defined($self->{http_collected}->{tables}->{ $result->{table} })); + foreach my $instance (keys %{$self->{http_collected}->{tables}->{ $result->{table} }}) { $self->{expand} = $self->set_constants(); $self->set_builtin(); + $self->set_local_variables(); $self->{expand}->{ $result->{table} . '.instance' } = $instance; + foreach my $label (keys %{$self->{http_collected}->{tables}->{ $result->{table} }->{$instance}}) { $self->{expand}->{ $result->{table} . '.' . $label } = $self->{http_collected}->{tables}->{ $result->{table} }->{$instance}->{$label}; } + my $config = {}; $self->{expand}->{name} = $_->{name} if (defined($_->{name})); $self->set_functions(section => "selection_loop > $i > functions", functions => $_->{functions}, position => 'before_expand'); diff --git a/tests/robot/apps/protocols/http/collection-centreon-web-check-auth.collection.json b/tests/robot/apps/protocols/http/collection-centreon-web-check-auth.collection.json new file mode 100644 index 000000000..bb80be606 --- /dev/null +++ b/tests/robot/apps/protocols/http/collection-centreon-web-check-auth.collection.json @@ -0,0 +1,61 @@ +{ + "constants": { + "protocol": "https", + "port": "443", + "customPath": "centreon" + }, + "http": { + "requests": [ + { + "name": "authenticationRequest", + "hostname": "%(constants.hostname)", + "proto": "%(constants.protocol)", + "port": "%(constants.port)", + "endpoint": "/%(constants.customPath)/api/latest/login", + "method": "POST", + "headers": [ + "Accept:application/json", + "Content-Type:application/json" + ], + "timeout": 30, + "payload": { + "type": "json", + "value": { + "security": { + "credentials": { + "login": "%(constants.username)", + "password": "%(constants.password)" + } + } + } + }, + "backend": "curl", + "rtype": "json", + "parse": [ + { + "name": "token", + "path": "$.security.token", + "entries": [ + { + "id": "value" + } + ] + } + ] + } + ] + }, + "selection": [ + { + "name": "authenticationSelection", + "critical": "defined(%(builtin.httpCode.authenticationRequest)) and %(builtin.httpCode.authenticationRequest) != 200", + "formatting": { + "printf_msg": "Authentication resulted in %s HTTP code", + "printf_var": [ + "%(builtin.httpCode.authenticationRequest)" + ], + "display_ok": true + } + } + ] +} diff --git a/tests/robot/apps/protocols/http/collection-centreon-web-check-broken-commands.collection.json b/tests/robot/apps/protocols/http/collection-centreon-web-check-broken-commands.collection.json new file mode 100644 index 000000000..1c3fc7d90 --- /dev/null +++ b/tests/robot/apps/protocols/http/collection-centreon-web-check-broken-commands.collection.json @@ -0,0 +1,160 @@ +{ + "constants": { + "protocol": "https", + "port": "443", + "customPath": "centreon" + }, + "http": { + "requests": [ + { + "name": "authenticationRequest", + "hostname": "%(constants.hostname)", + "proto": "%(constants.protocol)", + "port": "%(constants.port)", + "endpoint": "/%(constants.customPath)/api/latest/login", + "method": "POST", + "headers": [ + "Accept:application/json", + "Content-Type:application/json" + ], + "timeout": 30, + "payload": { + "type": "json", + "value": { + "security": { + "credentials": { + "login": "%(constants.username)", + "password": "%(constants.password)" + } + } + } + }, + "scenario_stopped": "%(builtin.httpCode.authenticationRequest) != 200", + "backend": "curl", + "rtype": "json", + "parse": [ + { + "name": "token", + "path": "$.security.token", + "entries": [ + { + "id": "value" + } + ] + } + ] + }, + { + "name": "resourcesRequest", + "hostname": "%(constants.hostname)", + "proto": "%(constants.protocol)", + "port": "%(constants.port)", + "endpoint": "/%(constants.customPath)/api/latest/monitoring/resources?limit=1000&search={\"information\": {\"$eq\": \"(Execute command failed)\"}}", + "method": "GET", + "headers": [ + "X-AUTH-TOKEN: %(http.tables.authenticationRequestToken.[0].value)", + "Accept: text/json" + ], + "timeout": 30, + "scenario_stopped": "%(builtin.httpCode.authenticationRequest) != 200", + "backend": "curl", + "rtype": "json", + "parse": [ + { + "name": "meta", + "type": "body", + "path": "$.meta", + "entries": [ + { + "id": "total" + } + ] + }, + { + "name": "entries", + "type": "body", + "path": "$.result[*]", + "entries": [ + { + "id": "parent.name" + }, + { + "id": "name" + }, + { + "id": "type" + }, + { + "id": "information" + } + ] + } + ] + } + ] + }, + "selection_loop": [ + { + "name": "HostsLoop", + "source": "%(http.tables.resourcesRequestEntries)", + "filter": "%(resourcesRequestEntries.type) eq 'host'", + "formatting": { + "display_ok": "false", + "printf_msg": "Host %s's output is '%s'", + "printf_var": [ + "%(resourcesRequestEntries.name)", + "%(resourcesRequestEntries.information)" + ] + }, + "warning": "%(resourcesRequestEntries.information) =~ /No output returned from plugin|Execute command failed/" + }, + { + "name": "ServicesLoop", + "source": "%(http.tables.resourcesRequestEntries)", + "filter": "%(resourcesRequestEntries.type) eq 'service'", + "formatting": { + "display_ok": "false", + "printf_msg": "Service %s/%s output is '%s'", + "printf_var": [ + "%(resourcesRequestEntries.parent.name)", + "%(resourcesRequestEntries.name)", + "%(resourcesRequestEntries.information)" + ] + }, + "warning": "%(resourcesRequestEntries.information) =~ /No output returned from plugin|Execute command failed/" + } + ], + "selection": [ + { + "name": "totalSelection", + "functions": [ + { + "type": "assign", + "expression": "%(http.tables.resourcesRequestMeta.[0].total)", + "save": "%(brokenCommandsCount)" + } + ], + "perfdatas": [ + { + "nlabel": "commands.broken.count", + "value": "%(brokenCommandsCount)", + "warning": "0", + "min": 0 + } + ], + "exit": "%(brokenCommandsCount) == 0", + "warning": "%(brokenCommandsCount) > 0", + "formatting_warning": { + "printf_msg": "", + "display_ok": false, + "separator": " " + }, + "formatting": { + "printf_msg": "All commands are fine", + "display_ok": true, + "separator": "" + } + } + ] +} + diff --git a/tests/robot/apps/protocols/http/collection-centreon-web-check-down-hosts.collection.json b/tests/robot/apps/protocols/http/collection-centreon-web-check-down-hosts.collection.json new file mode 100644 index 000000000..1d71f43d1 --- /dev/null +++ b/tests/robot/apps/protocols/http/collection-centreon-web-check-down-hosts.collection.json @@ -0,0 +1,132 @@ +{ + "constants": { + "protocol": "https", + "port": "443", + "customPath": "centreon" + }, + "http": { + "requests": [ + { + "name": "authenticationRequest", + "hostname": "%(constants.hostname)", + "proto": "%(constants.protocol)", + "port": "%(constants.port)", + "endpoint": "/%(constants.customPath)/api/latest/login", + "method": "POST", + "headers": [ + "Accept:application/json", + "Content-Type:application/json" + ], + "timeout": 30, + "payload": { + "type": "json", + "value": { + "security": { + "credentials": { + "login": "%(constants.username)", + "password": "%(constants.password)" + } + } + } + }, + "scenario_stopped": "%(builtin.httpCode.authenticationRequest) != 200", + "backend": "curl", + "rtype": "json", + "parse": [ + { + "name": "token", + "path": "$.security.token", + "entries": [ + { + "id": "value" + } + ] + } + ] + }, + { + "name": "hostsRequest", + "hostname": "%(constants.hostname)", + "proto": "%(constants.protocol)", + "port": "%(constants.port)", + "endpoint": "/%(constants.customPath)/api/latest/monitoring/hosts?limit=1000", + "method": "GET", + "headers": [ + "X-AUTH-TOKEN: %(http.tables.authenticationRequestToken.[0].value)", + "Accept: text/json" + ], + "timeout": 30, + "backend": "curl", + "rtype": "json", + "parse": [ + { + "name": "entries", + "type": "body", + "path": "$.result[*]", + "entries": [ + { + "id": "name" + }, + { + "id": "state" + } + ] + } + ] + } + ] + }, + "selection": [ + { + "name": "authenticationSelection", + "critical": "defined(%(builtin.httpCode.authenticationRequest)) and %(builtin.httpCode.authenticationRequest) != 200", + "exit": "defined(%(builtin.httpCode.authenticationRequest)) and %(builtin.httpCode.authenticationRequest) != 200", + "formatting": { + "printf_msg": "Authentication resulted in %s HTTP code", + "printf_var": [ + "%(builtin.httpCode.authenticationRequest)" + ], + "display_ok": false + } + }, + { + "name": "hostsSelection", + "functions": [ + { + "type": "count", + "src": "%(http.tables.hostsRequestEntries)", + "filter": "%(src.state) != 0", + "save": "%(downCount)" + }, + { + "type": "count", + "src": "%(http.tables.hostsRequestEntries)", + "save": "%(hostsCount)" + } + ], + "perfdatas": [ + { + "nlabel": "hostsRequest.down.count", + "value": "%(downCount)", + "warning": "0", + "min": 0, + "max": "%(hostsCount)" + } + ], + "warning": "%(downCount) > 0", + "formatting": { + "printf_msg": "Number of down hosts: %s out of %s", + "printf_var": [ + "%(downCount)", + "%(hostsCount)" + ], + "display_ok": true + } + } + ], + "formatting": { + "custom_message_global": "All hosts are UP", + "separator": "-" + } +} + diff --git a/tests/robot/apps/protocols/http/collection-centreon-web.mockoon.json b/tests/robot/apps/protocols/http/collection-centreon-web.mockoon.json new file mode 100644 index 000000000..fd6109081 --- /dev/null +++ b/tests/robot/apps/protocols/http/collection-centreon-web.mockoon.json @@ -0,0 +1,210 @@ +{ + "uuid": "919382d8-0f30-447f-abcd-45c98e84d7fe", + "lastMigration": 32, + "name": "Centreon web mock for tests of HTTP Collections", + "endpointPrefix": "", + "latency": 0, + "port": 3001, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "82abcb5a-0a65-409f-badd-5e881b6786df", + "type": "http", + "documentation": "Authentication request", + "method": "post", + "endpoint": "centreon/api/latest/login", + "responses": [ + { + "uuid": "b4229c90-76b3-4f8c-be0f-aceeb5566051", + "body": "{}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "content-type", + "value": "application/json; charset=utf-8" + }, + { + "key": "content-length", + "value": "2" + } + ], + "bodyType": "DATABUCKET", + "filePath": "", + "databucketID": "0ibb", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "d67aa94e-09c5-434d-b00a-e9e147e90220", + "type": "http", + "documentation": "Monitoring of hosts, used to look for down hosts", + "method": "get", + "endpoint": "centreon/api/latest/monitoring/hosts", + "responses": [ + { + "uuid": "c98ea5bd-143b-414e-ae28-d094e7059682", + "body": "", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "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": "DATABUCKET", + "filePath": "", + "databucketID": "fdau", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + }, + { + "uuid": "9622753c-4c89-4f37-aa27-6088a4c5557a", + "type": "http", + "documentation": "Monitoring of resources, used to look for broken commands", + "method": "get", + "endpoint": "centreon/api/latest/monitoring/resources", + "responses": [ + { + "uuid": "1d5391f0-9edc-4b84-b9ed-b78524ae1782", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "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": "DATABUCKET", + "filePath": "", + "databucketID": "tpm8", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [], + "body": "{}" + } + ], + "responseMode": null + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "82abcb5a-0a65-409f-badd-5e881b6786df" + }, + { + "type": "route", + "uuid": "d67aa94e-09c5-434d-b00a-e9e147e90220" + }, + { + "type": "route", + "uuid": "9622753c-4c89-4f37-aa27-6088a4c5557a" + } + ], + "proxyMode": false, + "proxyHost": "", + "proxyRemovePrefix": false, + "tlsOptions": { + "enabled": false, + "type": "CERT", + "pfxPath": "", + "certPath": "", + "keyPath": "", + "caPath": "", + "passphrase": "" + }, + "cors": true, + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "proxyReqHeaders": [ + { + "key": "", + "value": "" + } + ], + "proxyResHeaders": [ + { + "key": "", + "value": "" + } + ], + "data": [ + { + "uuid": "497b64fa-7c29-4741-8c57-c818aafc9482", + "id": "fdcz", + "name": "Authentication payload", + "documentation": "Authentication payload", + "value": "{\"security\":{\"credentials\":{\"password\":\"***\",\"login\":\"admin\"}}}" + }, + { + "uuid": "e9b76766-90a5-496e-8102-3941d22feb32", + "id": "0ibb", + "name": "Authentication response", + "documentation": "Authentication response", + "value": "{\n \"security\" : {\n \"token\" : \"Vj77k9p53L+FTXon1UDvaYBZU8P3MnUK2siU7mst3HJ1QcnjTDWVW6TX3RlccjpV\"\n },\n \"contact\" : {\n \"name\" : \"Administrateur_Centreon\",\n \"is_admin\" : true,\n \"email\" : \"null@localhost.local\",\n \"alias\" : \"admin\",\n \"id\" : 1\n }\n}" + }, + { + "uuid": "18e9cb4a-d822-44cd-b6a3-83dbead20130", + "id": "fdau", + "name": "Monitoring/hosts response", + "documentation": "Monitoring/hosts response to count down hosts", + "value": "{\n \"meta\": {\n \"search\": {},\n \"total\": 1,\n \"limit\": 1000,\n \"sort_by\": {},\n \"page\": 1\n },\n \"result\": [\n {\n \"passive_checks\": false,\n \"timezone\": \"\",\n \"last_check\": \"2023-11-21T21:21:14+01:00\",\n \"checked\": true,\n \"state\": 0,\n \"last_time_up\": \"2023-11-21T21:21:18+01:00\",\n \"icon_image\": \"ppm/applications-monitoring-centreon-central-centreon-128-2.png\",\n \"icon_image_alt\": \"\",\n \"state_type\": 1,\n \"address_ip\": \"127.0.0.1\",\n \"criticality\": null,\n \"last_time_down\": null,\n \"scheduled_downtime_depth\": 0,\n \"name\": \"CENTREON\",\n \"last_time_unreachable\": null,\n \"alias\": \"Centreon Central Server\",\n \"poller_id\": 1,\n \"last_hard_state_change\": \"2023-09-07T20:03:53+02:00\",\n \"execution_time\": 0.112673,\n \"last_state_change\": \"2023-09-07T20:03:53+02:00\",\n \"output\": \"OK - 127.0.0.1 rta 0.079ms lost 0%\\n\",\n \"id\": 13,\n \"max_check_attempts\": 3,\n \"last_update\": \"2023-11-21T08:57:13+01:00\",\n \"acknowledged\": false,\n \"display_name\": \"CENTREON\",\n \"check_attempt\": 1\n }\n ]\n}" + }, + { + "uuid": "0fcda655-3209-4726-8ab2-18f93666b57c", + "id": "tpm8", + "name": "Monitoring/resources response", + "documentation": "Monitoring/resources response to check errors in commands", + "value": "{\n \"meta\" : {\n \"page\" : 1,\n \"sort_by\" : {},\n \"search\" : {\n \"$and\" : {\n \"information\" : {\n \"$eq\" : \"(Execute command failed)\"\n }\n }\n },\n \"limit\" : 1000,\n \"total\" : 1\n },\n \"result\" : [\n {\n \"uuid\" : \"h254-s1616\",\n \"host_id\" : 254,\n \"monitoring_server_name\" : \"Central\",\n \"status\" : {\n \"name\" : \"UNKNOWN\",\n \"code\" : 3,\n \"severity_code\" : 3\n },\n \"icon\" : null,\n \"alias\" : null,\n \"last_status_change\" : \"2023-11-22T11:55:30+01:00\",\n \"short_type\" : \"s\",\n \"name\" : \"Svc-BadCommand\",\n \"last_check\" : \"1m 2s\",\n \"duration\" : \"4h 28m\",\n \"acknowledged\" : false,\n \"in_downtime\" : false,\n \"chart_url\" : null,\n \"tries\" : \"1/3 (H)\",\n \"information\" : \"(Execute command failed)\",\n \"performance_data\" : null,\n \"parent\" : {\n \"status\" : {\n \"severity_code\" : 1,\n \"name\" : \"DOWN\",\n \"code\" : 1\n },\n \"host_id\" : null,\n \"uuid\" : \"h254\",\n \"alias\" : \"Down\",\n \"icon\" : null,\n \"links\" : {\n \"externals\" : {\n \"action_url\" : null,\n \"notes\" : null\n },\n \"uris\" : {\n \"configuration\" : null,\n \"reporting\" : null,\n \"logs\" : null\n },\n \"endpoints\" : {}\n },\n \"id\" : 254,\n \"fqdn\" : \"1.2.3.4\",\n \"name\" : \"FakeHostThatIsDown\",\n \"type\" : \"host\",\n \"short_type\" : \"h\",\n \"service_id\" : null\n },\n \"links\" : {\n \"externals\" : {\n \"action_url\" : \"\",\n \"notes\" : {\n \"url\" : \"\",\n \"label\" : \"\"\n }\n },\n \"uris\" : {\n \"logs\" : \"/centreon/main.php?p=20301&svc=254_1616\",\n \"configuration\" : \"/centreon/main.php?p=60201&o=c&service_id=1616\",\n \"reporting\" : \"/centreon/main.php?p=30702&period=yesterday&start=&end=&host_id=254&item=1616\"\n },\n \"endpoints\" : {\n \"check\" : \"/centreon/api/latest/monitoring/hosts/254/services/1616/check\",\n \"acknowledgement\" : \"/centreon/api/latest/monitoring/hosts/254/services/1616/acknowledgements?limit=1\",\n \"timeline\" : \"/centreon/api/latest/monitoring/hosts/254/services/1616/timeline\",\n \"performance_graph\" : null,\n \"downtime\" : \"/centreon/api/latest/monitoring/hosts/254/services/1616/downtimes?search=%7B%22%24and%22:%5B%7B%22start_time%22:%7B%22%24lt%22:1700666614%7D,%22end_time%22:%7B%22%24gt%22:1700666614%7D,%220%22:%7B%22%24or%22:%7B%22is_cancelled%22:%7B%22%24neq%22:1%7D,%22deletion_time%22:%7B%22%24gt%22:1700666614%7D%7D%7D%7D%5D%7D\",\n \"forced_check\" : \"/centreon/api/latest/monitoring/hosts/254/services/1616/check\",\n \"status_graph\" : \"/centreon/api/latest/monitoring/hosts/254/services/1616/metrics/status\",\n \"details\" : \"/centreon/api/latest/monitoring/resources/hosts/254/services/1616\"\n }\n },\n \"passive_checks\" : false,\n \"notification_enabled\" : false,\n \"service_id\" : 1616,\n \"type\" : \"service\",\n \"severity\" : null,\n \"fqdn\" : null,\n \"active_checks\" : true,\n \"id\" : 1616\n }\n ]\n}" + } + ], + "callbacks": [] +} \ No newline at end of file diff --git a/tests/robot/apps/protocols/http/collection-centreon-web.robot b/tests/robot/apps/protocols/http/collection-centreon-web.robot new file mode 100644 index 000000000..0b4583f34 --- /dev/null +++ b/tests/robot/apps/protocols/http/collection-centreon-web.robot @@ -0,0 +1,34 @@ +*** Settings *** +Documentation Collections of HTTP Protocol plugin testing a mock of Centreon-web API + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}collection-centreon-web.mockoon.json + +${CMD} ${CENTREON_PLUGINS} --plugin apps::protocols::http::plugin --mode collection +... --constant='hostname=127.0.0.1' --constant='protocol=http' --constant='port=3000' +... --constant='username=admin' --constant='password=myPassword' + + +*** Test Cases *** +Check if ${test_desc} on Centreon + [Tags] Centreon Collections HTTP + ${output} Run + ... ${CMD} --config=${CURDIR}/${collection} + ${output} Strip String ${output} + Should Be Equal As Strings + ... ${output} + ... ${expected} + ... Wrong output result:\n\n ${output}\nInstead of:\n ${expected}\n\n + + Examples: test_desc collection expected -- + ... authentication succeeds collection-centreon-web-check-auth.collection.json OK: Authentication resulted in 200 HTTP code + ... hosts are down collection-centreon-web-check-down-hosts.collection.json OK: All hosts are UP | 'hostsRequest.down.count'=0;0;;0;1 + ... commands are broken collection-centreon-web-check-broken-commands.collection.json WARNING:${SPACE} - Service FakeHostThatIsDown/Svc-BadCommand output is '(Execute command failed)' | 'commands.broken.count'=1;0;;0; +